Add new resource for certs

This commit is contained in:
Martin Lensment 2015-02-25 16:26:09 +02:00
parent 143fb7eb1b
commit 5319db16b4
12 changed files with 310 additions and 33 deletions

View file

@ -53,14 +53,6 @@ class Admin::ApiUsersController < AdminController
end
end
def download_csr
send_data @api_user.csr, filename: "#{@api_user.username}.csr.pem"
end
def download_crt
send_data @api_user.crt, filename: "#{@api_user.username}.crt.pem"
end
private
def set_api_user

View file

@ -0,0 +1,68 @@
class Admin::CertificatesController < AdminController
load_and_authorize_resource
before_action :set_certificate, :set_api_user, only: [:sign, :show, :download_csr, :download_crt, :revoke]
def show
@csr = OpenSSL::X509::Request.new(@certificate.csr) if @certificate.csr
@crt = OpenSSL::X509::Certificate.new(@certificate.crt) if @certificate.crt
end
def new
set_api_user
@certificate = Certificate.new
end
def create
@api_user = ApiUser.find(params[:api_user_id])
csr = certificate_params[:csr].open.read if certificate_params[:csr]
@certificate = @api_user.certificates.build(csr: csr)
if @api_user.save
flash[:notice] = I18n.t('record_created')
redirect_to [:admin, @api_user, @certificate]
else
flash.now[:alert] = I18n.t('failed_to_create_record')
render 'new'
end
end
def sign
if @certificate.sign!
flash[:notice] = I18n.t('record_updated')
else
flash[:alert] = I18n.t('failed_to_update_record')
end
redirect_to [:admin, @api_user, @certificate]
end
def revoke
if @certificate.revoke!
flash[:notice] = I18n.t('record_updated')
else
flash[:alert] = I18n.t('failed_to_update_record')
end
redirect_to [:admin, @api_user, @certificate]
end
def download_csr
send_data @certificate.csr, filename: "#{@api_user.username}.csr.pem"
end
def download_crt
send_data @certificate.crt, filename: "#{@api_user.username}.crt.pem"
end
private
def set_certificate
@certificate = Certificate.find(params[:id])
end
def set_api_user
@api_user = ApiUser.find(params[:api_user_id])
end
def certificate_params
params.require(:certificate).permit(:csr)
end
end

View file

@ -45,6 +45,7 @@ class Ability
can :manage, DomainVersion
can :manage, User
can :manage, ApiUser
can :manage, Certificate
can :manage, Keyrelay
can :manage, LegalDocument
can :read, ApiLog::EppLog

View file

@ -5,6 +5,7 @@ class ApiUser < User
# TODO: should have max request limit per day
belongs_to :registrar
has_many :contacts
has_many :certificates
validates :username, :password, :registrar, presence: true
validates :username, uniqueness: true

83
app/models/certificate.rb Normal file
View file

@ -0,0 +1,83 @@
class Certificate < ActiveRecord::Base
SIGNED = 'signed'
UNSIGNED = 'unsigned'
EXPIRED = 'expired'
REVOKED = 'revoked'
VALID = 'valid'
validates :csr, presence: true
def parsed_crt
@p_crt ||= OpenSSL::X509::Certificate.new(crt) if crt
end
def parsed_csr
@p_csr ||= OpenSSL::X509::Request.new(csr) if csr
end
def revoked?
status == REVOKED
end
def status
return UNSIGNED if crt.blank?
return @cached_status if @cached_status
@cached_status = SIGNED
if parsed_crt.not_before > Time.now.utc && parsed_crt.not_after < Time.now.utc
@cached_status = EXPIRED
end
crl = OpenSSL::X509::CRL.new(File.open(APP_CONFIG['crl_path']).read)
return @cached_status unless crl.revoked.map(&:serial).include?(parsed_crt.serial)
@cached_status = REVOKED
end
def sign!
csr_file = Tempfile.new('client_csr')
csr_file.write(csr)
csr_file.rewind
crt_file = Tempfile.new('client_crt')
_out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \
-cert #{APP_CONFIG['ca_cert_path']} \
-extensions usr_cert -notext -md sha256 \
-in #{csr_file.path} -out #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch")
if err.match(/Data Base Updated/)
crt_file.rewind
self.crt = crt_file.read
save!
else
errors.add(:base, I18n.t('failed_to_create_certificate'))
logger.error('FAILED TO CREATE CLIENT CERTIFICATE')
logger.error(err)
return false
end
end
def revoke!
crt_file = Tempfile.new('client_crt')
crt_file.write(crt)
crt_file.rewind
_out, err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \
-cert #{APP_CONFIG['ca_cert_path']} \
-revoke #{crt_file.path} -key '#{APP_CONFIG['ca_key_password']}' -batch")
if err.match(/Data Base Updated/) || err.match(/ERROR:Already revoked/)
save!
else
errors.add(:base, I18n.t('failed_to_revoke_certificate'))
logger.error('FAILED TO REVOKE CLIENT CERTIFICATE')
logger.error(err)
return false
end
_out, _err, _st = Open3.capture3("openssl ca -keyfile #{APP_CONFIG['ca_key_path']} \
-cert #{APP_CONFIG['ca_cert_path']} \
-gencrl -out #{APP_CONFIG['crl_path']} -key '#{APP_CONFIG['ca_key_password']}' -batch")
end
end

View file

@ -15,7 +15,7 @@
- if @api_user.errors.any?
%hr
.row
.col-md-6
.col-md-12
.panel.panel-default
.panel-heading
%h3.panel-title= t('general')
@ -29,21 +29,24 @@
%dt= t('active')
%dd= @api_user.active
.col-md-6
.row
.col-md-12
.panel.panel-default
.panel-heading
%h3.panel-title= t('certificates')
.panel-body
%dl.dl-horizontal
%dt= t('csr')
- if @api_user.csr
%dd= link_to(t('download'), download_csr_admin_api_user_path)
- else
%dd -
.panel-heading.clearfix
.pull-left
= t('certificates')
.pull-right
= link_to(t('upload_csr'), new_admin_api_user_certificate_path(@api_user), class: 'btn btn-primary btn-xs')
%dt= t('crt')
- if @api_user.csr
%dd= link_to(t('download'), download_crt_admin_api_user_path)
- else
%dd -
.table-responsive
%table.table.table-hover.table-bordered.table-condensed
%thead
%tr
%th{class: 'col-xs-10'}= t('subject')
%th{class: 'col-xs-2'}= t('status')
%tbody
- @api_user.certificates.each do |x|
- if x.csr
%tr
%td= link_to(x.parsed_csr.try(:subject), admin_api_user_certificate_path(@api_user, x))
%td= x.status

View file

@ -0,0 +1,20 @@
%h2= t('upload_csr')
%hr
= form_for([:admin, @api_user, @certificate], multipart: true) do |f|
- if @certificate.errors.any?
- @certificate.errors.each do |attr, err|
= err
%br
- if @certificate.errors.any?
%hr
.row
.col-md-12.text-left
.form-group
= f.label :csr, t('certificate_signing_req')
= f.file_field :csr
%hr
.row
.col-md-12.text-right
= button_tag(t('save'), class: 'btn btn-primary')

View file

@ -0,0 +1,75 @@
.row
.col-sm-6
%h2.text-center-xs
= t('certificates')
.col-sm-6
%h2.text-right.text-center-xs
= link_to(t('back_to_api_user'), [:admin, @api_user], class: 'btn btn-default')
%hr
- if @certificate.errors.any?
- @certificate.errors.each do |attr, err|
= err
%br
- if @certificate.errors.any?
%hr
.row
.col-md-12
.panel.panel-default
.panel-heading.clearfix
.pull-left
= t('csr')
.pull-right
= link_to(t('download'), download_csr_admin_api_user_certificate_path(@api_user, @certificate), class: 'btn btn-default btn-xs')
- unless @crt
= link_to(t('sign_this_request'), sign_admin_api_user_certificate_path(@api_user, @certificate), method: :post, class: 'btn btn-primary btn-xs')
.panel-body
%dl.dl-horizontal
%dt= t('version')
%dd= @csr.version
%dt= t('subject')
%dd= @csr.subject
%dt= t('signature_algorithm')
%dd= @csr.signature_algorithm
- if @crt
.row
.col-md-12
.panel.panel-default
.panel-heading.clearfix
.pull-left
= t('crt') unless @certificate.revoked?
= t('crt_revoked') if @certificate.revoked?
.pull-right
= link_to(t('download'), download_crt_admin_api_user_certificate_path(@api_user, @certificate), class: 'btn btn-default btn-xs')
- unless @certificate.revoked?
= link_to(t('revoke_this_certificate'), revoke_admin_api_user_certificate_path(@api_user, @certificate), method: :post, class: 'btn btn-primary btn-xs')
- if @crt
.panel-body
%dl.dl-horizontal
%dt= t('version')
%dd= @crt.version
%dt= t('serial_number')
%dd= @crt.serial
%dt= t('signature_algorithm')
%dd= @crt.signature_algorithm
%dt= t('issuer')
%dd= @crt.issuer
%dt= t('valid_from')
%dd= @crt.not_before
%dt= t('valid_to')
%dd= @crt.not_after
%dt= t('subject')
%dd= @crt.subject
%dt= t('extensions')
%dd= @crt.extensions.map(&:to_s).join('<br>').html_safe

View file

@ -481,4 +481,11 @@ en:
address_help: 'Street name, house no - apartment no, city, county, country, zip'
download: 'Download'
failed_to_create_certificate: 'Failed to create certificate!'
failed_to_revoke_certificate: 'Failed to revoke certificate!'
contact_code: Contact code
upload_csr: 'Upload CSR'
signature_algorithm: 'Signature algorithm'
version: 'Version'
sign_this_request: 'Sign this request'
revoke_this_certificate: 'Revoke this certificate'
crt_revoked: 'CRT (revoked)'

View file

@ -48,9 +48,13 @@ Rails.application.routes.draw do
resources :admin_users
resources :api_users do
member do
get 'download_csr'
get 'download_crt'
resources :certificates do
member do
post 'sign'
post 'revoke'
get 'download_csr'
get 'download_crt'
end
end
end

View file

@ -0,0 +1,15 @@
class CreateCertificates < ActiveRecord::Migration
def change
create_table :certificates do |t|
t.integer :api_user_id
t.text :csr
t.text :crt
t.timestamps
end
ApiUser.all.each do |x|
x.certificates << Certificate.new(crt: x.crt, csr: x.csr)
end
end
end

View file

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150217133937) do
ActiveRecord::Schema.define(version: 20150223104842) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -53,6 +53,14 @@ ActiveRecord::Schema.define(version: 20150217133937) do
add_index "cached_nameservers", ["hostname", "ipv4", "ipv6"], name: "index_cached_nameservers_on_hostname_and_ipv4_and_ipv6", unique: true, using: :btree
create_table "certificates", force: :cascade do |t|
t.integer "api_user_id"
t.text "csr"
t.text "crt"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "contact_disclosures", force: :cascade do |t|
t.integer "contact_id"
t.boolean "phone"