Add CN support to certificates

This commit is contained in:
Martin Lensment 2015-05-21 17:06:21 +03:00
parent e2809cc285
commit 9ad66c0999
12 changed files with 144 additions and 133 deletions

View file

@ -1,21 +1,19 @@
class Admin::CertificatesController < AdminController class Admin::CertificatesController < AdminController
load_and_authorize_resource load_and_authorize_resource
before_action :set_certificate, :set_api_user, only: [:sign, :show, :download_csr, :download_crt, :revoke] before_action :set_api_user, only: [:new, :show, :destroy, :edit, :update]
def show; end def show; end
def edit; end
def new def new
set_api_user @certificate = Certificate.new(api_user: @api_user)
@certificate = Certificate.new
end end
def create def create
@api_user = ApiUser.find(params[:api_user_id]) @api_user = ApiUser.find(params[:api_user_id])
crt = certificate_params[:crt].open.read if certificate_params[:crt] @certificate = @api_user.certificates.build(certificate_params)
csr = certificate_params[:csr].open.read if certificate_params[:csr]
@certificate = @api_user.certificates.build(csr: csr, crt: crt)
if @api_user.save if @api_user.save
flash[:notice] = I18n.t('record_created') flash[:notice] = I18n.t('record_created')
redirect_to [:admin, @api_user, @certificate] redirect_to [:admin, @api_user, @certificate]
@ -25,50 +23,68 @@ class Admin::CertificatesController < AdminController
end end
end end
def sign def update
if @certificate.sign! if @certificate.update(certificate_params)
flash[:notice] = I18n.t('record_updated') flash[:notice] = I18n.t('record_updated')
redirect_to [:admin, @api_user, @certificate] redirect_to [:admin, @api_user, @certificate]
else else
flash.now[:alert] = I18n.t('failed_to_update_record') flash.now[:alert] = I18n.t('failed_to_update_record')
render 'edit'
end
end
def destroy
if @certificate.destroy
flash[:notice] = I18n.t('record_deleted')
redirect_to admin_api_user_path(@api_user)
else
flash.now[:alert] = I18n.t('failed_to_delete_record')
render 'show' render 'show'
end end
end end
def revoke # DEPRECATED FOR NOW
if @certificate.revoke! # def sign
flash[:notice] = I18n.t('record_updated') # if @certificate.sign!
else # flash[:notice] = I18n.t('record_updated')
flash[:alert] = I18n.t('failed_to_update_record') # redirect_to [:admin, @api_user, @certificate]
end # else
redirect_to [:admin, @api_user, @certificate] # flash.now[:alert] = I18n.t('failed_to_update_record')
end # render 'show'
# end
# end
def download_csr # def revoke
send_data @certificate.csr, filename: "#{@api_user.username}.csr.pem" # if @certificate.revoke!
end # 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_crt # def download_csr
send_data @certificate.crt, filename: "#{@api_user.username}.crt.pem" # send_data @certificate.csr, filename: "#{@api_user.username}.csr.pem"
end # end
# def download_crt
# send_data @certificate.crt, filename: "#{@api_user.username}.crt.pem"
# end
private private
def set_certificate # DEPRECATED FOR NOW
@certificate = Certificate.find(params[:id]) # def set_certificate
@csr = OpenSSL::X509::Request.new(@certificate.csr) if @certificate.csr # @certificate = Certificate.find(params[:id])
@crt = OpenSSL::X509::Certificate.new(@certificate.crt) if @certificate.crt # @csr = OpenSSL::X509::Request.new(@certificate.csr) if @certificate.csr
end # @crt = OpenSSL::X509::Certificate.new(@certificate.crt) if @certificate.crt
# end
def set_api_user def set_api_user
@api_user = ApiUser.find(params[:api_user_id]) @api_user = ApiUser.find(params[:api_user_id])
end end
def certificate_params def certificate_params
if params[:certificate] params.require(:certificate).permit(:common_name, :md5, :interface)
params.require(:certificate).permit(:csr, :crt)
else
{}
end
end end
end end

View file

@ -11,12 +11,9 @@ class Certificate < ActiveRecord::Base
REVOKED = 'revoked' REVOKED = 'revoked'
VALID = 'valid' VALID = 'valid'
validate :validate_csr_and_crt INTERFACES = ['api', 'registrar']
def validate_csr_and_crt validates :common_name, :md5, :interface, presence: true
return if csr.present? || crt.present?
errors.add(:base, I18n.t(:crt_or_csr_must_be_present))
end
def parsed_crt def parsed_crt
@p_crt ||= OpenSSL::X509::Certificate.new(crt) if crt @p_crt ||= OpenSSL::X509::Certificate.new(crt) if crt

View file

@ -1,5 +1,5 @@
- content_for :actions do - content_for :actions do
= link_to(t(:edit), edit_admin_api_user_path(@api_user), class: 'btn btn-primary') = link_to(t(:edit), edit_admin_api_user_path(@api_user), class: 'btn btn-default')
= link_to(t(:delete), admin_api_user_path(@api_user), = link_to(t(:delete), admin_api_user_path(@api_user),
method: :delete, data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger') method: :delete, data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger')
= render 'shared/title', name: @api_user.username = render 'shared/title', name: @api_user.username
@ -38,24 +38,19 @@
.pull-left .pull-left
= t(:certificates) = t(:certificates)
.pull-right .pull-right
= link_to(t(:upload_crt), = link_to(t(:add),
new_admin_api_user_certificate_path(@api_user, crt: true), class: 'btn btn-primary btn-xs') new_admin_api_user_certificate_path(@api_user), class: 'btn btn-default btn-xs')
= link_to(t(:upload_csr),
new_admin_api_user_certificate_path(@api_user), class: 'btn btn-primary btn-xs')
.table-responsive .table-responsive
%table.table.table-hover.table-bordered.table-condensed %table.table.table-hover.table-bordered.table-condensed
%thead %thead
%tr %tr
%th{class: 'col-xs-10'}= t(:subject) %th{class: 'col-xs-4'}= t(:common_name)
%th{class: 'col-xs-2'}= t(:status) %th{class: 'col-xs-4'}= t(:md5)
%th{class: 'col-xs-4'}= t(:interface)
%tbody %tbody
- @api_user.certificates.each do |x| - @api_user.certificates.each do |x|
- if x.csr
%tr %tr
%td= link_to(x.parsed_csr.try(:subject), admin_api_user_certificate_path(@api_user, x)) %td= link_to(x.common_name, admin_api_user_certificate_path(@api_user, x))
%td= x.status %td= x.md5
- elsif x.crt %td= x.interface
%tr
%td= link_to(x.parsed_crt.try(:subject), admin_api_user_certificate_path(@api_user, x))
%td= x.status

View file

@ -0,0 +1,29 @@
= form_for([:admin, @api_user, @certificate], html: {class: 'form-horizontal'}) do |f|
= render 'shared/full_errors', object: f.object
.row
.col-md-8
.form-group
.col-md-4.control-label
= f.label :api_user
.col-md-7
= f.text_field(:api_user, class: 'form-control', disabled: :disabled)
.form-group
.col-md-4.control-label
= f.label :common_name
.col-md-7
= f.text_field(:common_name, class: 'form-control', autocomplete: 'off')
.form-group
.col-md-4.control-label
= f.label :md5
.col-md-7
= f.text_field(:md5, class: 'form-control', autocomplete: 'off')
.form-group
.col-md-4.control-label
= f.label :interface
.col-md-7
= f.select :interface, Certificate::INTERFACES.map {|x| [x.upcase, x]}, {}, class: 'form-control selectize'
%hr
.row
.col-md-8.text-right
= button_tag(t(:save), class: 'btn btn-primary')

View file

@ -0,0 +1,5 @@
- content_for :actions do
= link_to(t(:back_to_api_user), admin_api_user_path(@api_user), class: 'btn btn-default')
= render 'shared/title', name: t(:edit_certificate)
= render 'form'

View file

@ -1,23 +1,5 @@
= render 'shared/title', name: params[:crt] ? t(:upload_crt) : t(:upload_csr) - content_for :actions do
= link_to(t(:back_to_api_user), admin_api_user_path(@api_user), class: 'btn btn-default')
= form_for([:admin, @api_user, @certificate], multipart: true) do |f|
= render 'shared/full_errors', object: f.object
.row
.col-md-8
.form-group
- if params[:crt]
.col-md-4.control-label
= f.label :crt, t(:certificate)
.col-md-8
= f.file_field :crt
- else
.col-md-4.control-label
= f.label :csr, t(:certificate_signing_req)
.col-md-8
= f.file_field :csr
%hr
.row
.col-md-8.text-right
= button_tag(t(:save), class: 'btn btn-primary')
= render 'shared/title', name: t(:add_certificate)
= render 'form'

View file

@ -1,5 +1,7 @@
- content_for :actions do - content_for :actions do
= link_to(t(:back_to_api_user), [:admin, @api_user], class: 'btn btn-default') = link_to(t(:edit), edit_admin_api_user_certificate_path(@api_user, @certificate), class: 'btn btn-default')
= link_to(t(:delete), admin_api_user_certificate_path(@api_user, @certificate),
method: :delete, data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger')
= render 'shared/title', name: t(:certificates) = render 'shared/title', name: t(:certificates)
- if @certificate.errors.any? - if @certificate.errors.any?
@ -9,64 +11,29 @@
- if @certificate.errors.any? - if @certificate.errors.any?
%hr %hr
- if @csr
.row .row
.col-md-12 .col-md-12
.panel.panel-default .panel.panel-default
.panel-heading.clearfix .panel-heading.clearfix
.pull-left .pull-left
= t(:csr) = t(:general)
.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 .panel-body
%dl.dl-horizontal %dl.dl-horizontal
%dt= t(:version) %dt= t(:api_user)
%dd= @csr.version %dd= link_to(@certificate.api_user, [:admin, @api_user])
%dt= t(:subject) %dt= t(:common_name)
%dd= @csr.subject %dd= @certificate.common_name
%dt= t(:signature_algorithm) %dt= t(:md5)
%dd= @csr.signature_algorithm %dd= @certificate.md5
- if @crt %dt= t(:interface)
.row %dd= @certificate.interface
.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')
- if !@certificate.revoked? && @certificate.csr
= 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) %dt= t(:updated_at)
%dd= @crt.serial %dd= l(@certificate.updated_at)
%dt= t(:signature_algorithm) %dt= t(:created_at)
%dd= @crt.signature_algorithm %dd= l(@certificate.created_at)
%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

@ -799,3 +799,8 @@ en:
no_permission: 'No permission' no_permission: 'No permission'
access_denied: 'Access denied' access_denied: 'Access denied'
connection_limit_reached: 'Connection limit reached' connection_limit_reached: 'Connection limit reached'
common_name: 'Common name'
md5: 'Md5'
interface: 'Interface'
add_certificate: 'Add certificate'
edit_certificate: 'Edit certificate'

View file

@ -0,0 +1,7 @@
class AddFieldsForCertificate < ActiveRecord::Migration
def change
add_column :certificates, :common_name, :string
add_column :certificates, :md5, :string
add_column :certificates, :interface, :string
end
end

View file

@ -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: 20150520164507) do ActiveRecord::Schema.define(version: 20150521120145) 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"
@ -147,6 +147,9 @@ ActiveRecord::Schema.define(version: 20150520164507) do
t.string "updator_str" t.string "updator_str"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.string "common_name"
t.string "md5"
t.string "interface"
end end
add_index "certificates", ["api_user_id"], name: "index_certificates_on_api_user_id", using: :btree add_index "certificates", ["api_user_id"], name: "index_certificates_on_api_user_id", using: :btree

View file

@ -1,6 +1,9 @@
# default fabricator should be reusable # default fabricator should be reusable
Fabricator(:certificate) do Fabricator(:certificate) do
api_user api_user
common_name 'cn'
md5 'md5hash'
interface 'api'
csr "-----BEGIN CERTIFICATE REQUEST-----\n" \ csr "-----BEGIN CERTIFICATE REQUEST-----\n" \
"MIIE+DCCAuACAQAwgZ0xCzAJBgNVBAYTAkVFMREwDwYDVQQIDAhIYXJqdW1hYTEQ\n" \ "MIIE+DCCAuACAQAwgZ0xCzAJBgNVBAYTAkVFMREwDwYDVQQIDAhIYXJqdW1hYTEQ\n" \
"MA4GA1UEBwwHVGFsbGlubjEbMBkGA1UECgwSRWVzdGkgSW50ZXJuZXRpIFNBMRIw\n" \ "MA4GA1UEBwwHVGFsbGlubjEbMBkGA1UECgwSRWVzdGkgSW50ZXJuZXRpIFNBMRIw\n" \

View file

@ -11,7 +11,9 @@ describe Certificate do
it 'should not be valid' do it 'should not be valid' do
@certificate.valid? @certificate.valid?
@certificate.errors.full_messages.should match_array([ @certificate.errors.full_messages.should match_array([
"CRT or CSR must be present" "Common name is missing",
"Interface is missing",
"Md5 is missing"
]) ])
end end