Skip to content

Commit

Permalink
Add new resource for certs
Browse files Browse the repository at this point in the history
  • Loading branch information
mlensment committed Feb 25, 2015
1 parent 143fb7e commit 5319db1
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 33 deletions.
8 changes: 0 additions & 8 deletions app/controllers/admin/api_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,6 @@ def destroy
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
Expand Down
68 changes: 68 additions & 0 deletions app/controllers/admin/certificates_controller.rb
Original file line number Diff line number Diff line change
@@ -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
9 changes: 5 additions & 4 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def initialize(user)
alias_action :show, :create, :update, :destroy, to: :crud

@user = user || AdminUser.new

case @user.class.to_s
when 'AdminUser'
@user.roles.each { |role| send(role) } if @user.roles
Expand All @@ -18,11 +18,11 @@ def initialize(user)

def epp
# Epp::Contact
can(:info, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw }
can(:info, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id || c.auth_info == pw }
can(:check, Epp::Contact)
can(:create, Epp::Contact)
can(:update, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw }
can(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw }
can(:update, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw }
can(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw }
can(:renew, Epp::Contact)
can(:view_password, Epp::Contact) { |c| c.registrar_id == @user.registrar_id }
end
Expand All @@ -45,6 +45,7 @@ def admin
can :manage, DomainVersion
can :manage, User
can :manage, ApiUser
can :manage, Certificate
can :manage, Keyrelay
can :manage, LegalDocument
can :read, ApiLog::EppLog
Expand Down
1 change: 1 addition & 0 deletions app/models/api_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 83 additions & 0 deletions app/models/certificate.rb
Original file line number Diff line number Diff line change
@@ -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
37 changes: 20 additions & 17 deletions app/views/admin/api_users/show.haml
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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
20 changes: 20 additions & 0 deletions app/views/admin/certificates/new.haml
Original file line number Diff line number Diff line change
@@ -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')

75 changes: 75 additions & 0 deletions app/views/admin/certificates/show.haml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)'
10 changes: 7 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@

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

Expand Down
15 changes: 15 additions & 0 deletions db/migrate/20150223104842_create_certificates.rb
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 5319db1

Please sign in to comment.