foreman_x509 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +38 -0
  4. data/Rakefile +47 -0
  5. data/app/controllers/foreman_x509/api/v2/base_controller.rb +12 -0
  6. data/app/controllers/foreman_x509/api/v2/certificates_controller.rb +59 -0
  7. data/app/controllers/foreman_x509/api/v2/generations_controller.rb +58 -0
  8. data/app/controllers/foreman_x509/api/v2/issuers_controller.rb +41 -0
  9. data/app/controllers/foreman_x509/api/v2/requests_controller.rb +32 -0
  10. data/app/controllers/foreman_x509/certificates_controller.rb +82 -0
  11. data/app/controllers/foreman_x509/generations_controller.rb +71 -0
  12. data/app/controllers/foreman_x509/issuers_controller.rb +49 -0
  13. data/app/controllers/foreman_x509/requests_controller.rb +17 -0
  14. data/app/helpers/foreman_x509/certificates_helper.rb +49 -0
  15. data/app/models/concerns/foreman_x509/digest.rb +11 -0
  16. data/app/models/concerns/foreman_x509/extensions.rb +20 -0
  17. data/app/models/concerns/foreman_x509/subject.rb +23 -0
  18. data/app/models/foreman_x509/certificate.rb +75 -0
  19. data/app/models/foreman_x509/generation.rb +61 -0
  20. data/app/models/foreman_x509/issuer.rb +87 -0
  21. data/app/models/foreman_x509/request.rb +40 -0
  22. data/app/services/foreman_x509/builder.rb +86 -0
  23. data/app/services/foreman_x509/serializer/big_number.rb +13 -0
  24. data/app/services/foreman_x509/serializer/certificate.rb +15 -0
  25. data/app/services/foreman_x509/serializer/certificate_revocation_list.rb +14 -0
  26. data/app/services/foreman_x509/serializer/configuration.rb +13 -0
  27. data/app/services/foreman_x509/serializer/key.rb +13 -0
  28. data/app/services/foreman_x509/serializer/request.rb +13 -0
  29. data/app/views/foreman_x509/api/v2/certificates/base.json.rabl +3 -0
  30. data/app/views/foreman_x509/api/v2/certificates/index.json.rabl +3 -0
  31. data/app/views/foreman_x509/api/v2/certificates/show.json.rabl +9 -0
  32. data/app/views/foreman_x509/api/v2/generations/base.json.rabl +11 -0
  33. data/app/views/foreman_x509/api/v2/generations/index.json.rabl +0 -0
  34. data/app/views/foreman_x509/api/v2/generations/show.json.rabl +7 -0
  35. data/app/views/foreman_x509/api/v2/issuers/base.json.rabl +3 -0
  36. data/app/views/foreman_x509/api/v2/issuers/index.json.rabl +3 -0
  37. data/app/views/foreman_x509/api/v2/requests/show.json.rabl +16 -0
  38. data/app/views/foreman_x509/certificates/_form.html.erb +12 -0
  39. data/app/views/foreman_x509/certificates/_generations.html.erb +32 -0
  40. data/app/views/foreman_x509/certificates/index.html.erb +34 -0
  41. data/app/views/foreman_x509/certificates/new.html.erb +3 -0
  42. data/app/views/foreman_x509/certificates/show.html.erb +31 -0
  43. data/app/views/foreman_x509/generations/edit.html.erb +8 -0
  44. data/app/views/foreman_x509/issuers/index.html.erb +24 -0
  45. data/app/views/foreman_x509/issuers/new.html.erb +0 -0
  46. data/app/views/foreman_x509/issuers/show.html.erb +52 -0
  47. data/app/views/foreman_x509/requests/show.html.erb +15 -0
  48. data/config/routes.rb +68 -0
  49. data/db/migrate/20250201155706_initialize_foreman_x509_schema.rb +37 -0
  50. data/db/migrate/20250401083842_create_foreman_x509_requests.rb +14 -0
  51. data/lib/foreman_x509/engine.rb +47 -0
  52. data/lib/foreman_x509/plugin.rb +30 -0
  53. data/lib/foreman_x509/version.rb +3 -0
  54. data/lib/foreman_x509.rb +4 -0
  55. data/lib/tasks/foreman_x509_tasks.rake +31 -0
  56. data/locale/Makefile +73 -0
  57. data/locale/en/foreman_x509.po +19 -0
  58. data/locale/foreman_x509.pot +19 -0
  59. data/locale/gemspec.rb +2 -0
  60. data/package.json +41 -0
  61. data/test/factories/foreman_x509_factories.rb +5 -0
  62. data/test/test_plugin_helper.rb +6 -0
  63. data/test/unit/foreman_x509_test.rb +11 -0
  64. data/webpack/global_index.js +17 -0
  65. data/webpack/global_test_setup.js +11 -0
  66. data/webpack/index.js +8 -0
  67. data/webpack/src/Components/EmptyState/Constants.js +2 -0
  68. data/webpack/src/Components/EmptyState/EmptyStateReducer.js +19 -0
  69. data/webpack/src/Components/EmptyState/ExtendedEmptyState.js +43 -0
  70. data/webpack/src/Components/EmptyState/__tests__/ExtendedEmptyState.test.js +37 -0
  71. data/webpack/src/Extends/index.js +15 -0
  72. data/webpack/src/Router/WelcomePage/Welcome.js +9 -0
  73. data/webpack/src/Router/WelcomePage/index.js +1 -0
  74. data/webpack/src/Router/routes.js +12 -0
  75. data/webpack/src/reducers.js +10 -0
  76. data/webpack/test_setup.js +17 -0
  77. metadata +168 -0
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ForemanX509'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+ task default: :test
37
+
38
+ begin
39
+ require 'rubocop/rake_task'
40
+ RuboCop::RakeTask.new
41
+ rescue LoadError
42
+ puts 'Rubocop not loaded.'
43
+ end
44
+
45
+ task :default do
46
+ Rake::Task['rubocop'].execute
47
+ end
@@ -0,0 +1,12 @@
1
+ module ForemanX509
2
+ module Api
3
+ module V2
4
+ class BaseController < ::Api::V2::BaseController
5
+ resource_description do
6
+ api_version '2'
7
+ api_base_url '/foreman_x509/api'
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ module ForemanX509
2
+ module Api
3
+ module V2
4
+ class CertificatesController < BaseController
5
+ resource_description do
6
+ resource_id 'certificates'
7
+ api_version 'v2'
8
+ api_base_url '/foreman_x509/api'
9
+ end
10
+
11
+ before_action :find_resource, only: [:show, :destroy]
12
+
13
+ api :GET, '/certificates', N_('List certificates')
14
+ param_group :search_and_pagination, ::Api::V2::BaseController
15
+ add_scoped_search_description_for(Certificate)
16
+ def index
17
+ @certificates = resource_scope_for_index
18
+ end
19
+
20
+ def create
21
+ end
22
+
23
+ def show
24
+ end
25
+
26
+ def update
27
+ end
28
+
29
+ api :GET, '/certificates/:id/chain', N_('Download the PEM format chain')
30
+ param :id, Integer, desc: N_('Id of the certificate')
31
+ def chain
32
+ send_data @certificate.issuer.bundle.map(&:to_pem).join('\n'), filename: "#{@certificate.name}_ca.pem"
33
+ end
34
+
35
+ api :GET, '/certificates/:id/certificate', N_('Download the PEM format certificate')
36
+ param :id, Integer, desc: N_('Id of the certificate')
37
+ def certificate
38
+ send_data @certificate.certificate.to_pem, filename: "#{@certificate.name}_cert.pem"
39
+ end
40
+
41
+ api :GET, '/certificate/:id/key', N_('Download the PEM format key')
42
+ param :id, Integer, desc: N_('Id of the key')
43
+ def key
44
+ send_data @certificate.key.to_pem, filename: "#{@certificate.name}_key.pem"
45
+ end
46
+
47
+ api :DELETE, '/certificates/:id', N_('Destroy a certificate')
48
+ param :id, Integer, desc: N_('Id of the certificate')
49
+ def destroy
50
+ process_response @certificate.destroy
51
+ end
52
+
53
+ def resource_class
54
+ ForemanX509::Certificate
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ module ForemanX509
2
+ module Api
3
+ module V2
4
+ class GenerationsController < BaseController
5
+ before_action :find_owner
6
+ before_action :find_generation, except: [:index, :create]
7
+
8
+ api :GET, '/certificates/:owner_id/generations', N_('List certificate generations')
9
+ param :certificate_id, Integer, desc: N_('ID of the owning certificate')
10
+ param_group :search_and_pagination, ::Api::V2::BaseController
11
+ def index
12
+ @generations = resource_scope_for_index
13
+ end
14
+
15
+ api :GET, '/certificates/:owner_id/generations/:id', N_('Get generation details')
16
+ param :certificate_id, Integer, desc: N_('ID of the owning certificate')
17
+ param :id, Integer, desc: N_('ID of the generation')
18
+ def show
19
+ end
20
+
21
+ api :GET, '/certificates/:owner_id/generations/:id/certificate', N_('Download the generation certificate')
22
+ param :certificate_id, Integer, desc: N_('ID of the owning certificate')
23
+ param :id, Integer, desc: N_('ID of the generation')
24
+ def certificate
25
+ end
26
+
27
+ api :GET, '/certificates/:owner_id/generations/:id/key', N_('Download the generation key')
28
+ param :certificate_id, Integer, desc: N_('ID of the owning certificate')
29
+ param :id, Integer, desc: N_('ID of the generation')
30
+ def key
31
+ send_data @generation.key.to_pem, filename: "#{@generation.owner.name}-#{@generation.id}_key.pem"
32
+ end
33
+
34
+ api :POST, '/certificates/:owner_id/generations/:id', N_('Activate certificate generation')
35
+ param :certificate_id, Integer, desc: N_('ID of the owning certificate')
36
+ param :id, Integer, desc: N_('ID of the generation')
37
+ def activate
38
+ @generation.activate!
39
+ end
40
+
41
+ api :DELETE, '/certificates/:owner_id/generations/:id', N_('Delete certificate generation')
42
+ def destroy
43
+ @generation.destroy
44
+ end
45
+
46
+ private
47
+
48
+ def find_owner
49
+ @owner ||= ForemanX509::Certificate.find(params[:owner_id])
50
+ end
51
+
52
+ def find_generation
53
+ @generation ||= find_certificate.generations.find_by(id: params[:id])
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ module ForemanX509
2
+ module Api
3
+ module V2
4
+ class IssuersController < BaseController
5
+ resource_description do
6
+ resource_id 'issuer'
7
+ api_version 'v2'
8
+ api_base_url '/foreman_x509/api'
9
+ end
10
+
11
+ before_action :find_resource, only: [:show, :destroy]
12
+
13
+ api :GET, '/issuers', N_('List issuers')
14
+ param_group :search_and_pagination, ::Api::V2::BaseController
15
+ add_scoped_search_description_for(ForemanX509::Issuer)
16
+ def index
17
+ @issuers = resource_scope_for_index
18
+ end
19
+
20
+ def create
21
+ end
22
+
23
+ def show
24
+ end
25
+
26
+ def update
27
+ end
28
+
29
+ api :DELETE, '/issuer/:id', N_('Destroy an issuer')
30
+ param :id, Integer, desc: N_('Id of the issuer')
31
+ def destroy
32
+ process_response @issuer.destroy
33
+ end
34
+
35
+ def resource_class
36
+ ForemanX509::Issuer
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module ForemanX509
2
+ module Api
3
+ module V2
4
+ class RequestsController < BaseController
5
+ resource_description do
6
+ resource_id 'requests'
7
+ api_version 'v2'
8
+ api_base_url '/foreman_x509/api'
9
+ end
10
+
11
+ before_action :find_resource, only: [:show, :download]
12
+
13
+ def index
14
+ @requests = resource_scope_for_index
15
+ end
16
+
17
+ def show
18
+ respond_to do |format|
19
+ format.der { send_data @request.to_der, filename: "#{@request.name}_req.der" }
20
+ format.pem { send_data @request.to_pem, filename: "#{@request.name}_req.pem" }
21
+ format.json
22
+ end
23
+ end
24
+
25
+ def resource_class
26
+ ForemanX509::Request
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,82 @@
1
+ module ForemanX509
2
+ class CertificatesController < ::ApplicationController
3
+ before_action :upload_certificate_file, only: [:create, :update]
4
+ before_action :upload_key_file, only: [:create, :update]
5
+ before_action :upload_configuration_file, only: [:create, :update]
6
+ before_action :find_resource, except: [:index, :new, :create]
7
+
8
+ def index
9
+ @certificates = resource_base_search_and_page
10
+ end
11
+
12
+ def new
13
+ @certificate = Certificate.new
14
+ end
15
+
16
+ def create
17
+ @certificate = Certificate.new(certificate_params)
18
+ if @certificate.save
19
+ process_success object: @certificate
20
+ else
21
+ process_error object: @certificate
22
+ end
23
+ end
24
+
25
+ def show
26
+ end
27
+
28
+ def update
29
+ if @certificate.update(certificate_params)
30
+ process_success object: @certificate
31
+ else
32
+ process_error object: @certificate
33
+ end
34
+ end
35
+
36
+ def chain
37
+ send_date @certificate.issuer.bundle.map(&:to_pem).join('\n'), filename: "#{@certificate.name}_ca.pem"
38
+ end
39
+
40
+ def certificate
41
+ send_data @certificate.certificate.to_pem, filename: "#{@certificate.name}_cert.pem"
42
+ end
43
+
44
+ def key
45
+ send_data @certificate.key.to_pem, filename: "#{@certificate.name}_key.pem"
46
+ end
47
+
48
+ def destroy
49
+ if @certificate.destroy
50
+ process_success object: @certificate
51
+ else
52
+ process_error object: @certificate
53
+ end
54
+ end
55
+
56
+ def resource_class
57
+ ForemanX509::Certificate
58
+ end
59
+
60
+ private
61
+
62
+ def upload_configuration_file
63
+ return if (configuration = params.dig(:certificate, :configuration_file)).nil?
64
+ params[:certificate][:configuration] = configuration.read if configuration.respond_to?(:read)
65
+ end
66
+
67
+ def upload_certificate_file
68
+ return if (certificate = params.dig(:certificate, :generation_attributes, :certificate_file)).nil?
69
+ params[:certificate][:generation_attributes][:certificate] = certificate.read if certificate.respond_to?(:read)
70
+ end
71
+
72
+ def upload_key_file
73
+ return if (key = params.dig(:certificate, :generation_attributes, :key_file)).nil?
74
+ params[:certificate][:generation_attributes][:key] = key.read if key.respond_to?(:read)
75
+ end
76
+
77
+ def certificate_params
78
+ params.require(:certificate).permit(:name, :description, :issuer_id, :configuration, generation_attributes: [:certificate, :key])
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,71 @@
1
+ module ForemanX509
2
+ class GenerationsController < ::ApplicationController
3
+
4
+ before_action :find_owner
5
+ before_action :find_generation, except: [:new, :create]
6
+ before_action :upload_certificate_file, only: [:create, :update]
7
+
8
+ def new
9
+ @generation = ForemanX509::Generation.new(owner: @owner)
10
+ end
11
+
12
+ def create
13
+ @generation = ForemanX509::Builder.create(@owner) if generation_params.empty?
14
+ @generation = @owner.generations.create(generation_params) unless generation_params.empty?
15
+ if @generation
16
+ process_success object: @generation, success_redirect: certificate_path(@owner)
17
+ else
18
+ process_error object: @generation, redirect: certificate_path(@owner)
19
+ end
20
+ end
21
+
22
+ def update
23
+ if @generation.update(generation_params)
24
+ process_success object: @generation, success_redirect: certificate_path(@owner)
25
+ else
26
+ process_error object: @generation, redirect: request_path(@generation.request)
27
+ end
28
+ end
29
+
30
+ def activate
31
+ @generation.activate!
32
+
33
+ redirect_to certificate_path(@owner)
34
+ end
35
+
36
+ def certificate
37
+ send_data @generation.certificate.to_pem, filename: "#{@generation.owner.name}-#{@generation.id}_cert.pem"
38
+ end
39
+
40
+ def key
41
+ send_data @generation.key.to_pem, filename: "#{@generation.owner.name}-#{@generation.id}_key.pem"
42
+ end
43
+
44
+ def destroy
45
+ if @generation.destroy
46
+ process_success object: @generation, success_redirect: certificate_path(@owner)
47
+ else
48
+ process_error object: @generation, redirect: certificate_path(@owner)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def find_owner
55
+ @owner ||= ForemanX509::Certificate.friendly.find(params[:owner_id])
56
+ end
57
+
58
+ def find_generation
59
+ @generation ||= ForemanX509::Generation.find_by(owner: find_owner, id: params[:id])
60
+ end
61
+
62
+ def upload_certificate_file
63
+ return if (certificate = params.dig(:generation, :certificate_file)).nil?
64
+ params[:generation][:certificate] = certificate.read if certificate.respond_to?(:read)
65
+ end
66
+
67
+ def generation_params
68
+ params.require(:generation).permit(:certificate, :active)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,49 @@
1
+ module ForemanX509
2
+ class IssuersController < ::ApplicationController
3
+
4
+ before_action :find_resource, only: [:show, :destroy]
5
+
6
+ def index
7
+ @issuers = resource_base_search_and_page
8
+ end
9
+
10
+ def new
11
+ @issuer = Issuer.new
12
+ end
13
+
14
+ def create
15
+ if @issuer.create(issuer_params)
16
+ process_success object: @issuer
17
+ else
18
+ process_error object: @issuer
19
+ end
20
+ end
21
+
22
+ def show
23
+ end
24
+
25
+ def bundle
26
+ send_data @issuer.bundle.map(&:to_pem).join('\n'), filename: "#{@issuer.name}_bundle.pem"
27
+ end
28
+
29
+ def destroy
30
+ if @issuer.destroy
31
+ process_success object: @issuer
32
+ else
33
+ process_error object: @issuer
34
+ end
35
+ end
36
+
37
+ def resource_class
38
+ ForemanX509::Issuer
39
+ end
40
+
41
+ private
42
+
43
+ def issuer_params_for_create
44
+ params.require(:issuer).permit(:certificate_id, :serial, :crl_number, :certificate_revocation_list,
45
+ certificate_attributes: [:name, :issuer_id, :description, :configuration, generation_attributes: [:certificate, :key]])
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ module ForemanX509
2
+ class RequestsController < ::ApplicationController
3
+ before_action :find_resource
4
+
5
+ def show
6
+ respond_to do |format|
7
+ format.der { send_data @request.to_der, filename: "#{@request.name}_req.der" }
8
+ format.pem { send_data @request.to_pem, filename: "#{@request.name}_req.pem" }
9
+ format.html
10
+ end
11
+ end
12
+
13
+ def resource_class
14
+ ForemanX509::Request
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module ForemanX509
2
+ module CertificatesHelper
3
+ def certificate_details
4
+ if @certificate.certificate.present?
5
+ @certificate.certificate.to_text
6
+ else
7
+ @certificate.subject.to_s(OpenSSL::X509::Name::MULTILINE)
8
+ end
9
+ end
10
+
11
+ def issuer_link(certificate)
12
+ return if certificate.issuer.nil?
13
+ link_to certificate.issuer.name, issuer_path(certificate.issuer)
14
+ end
15
+
16
+ def certificate_download_links
17
+ links = []
18
+
19
+ links << link_to(_('Download Certificate'), certificate_certificate_path(@certificate),
20
+ class: 'btn btn-default',
21
+ title: _('Download the PEM format certificate')) unless @certificate.certificate.nil?
22
+
23
+ links << link_to(_('Sign Request'), request_path(@certificate.request),
24
+ class: 'btn btn-default',
25
+ title: _('Upload the signed certificate')) unless @certificate.request.nil?
26
+
27
+ links << link_to(_('Download Key'), key_certificate_path(@certificate),
28
+ class: 'btn btn-default',
29
+ title: _('Download the PEM format private key')) unless @certificate.key.nil?
30
+
31
+ links
32
+ end
33
+
34
+ def generation_actions(generation)
35
+ buttons = []
36
+
37
+ params = [generation.owner, generation]
38
+
39
+ buttons << link_to(_('Activate'), generation_path(*params, generation: { active: true }), method: :put) if generation.status == 'inactive'
40
+ buttons << link_to(_('Download Certificate'), certificate_generation_path(*params)) unless generation.status == 'pending'
41
+ buttons << link_to(_('Upload Certificate'), request_path(generation.request)) if generation.status == 'pending'
42
+ buttons << link_to(_('Download Request'), request_path(generation.request, format: :pem)) if generation.status == 'pending'
43
+ buttons << link_to(_('Download Key'), key_generation_path(*params)) unless generation.key.nil?
44
+ buttons << link_to(_('Delete'), generation_path(*params), method: :delete, data: { confirm: _("Are you sure?") }) unless generation.active?
45
+
46
+ action_buttons(buttons)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ module ForemanX509
2
+ module Digest
3
+ extend ActiveSupport::Concern
4
+
5
+ def digest
6
+ algorithm = configuration.get_value(authority_section, 'default_md') if respond_to?(:authority_section)
7
+ algorithm ||= configuration.get_value('req', 'default_md')
8
+ OpenSSL::Digest.new(algorithm || 'sha256') # TODO: make Setting[:default_digest_algorithm]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module ForemanX509
2
+ module Extensions
3
+ extend ActiveSupport::Concern
4
+
5
+ EXTENSION_ENTRY_FORMAT = /\A(?<critical>critical,)?\s*(?:@(?<section>[a-zA-Z_]+)|(?<value>[^@].*))\Z/.freeze
6
+
7
+ def extensions_from_section(section)
8
+ return {} if section.nil?
9
+
10
+ configuration[section].transform_values do |value|
11
+ data = EXTENSION_ENTRY_FORMAT.match(value)
12
+ data[:critical].to_s + (data[:value] || extension_value_from_section(data[:section])).to_s
13
+ end
14
+ end
15
+
16
+ def extension_value_from_section(section)
17
+ configuration[section].entries.map { |entry| entry.join(':') }.join(',')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module ForemanX509
2
+ module Subject
3
+ extend ActiveSupport::Concern
4
+
5
+ def subject
6
+ @subject ||= subject_from_certificate
7
+ @subject ||= subject_from_configuration
8
+ end
9
+
10
+ def subject_from_certificate
11
+ certificate.subject unless certificate.nil?
12
+ end
13
+
14
+ def subject_from_configuration
15
+ return if configuration.blank?
16
+
17
+ section = configuration.get_value('req', 'distinguished_name')
18
+
19
+ OpenSSL::X509::Name.new(configuration[section].to_a) unless section.nil?
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,75 @@
1
+ module ForemanX509
2
+ class Certificate < ::ApplicationRecord
3
+ include ForemanX509::Subject
4
+ include ForemanX509::Extensions
5
+ include ForemanX509::Digest
6
+ extend FriendlyId
7
+ friendly_id :name
8
+
9
+ belongs_to :issuer, class_name: 'ForemanX509::Issuer', inverse_of: :certificates
10
+
11
+ has_many :generations, class_name: 'ForemanX509::Generation', foreign_key: :owner_id, inverse_of: :owner
12
+ has_one :generation, -> { where(foreman_x509_generations: { active: true }) }, class_name: 'ForemanX509::Generation', foreign_key: :owner_id
13
+ accepts_nested_attributes_for :generation
14
+
15
+ delegate :certificate, :key, to: :generation, allow_nil: true
16
+
17
+ has_one :request, class_name: 'ForemanX509::Request', inverse_of: :certificate
18
+
19
+ serialize :configuration, ForemanX509::Serializer::Configuration
20
+
21
+ validates :name, format: { with: /\A[a-z][a-z0-9.-]*(?<!-)\z/, message: _("Invalid name format!") }
22
+ validates :certificate, presence: true, if: -> { configuration.nil? }
23
+ validate :configuration_has_required_fields, unless: -> { configuration.nil? }
24
+
25
+ after_save :ensure_active_generation, if: -> { generations.empty? }
26
+
27
+ scoped_search on: :name, complete_value: true
28
+
29
+ def can_self_sign?
30
+ return false if configuration.nil?
31
+
32
+ section = configuration.get_value('req', 'x509_extensions')
33
+ configuration[section].present?
34
+ end
35
+
36
+ def active?
37
+ return false if certificate.nil?
38
+
39
+ (not_before..not_after).include? Time.now
40
+ end
41
+
42
+ def not_before
43
+ certificate.not_before unless certificate.nil?
44
+ end
45
+
46
+ def not_after
47
+ certificate.not_after unless certificate.nil?
48
+ end
49
+
50
+ def requested_extensions
51
+ extensions_from_section(configuration.get_value('req', 'req_extensions'))
52
+ end
53
+
54
+ def certificate_extensions_section
55
+ configuration.get_value('req', 'x509_extensions')
56
+ end
57
+
58
+ def key_bits
59
+ return 4096 if configuration.get_value('req', 'default_bits').nil?
60
+ configuration.get_value('req', 'default_bits').to_i
61
+ end
62
+
63
+ private
64
+
65
+ def configuration_has_required_fields
66
+ errors.add("Configuration missing distinguished name definition") if subject_from_configuration.nil?
67
+ end
68
+
69
+ def ensure_active_generation
70
+ return if configuration.blank?
71
+
72
+ ForemanX509::Builder.create(self)
73
+ end
74
+ end
75
+ end