mirror of
https://github.com/internetee/registry.git
synced 2025-05-17 09:57:23 +02:00
Add new resource for certs
This commit is contained in:
parent
143fb7eb1b
commit
5319db16b4
12 changed files with 310 additions and 33 deletions
|
@ -53,14 +53,6 @@ class Admin::ApiUsersController < AdminController
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def set_api_user
|
def set_api_user
|
||||||
|
|
68
app/controllers/admin/certificates_controller.rb
Normal file
68
app/controllers/admin/certificates_controller.rb
Normal 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
|
|
@ -5,7 +5,7 @@ class Ability
|
||||||
alias_action :show, :create, :update, :destroy, to: :crud
|
alias_action :show, :create, :update, :destroy, to: :crud
|
||||||
|
|
||||||
@user = user || AdminUser.new
|
@user = user || AdminUser.new
|
||||||
|
|
||||||
case @user.class.to_s
|
case @user.class.to_s
|
||||||
when 'AdminUser'
|
when 'AdminUser'
|
||||||
@user.roles.each { |role| send(role) } if @user.roles
|
@user.roles.each { |role| send(role) } if @user.roles
|
||||||
|
@ -18,11 +18,11 @@ class Ability
|
||||||
|
|
||||||
def epp
|
def epp
|
||||||
# Epp::Contact
|
# 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(:check, Epp::Contact)
|
||||||
can(:create, Epp::Contact)
|
can(:create, Epp::Contact)
|
||||||
can(:update, 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(:delete, Epp::Contact) { |c, pw| c.registrar_id == @user.registrar_id && c.auth_info == pw }
|
||||||
can(:renew, Epp::Contact)
|
can(:renew, Epp::Contact)
|
||||||
can(:view_password, Epp::Contact) { |c| c.registrar_id == @user.registrar_id }
|
can(:view_password, Epp::Contact) { |c| c.registrar_id == @user.registrar_id }
|
||||||
end
|
end
|
||||||
|
@ -45,6 +45,7 @@ class Ability
|
||||||
can :manage, DomainVersion
|
can :manage, DomainVersion
|
||||||
can :manage, User
|
can :manage, User
|
||||||
can :manage, ApiUser
|
can :manage, ApiUser
|
||||||
|
can :manage, Certificate
|
||||||
can :manage, Keyrelay
|
can :manage, Keyrelay
|
||||||
can :manage, LegalDocument
|
can :manage, LegalDocument
|
||||||
can :read, ApiLog::EppLog
|
can :read, ApiLog::EppLog
|
||||||
|
|
|
@ -5,6 +5,7 @@ class ApiUser < User
|
||||||
# TODO: should have max request limit per day
|
# TODO: should have max request limit per day
|
||||||
belongs_to :registrar
|
belongs_to :registrar
|
||||||
has_many :contacts
|
has_many :contacts
|
||||||
|
has_many :certificates
|
||||||
|
|
||||||
validates :username, :password, :registrar, presence: true
|
validates :username, :password, :registrar, presence: true
|
||||||
validates :username, uniqueness: true
|
validates :username, uniqueness: true
|
||||||
|
|
83
app/models/certificate.rb
Normal file
83
app/models/certificate.rb
Normal 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
|
|
@ -15,7 +15,7 @@
|
||||||
- if @api_user.errors.any?
|
- if @api_user.errors.any?
|
||||||
%hr
|
%hr
|
||||||
.row
|
.row
|
||||||
.col-md-6
|
.col-md-12
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading
|
||||||
%h3.panel-title= t('general')
|
%h3.panel-title= t('general')
|
||||||
|
@ -29,21 +29,24 @@
|
||||||
|
|
||||||
%dt= t('active')
|
%dt= t('active')
|
||||||
%dd= @api_user.active
|
%dd= @api_user.active
|
||||||
|
.row
|
||||||
.col-md-6
|
.col-md-12
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading.clearfix
|
||||||
%h3.panel-title= t('certificates')
|
.pull-left
|
||||||
.panel-body
|
= t('certificates')
|
||||||
%dl.dl-horizontal
|
.pull-right
|
||||||
%dt= t('csr')
|
= link_to(t('upload_csr'), new_admin_api_user_certificate_path(@api_user), class: 'btn btn-primary btn-xs')
|
||||||
- if @api_user.csr
|
|
||||||
%dd= link_to(t('download'), download_csr_admin_api_user_path)
|
|
||||||
- else
|
|
||||||
%dd -
|
|
||||||
|
|
||||||
%dt= t('crt')
|
.table-responsive
|
||||||
- if @api_user.csr
|
%table.table.table-hover.table-bordered.table-condensed
|
||||||
%dd= link_to(t('download'), download_crt_admin_api_user_path)
|
%thead
|
||||||
- else
|
%tr
|
||||||
%dd -
|
%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
app/views/admin/certificates/new.haml
Normal file
20
app/views/admin/certificates/new.haml
Normal 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')
|
||||||
|
|
75
app/views/admin/certificates/show.haml
Normal file
75
app/views/admin/certificates/show.haml
Normal 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
|
|
@ -481,4 +481,11 @@ en:
|
||||||
address_help: 'Street name, house no - apartment no, city, county, country, zip'
|
address_help: 'Street name, house no - apartment no, city, county, country, zip'
|
||||||
download: 'Download'
|
download: 'Download'
|
||||||
failed_to_create_certificate: 'Failed to create certificate!'
|
failed_to_create_certificate: 'Failed to create certificate!'
|
||||||
|
failed_to_revoke_certificate: 'Failed to revoke certificate!'
|
||||||
contact_code: Contact code
|
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)'
|
||||||
|
|
|
@ -48,9 +48,13 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :admin_users
|
resources :admin_users
|
||||||
resources :api_users do
|
resources :api_users do
|
||||||
member do
|
resources :certificates do
|
||||||
get 'download_csr'
|
member do
|
||||||
get 'download_crt'
|
post 'sign'
|
||||||
|
post 'revoke'
|
||||||
|
get 'download_csr'
|
||||||
|
get 'download_crt'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
15
db/migrate/20150223104842_create_certificates.rb
Normal file
15
db/migrate/20150223104842_create_certificates.rb
Normal 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
|
10
db/schema.rb
10
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
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
|
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|
|
create_table "contact_disclosures", force: :cascade do |t|
|
||||||
t.integer "contact_id"
|
t.integer "contact_id"
|
||||||
t.boolean "phone"
|
t.boolean "phone"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue