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
@@ -0,0 +1,61 @@
1
+ module ForemanX509
2
+ class Generation < ::ApplicationRecord
3
+ belongs_to :owner, class_name: 'ForemanX509::Certificate', inverse_of: :generations
4
+ has_one :issuer, through: :owner, class_name: 'ForemanX509::Issuer'
5
+
6
+ has_one :request, class_name: 'ForemanX509::Request', inverse_of: :generation
7
+
8
+ serialize :certificate, ForemanX509::Serializer::Certificate
9
+ serialize :key, ForemanX509::Serializer::Key
10
+
11
+ delegate :not_before, :not_after, to: :certificate, allow_nil: true
12
+
13
+ before_validation :activate_automatically, if: :will_save_change_to_certificate?
14
+ before_save :deactivate_previous_generation, if: :active?
15
+ after_save :delete_associated_request, if: :certificate?
16
+
17
+ scoped_search relation: :owner, on: :name, complete_value: true, rename: :owner
18
+
19
+ validates :active, presence: true, if: :active?
20
+ validates :active, uniqueness: { scope: :owner_id }, if: :active?
21
+ validate :validate_certificate_key_pairing, unless: -> { certificate.blank? or key.blank? }
22
+
23
+ def status
24
+ return 'pending' if certificate.nil?
25
+
26
+ return 'expired' if expired?
27
+
28
+ return 'active' if active?
29
+
30
+ 'inactive'
31
+ end
32
+
33
+ def activate!
34
+ update!(active: true)
35
+ end
36
+
37
+ def expired?
38
+ return false unless certificate?
39
+
40
+ not (certificate.not_before..certificate.not_after).include? Time.now
41
+ end
42
+
43
+ private
44
+
45
+ def validate_certificate_key_pairing
46
+ certificate.check_private_key(key)
47
+ end
48
+
49
+ def deactivate_previous_generation
50
+ owner.generations.where(active: true).update_all(active: false) unless owner.nil?
51
+ end
52
+
53
+ def delete_associated_request
54
+ request.destroy unless request.nil?
55
+ end
56
+
57
+ def activate_automatically
58
+ self[:active] = true # if Setting[:certs_activate_new_generation_automatically]
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ module ForemanX509
2
+ class Issuer < ::ApplicationRecord
3
+ include ForemanX509::Subject
4
+ include ForemanX509::Extensions
5
+ include ForemanX509::Digest
6
+
7
+ belongs_to :certificate, class_name: 'ForemanX509::Certificate'
8
+ accepts_nested_attributes_for :certificate
9
+
10
+ has_many :certificates, class_name: 'ForemanX509::Certificate', inverse_of: :issuer
11
+
12
+ serialize :serial, ForemanX509::Serializer::BigNumber
13
+ serialize :certificate_revocation_list, ForemanX509::Serializer::CertificateRevocationList
14
+
15
+ delegate :name, :description, :configuration, to: :certificate
16
+
17
+ validate :validate_authority_section, unless: -> { configuration.blank? }
18
+
19
+ scoped_search relation: :certificate, on: :name, complete_value: true
20
+
21
+ def external?
22
+ configuration.nil?
23
+ end
24
+
25
+ def self_signing?
26
+ certificate.issuer == self
27
+ end
28
+
29
+ def serial!
30
+ next_serial = serial || SecureRandom.hex(16)
31
+ update_attribute(:serial, next_serial + 1) unless new_record?
32
+ next_serial
33
+ end
34
+
35
+ def start_date
36
+ start_date = configuration.get_value(authority_section, 'default_startdate')
37
+ start_date = Time.parse(start_date) unless start_date.nil?
38
+ start_date || Time.now
39
+ end
40
+
41
+ def end_date
42
+ end_date = configuration.get_value(authority_section, 'default_enddate')
43
+ if end_date.nil?
44
+ duration = configuration.get_value(authority_section, 'default_days')
45
+ start_date + (duration.nil? ? 3650.days : duration.to_i.days) # TODO: make Setting[:default_certificate_days]
46
+ else
47
+ Time.parse(end_date)
48
+ end
49
+ end
50
+
51
+ def certificate_extensions(requested_extensions, section = nil)
52
+ section ||= default_certificate_extensions_section
53
+ case copy_extensions
54
+ when 'copy'
55
+ requested_extensions.merge(extensions_from_section(section))
56
+ when 'copyall'
57
+ extensions_from_section(section).merge(requested_extensions)
58
+ else # 'none'
59
+ extensions_from_section(section)
60
+ end
61
+ end
62
+
63
+ def bundle
64
+ return [ certificate.certificate ] if certificate.issuer.nil? or certificate.issuer == self
65
+
66
+ [ certificate.certificate ] + certificate.issuer.bundle
67
+ end
68
+
69
+ private
70
+
71
+ def default_certificate_extensions_section
72
+ configuration.get_value(authority_section, 'x509_extensions')
73
+ end
74
+
75
+ def copy_extensions
76
+ configuration.get_value(authority_section, 'copy_extensions') || 'none'
77
+ end
78
+
79
+ def authority_section
80
+ configuration.get_value('ca', 'default_ca')
81
+ end
82
+
83
+ def validate_authority_section
84
+ errors.add(:configuration, "missing 'default_ca' from 'ca' section") if authority_section.nil?
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,40 @@
1
+ module ForemanX509
2
+ class Request < ::ApplicationRecord
3
+ belongs_to :certificate, class_name: 'ForemanX509::Certificate'
4
+ belongs_to :generation, class_name: 'ForemanX509::Generation'
5
+
6
+ serialize :request, ForemanX509::Serializer::Request
7
+
8
+ validates :certificate, presence: true
9
+ validates :generation, presence: true
10
+
11
+ delegate :name, to: :certificate
12
+ delegate :to_pem, :to_der, to: :request
13
+
14
+ before_save :create_request, unless: :request?
15
+
16
+ private
17
+
18
+ def create_request
19
+ request = OpenSSL::X509::Request.new
20
+
21
+ request.public_key = generation.key.public_key
22
+ request.version = 0
23
+ request.subject = certificate.subject
24
+
25
+ request.add_attribute OpenSSL::X509::Attribute.new('extReq', OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(request_extensions)]))
26
+
27
+ self.request = request.sign(generation.key, certificate.digest)
28
+ end
29
+
30
+ def request_extensions
31
+ certificate.requested_extensions.map do |key, value|
32
+ extension_factory.create_extension(key, value)
33
+ end
34
+ end
35
+
36
+ def extension_factory
37
+ @extension_factory ||= OpenSSL::X509::ExtensionFactory.new(nil, nil, request)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,86 @@
1
+ module ForemanX509
2
+ class Builder
3
+ def self.create(subject)
4
+ new(subject).create
5
+ end
6
+
7
+ attr_reader :subject, :issuer, :request, :generation
8
+ attr_accessor :activate
9
+
10
+ def initialize(subject, **options)
11
+ @subject = subject
12
+ @issuer = subject.issuer
13
+ @issuer ||= Issuer.new(certificate: subject) if subject.can_self_sign?
14
+
15
+ @activate = options.fetch(:activate, true)
16
+ end
17
+
18
+ def create
19
+ @generation = subject.generations.create(key: key)
20
+
21
+ if issuer
22
+ build_certificate
23
+
24
+ generation.update(certificate: certificate, active: activate)
25
+ else
26
+ @request = Request.create(certificate: @subject, generation: @generation)
27
+ end
28
+
29
+ generation
30
+ end
31
+
32
+ private
33
+
34
+ def key
35
+ @key ||= (subject.key || OpenSSL::PKey::RSA.new(subject.key_bits))
36
+ end
37
+
38
+ def certificate
39
+ @certificate ||= OpenSSL::X509::Certificate.new
40
+ end
41
+
42
+ def self_signing?
43
+ issuer.certificate == subject
44
+ end
45
+
46
+ def build_certificate
47
+ return if issuer.external?
48
+
49
+ certificate.subject = subject.subject
50
+ certificate.public_key = key.public_key
51
+ certificate.version = 2
52
+
53
+ certificate.issuer = issuer.subject
54
+ certificate.serial = issuer.serial!
55
+ certificate.not_before = issuer.start_date
56
+ certificate.not_after = issuer.end_date
57
+
58
+ certificate_extensions do |extension|
59
+ certificate.add_extension extension
60
+ end
61
+
62
+ certificate.sign(signing_key, issuer.digest)
63
+ end
64
+
65
+ def certificate_extensions
66
+ issuer.certificate_extensions(subject.requested_extensions, subject.certificate_extensions_section).each do |key, value|
67
+ extension = extension_factory.create_extension(key, value)
68
+ yield extension if block_given?
69
+ end
70
+ end
71
+
72
+ def extension_factory
73
+ @extension_factory ||= OpenSSL::X509::ExtensionFactory.new(signing_certificate, certificate, nil)
74
+ end
75
+
76
+ def signing_certificate
77
+ return certificate if self_signing?
78
+ issuer.certificate.certificate
79
+ end
80
+
81
+ def signing_key
82
+ return key if self_signing?
83
+ issuer.certificate.key
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,13 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class BigNumber
4
+ def self.dump(object)
5
+ object.to_s(16) unless object.nil?
6
+ end
7
+
8
+ def self.load(data)
9
+ OpenSSL::BN.new(data, 16) unless data.blank?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class Certificate
4
+ def self.dump(object)
5
+ object = OpenSSL::X509::Certificate.new(object) if object.is_a? String
6
+
7
+ object.to_pem unless object.blank?
8
+ end
9
+
10
+ def self.load(data)
11
+ OpenSSL::X509::Certificate.new(data) unless data.blank?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class CertificateRevocationList
4
+
5
+ def self.dump(object)
6
+ object.to_s unless object.blank?
7
+ end
8
+
9
+ def self.load(data)
10
+ OpenSSL::X509::CRL.new(data) unless data.blank?
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class Configuration
4
+ def self.dump(object)
5
+ object.to_s unless object.blank?
6
+ end
7
+
8
+ def self.load(data)
9
+ OpenSSL::Config.parse(data) unless data.blank?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class Key
4
+ def self.dump(object)
5
+ object.to_s unless object.blank?
6
+ end
7
+
8
+ def self.load(data)
9
+ OpenSSL::PKey.read(data) unless data.blank?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module ForemanX509
2
+ module Serializer
3
+ class Request
4
+ def self.dump(object)
5
+ object.to_pem unless object.blank?
6
+ end
7
+
8
+ def self.load(data)
9
+ OpenSSL::X509::Request.new(data) unless data.blank?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ object @certificate
2
+
3
+ attributes :id, :name
@@ -0,0 +1,3 @@
1
+ collection @certificates
2
+
3
+ extends 'foreman_x509/api/v2/certificates/base'
@@ -0,0 +1,9 @@
1
+ object @certificate
2
+
3
+ extends 'foreman_x509/api/v2/certificates/base'
4
+
5
+ attributes :description
6
+
7
+ child :issuer do
8
+ extends 'foreman_x509/api/v2/issuers/base'
9
+ end
@@ -0,0 +1,11 @@
1
+ object @generation
2
+
3
+ attribute :id
4
+
5
+ node(:certificate_link) do |generation|
6
+ certificate_api_certificate_generation_url(generation.owner, generation) if generation.certificate?
7
+ end
8
+
9
+ node(:key_link) do |generation|
10
+ key_api_certificate_generation_url(generation.owner, generation) if generation.key?
11
+ end
@@ -0,0 +1,7 @@
1
+ object @generation
2
+
3
+ extends 'foreman_x509/api/v2/generations/base'
4
+
5
+ child :owner do
6
+ extends 'foreman_x509/api/v2/certificates/base'
7
+ end
@@ -0,0 +1,3 @@
1
+ object @issuer
2
+
3
+ attributes :id, :name
@@ -0,0 +1,3 @@
1
+ collection @issuers
2
+
3
+ extends 'foreman_x509/api/v2/issuers/base'
@@ -0,0 +1,16 @@
1
+ object @request
2
+
3
+ attribute :id
4
+
5
+ node :link do |request|
6
+ download_api_request_url(request)
7
+ end
8
+
9
+ child :certificate do
10
+ extends 'foreman_x509/api/v2/certificates/base'
11
+ end
12
+
13
+ child :generation do
14
+ extends 'foreman_x509/api/v2/generations/base'
15
+ end
16
+
@@ -0,0 +1,12 @@
1
+ <%= form_for @certificate do |f| %>
2
+ <%= base_errors_for @certificate %>
3
+ <%= text_f f, :name %>
4
+ <%= textarea_f f, :description %>
5
+ <%= selectable_f f, :issuer_id, ForemanX509::Issuer.all.collect { |c| [c.name, c.id] }, { include_blank: true }, { label: _('Issuer'), required: false } %>
6
+ <%= field(f, :configuration) do %>
7
+ <%= f.text_area :configuration, rows: 10, class: "form-control" %>
8
+ <%= f.file_field :configuration_file %>
9
+ <% end %>
10
+
11
+ <%= submit_or_cancel f, false, { cancel_path: (@certificate.new_record? ? certificates_path : certificate_path(@certificate)) } %>
12
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <table class="<%= table_css_classes 'table-condensed table-fixed' %>">
2
+ <thead>
3
+ <tr>
4
+ <th class="col-md-1"><%= _('Id') %></th>
5
+ <th class="col-md-2"><%= _('Not Before') %></th>
6
+ <th class="col-md-2"><%= _('Not After') %></th>
7
+ <th class="col-md-1"><%= _('Status') %></th>
8
+ <th class="col-md-1"><%= _('Actions') %></th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <% @certificate.generations.each do |generation| %>
13
+ <tr>
14
+ <td>
15
+ <%= generation.id %>
16
+ </td>
17
+ <td>
18
+ <%= generation.not_before.httpdate unless generation.not_before.nil? %>
19
+ </td>
20
+ <td>
21
+ <%= generation.not_after.httpdate unless generation.not_after.nil? %>
22
+ </td>
23
+ <td>
24
+ <%= generation.status %>
25
+ </td>
26
+ <td>
27
+ <%= generation_actions(generation) %>
28
+ </td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
@@ -0,0 +1,34 @@
1
+ <% title _("Certificates") %>
2
+
3
+ <% title_actions link_to(_("Create Certificate"), hash_for_new_certificate_path, class: 'btn btn-default') %>
4
+
5
+ <table class="<%= table_css_classes 'table-condensed table-fixed' %>">
6
+ <thead>
7
+ <tr>
8
+ <th class="col-md-2"><%= sort :name, as: _('Name') %></th>
9
+ <th class="col-md-3"><%= _('Issuer') %></th>
10
+ <th class="col-md-1"><%= _('Not Before') %></th>
11
+ <th class="col-md-1"><%= _('Not After') %></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% @certificates.each do |certificate| %>
16
+ <tr>
17
+ <td>
18
+ <%= link_to certificate.name, certificate_path(certificate) %>
19
+ </td>
20
+ <td>
21
+ <%= issuer_link certificate %>
22
+ </td>
23
+ <td>
24
+ <%= certificate.not_before.httpdate unless certificate.not_before.nil? %>
25
+ </td>
26
+ <td>
27
+ <%= certificate.not_after.httpdate unless certificate.not_after.nil? %>
28
+ </td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
33
+
34
+ <%= will_paginate_with_info @certificates %>
@@ -0,0 +1,3 @@
1
+ <% title _('Create Certificate') %>
2
+
3
+ <%= render partial: 'form' %>
@@ -0,0 +1,31 @@
1
+ <% title @certificate.name %>
2
+
3
+ <%
4
+ items = [{ caption: _('Certificates'), url: certificates_path },
5
+ { caption: @certificate.name }]
6
+
7
+ breadcrumbs(resource_url: api_certificates_path,
8
+ name_field: 'name',
9
+ switcher_item_url: certificate_path(':name'),
10
+ items: items)
11
+ %>
12
+
13
+ <% title_actions certificate_download_links %>
14
+
15
+ <ul id="host-show-tabs" class="nav nav-tabs">
16
+ <li class="active"><a href="#primary" data-toggle="tab"><%= _("Details") %></a></li>
17
+ <li><a href="#configuration" data-toggle="tab"><%= _("Configuration") %></a></li>
18
+ <li><a href="#generations" data-toggle="tab"><%= _("Generations") %></a></li>
19
+ </ul>
20
+
21
+ <div class="tab-content">
22
+ <div class="tab-pane active" id="primary">
23
+ <pre><%= certificate_details %></pre>
24
+ </div>
25
+ <div class="tab-pane" id="configuration">
26
+ <pre><%= @certificate.configuration.to_s %></pre>
27
+ </div>
28
+ <div class="tab-pane" id="generations">
29
+ <%= render partial: "generations" %>
30
+ </div>
31
+ </div>
@@ -0,0 +1,8 @@
1
+ <pre><%= @generation.request.to_pem %></pre>
2
+ <%= form_for @generation, url: certificate_generation_path(certificate_id: @owner, id: @generation) do |f| %>
3
+ <%= field(f, :certificate) do %>
4
+ <%= f.text_area :certificate, rows: 10, class: "form-control" %>
5
+ <%= f.file_field :certificate_file %>
6
+ <% end %>
7
+ <%= submit_or_cancel f, false, { cancel_path: certificate_path(@owner) } %>
8
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <% title _("Issuers") %>
2
+
3
+ <table class="<%= table_css_classes 'table-condensed table-fixed' %>">
4
+ <thead>
5
+ <tr>
6
+ <th><%= sort :name, as: _('Name') %></th>
7
+ <th class="col-md-1"><%= _('Certificates') %></th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <% @issuers.each do |issuer| %>
12
+ <tr>
13
+ <td>
14
+ <%= link_to issuer.name, issuer_path(issuer) %>
15
+ </td>
16
+ <td>
17
+ <%= issuer.certificates.count %>
18
+ </td>
19
+ </tr>
20
+ <% end %>
21
+ </tbody>
22
+ </table>
23
+
24
+ <%= will_paginate_with_info @issuers %>
File without changes
@@ -0,0 +1,52 @@
1
+ <% title @issuer.name %>
2
+
3
+ <%
4
+ items = [{ caption: _('Issuers'), url: issuers_path },
5
+ { caption: @issuer.name }]
6
+
7
+ breadcrumbs(resource_url: api_issuers_path,
8
+ name_field: 'name',
9
+ switcher_item_url: issuer_path(':id'),
10
+ items: items)
11
+ %>
12
+
13
+ <ul id="host-show-tabs" class="nav nav-tabs">
14
+ <li class="active"><a href="#primary" data-toggle="tab"><%= _("Details") %></a></li>
15
+ <li><a href="#configuration" data-toggle="tab"><%= _("Configuration") %></a></li>
16
+ <li><a href="#certificates" data-toggle="tab"><%= _("Certificates") %></a></li>
17
+ </ul>
18
+
19
+ <div class="tab-content">
20
+ <div class="tab-pane active" id="primary">
21
+ <pre><%= @issuer.certificate.certificate.to_text %></pre>
22
+ </div>
23
+ <div class="tab-pane" id="configuration">
24
+ <pre><%= @issuer.configuration.to_s %></pre>
25
+ </div>
26
+ <div class="tab-pane" id="certificates">
27
+ <table class="<%= table_css_classes 'table-condensed table-fixed' %>">
28
+ <thead>
29
+ <tr>
30
+ <th class="col-md-2"><%= sort :name, as: _('Name') %></th>
31
+ <th class="col-md-1"><%= _('Not Before') %></th>
32
+ <th class="col-md-1"><%= _('Not After') %></th>
33
+ </tr>
34
+ </thead>
35
+ <tbody>
36
+ <% @issuer.certificates.each do |certificate| %>
37
+ <tr>
38
+ <td>
39
+ <%= link_to certificate.name, certificate_path(certificate) %>
40
+ </td>
41
+ <td>
42
+ <%= certificate.not_before.httpdate unless certificate.not_before.nil? %>
43
+ </td>
44
+ <td>
45
+ <%= certificate.not_after.httpdate unless certificate.not_after.nil? %>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+ </div>
52
+ </div>
@@ -0,0 +1,15 @@
1
+ <%= form_for @request.generation, url: generation_path(@request.certificate, @request.generation) do |f| %>
2
+ <div class="clearfix">
3
+ <div class="form-group">
4
+ <label class="col-md-2 control-label">Request</label>
5
+ <div class="col-md-5">
6
+ <pre><%= @request.to_pem %></pre>
7
+ </div>
8
+ </div>
9
+ </div>
10
+ <%= field(f, :certificate, size: "col-md-5") do %>
11
+ <%= f.text_area :certificate, rows: 10, class: "form-control" %>
12
+ <%= f.file_field :certificate_file %>
13
+ <% end %>
14
+ <%= submit_or_cancel f, false, { cancel_path: certificate_path(@request.certificate) } %>
15
+ <% end %>