foreman_vault 0.2.0 → 1.1.0

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -0
  3. data/app/controllers/concerns/foreman_vault/controller/parameters/vault_connection.rb +1 -1
  4. data/app/lib/foreman_vault/macros.rb +2 -2
  5. data/app/models/concerns/foreman_vault/orchestration/vault_policy.rb +12 -5
  6. data/app/models/concerns/foreman_vault/provisioning_template_extensions.rb +15 -0
  7. data/app/models/setting/vault.rb +32 -2
  8. data/app/models/vault_connection.rb +34 -6
  9. data/app/services/foreman_vault/vault_auth_method.rb +3 -3
  10. data/app/services/foreman_vault/vault_client.rb +13 -4
  11. data/app/services/foreman_vault/vault_policy.rb +1 -1
  12. data/app/views/unattended/provisioning_templates/VaultPolicy/default.erb +3 -3
  13. data/app/views/vault_connections/_form.html.erb +20 -4
  14. data/app/views/vault_connections/index.html.erb +21 -7
  15. data/db/migrate/20201203220058_add_approle_to_vault_connection.rb +8 -0
  16. data/lib/foreman_vault/engine.rb +2 -1
  17. data/lib/foreman_vault/version.rb +1 -1
  18. data/lib/tasks/foreman_vault_tasks.rake +44 -1
  19. data/test/factories/vault_connection.rb +1 -1
  20. data/test/factories/vault_policy_template.rb +1 -0
  21. data/test/lib/tasks/push_auth_methods_test.rb +49 -0
  22. data/test/lib/tasks/push_policies_test.rb +49 -0
  23. data/test/models/foreman_vault/orchestration/vault_policy_test.rb +64 -9
  24. data/test/models/vault_policy_template_test.rb +28 -0
  25. data/test/unit/services/foreman_vault/vault_auth_method_test.rb +6 -19
  26. data/test/unit/services/foreman_vault/vault_client_test.rb +46 -10
  27. data/test/unit/services/foreman_vault/vault_policy_test.rb +9 -5
  28. metadata +15 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a11fc63d2584b7fd9f4ad6b561cdbdd471503d3de307d4a77610094d67d26b62
4
- data.tar.gz: 215b5f6e87ed318a16b7623bedccb25d17dd001ee73281f144b6726a44aee28b
3
+ metadata.gz: 39553e728b4ff3661a8b0fc008ee0959e5fdbba5f915a9f7f9d09bdd24d9d65a
4
+ data.tar.gz: 34b06a3ffc2cfdd6055356c4af15e91bb7d94de7954983d0becc20881c85fef3
5
5
  SHA512:
6
- metadata.gz: b73b1a230f9982752e8ede2a38b2afd1a071a7ca397fe5f518d6447dbd30ca4f94efcbf29d4c39430cb8f8bf5b9508d9c4edd542aead2ebde60d00e82f889833
7
- data.tar.gz: 3b85d123010e4ff958ed12885792b759cddd81b550c4f3cc287cce45b38881ea6589e700caa99751f0f0baf79002a89c2089f5a5784b23d6dafbf3509dc938cd
6
+ metadata.gz: 04fe38f150fb63017eeb3803c14ea02fe6eb557a8a51d13f7f089bc4a7e5ca12f08182ede2c642e5dd6b47d5ab53e5adcc80e45117ad714ad6154cec2df486de
7
+ data.tar.gz: d6ecb38160b4180a137a6db4f0d9e7fa6e9e14d32ebc5e98cae09a92f18997946bacc548adc4a55f7e19107ae6f2fcdbe7e43ab5a540b0d96706239dd81aa462
data/README.md CHANGED
@@ -18,11 +18,20 @@ Auth methods also get deleted after the host is removed from Foreman.
18
18
 
19
19
  This allows Foreman to create everything needed to access Hashicorp Vault directly from a VM using it's Puppet certificate (e.g. for _Deferred functions_ in Puppet or other CLI tools).
20
20
 
21
+ ## Compatibility
22
+
23
+ | Foreman Version | Plugin Version |
24
+ | --------------- | -------------- |
25
+ | >= 2.3 | ~> 1.0 |
26
+ | >= 1.23 | ~> 0.3, ~> 0.4 |
27
+ | >= 1.20 | ~> 0.2 |
28
+
21
29
  ## Requirements
22
30
 
23
31
  - Foreman >= 1.20
24
32
  - Working Vault instance
25
33
  - with _cert_ auth enabled
34
+ - with _approle_ auth enabled
26
35
  - with _kv_ secret store enabled
27
36
  - valid Vault Token
28
37
 
@@ -41,6 +50,27 @@ $ vault token create -period=60m
41
50
  [...]
42
51
  ```
43
52
 
53
+ To interact with Vault you can use Vault UI, which is available at `http://127.0.0.1:8200/ui`.
54
+
55
+ - The AppRole auth method
56
+
57
+ ```
58
+ $ vault auth enable approle
59
+ $ vault write auth/approle/role/my-role policies="default"
60
+ Success! Data written to: auth/approle/role/my-role
61
+ $ vault read auth/approle/role/my-role/role-id
62
+ Key Value
63
+ --- -----
64
+ role_id 8403910c-e563-d2f2-1c77-6e26319be8b5
65
+ $ vault write -f auth/approle/role/my-role/secret-id
66
+ Key Value
67
+ --- -----
68
+ secret_id 1058434b-b4aa-bf5a-b376-a15d9efb1059
69
+ secret_id_accessor 9cc19ed7-201f-7438-782e-561edd12b2a8
70
+ ```
71
+
72
+ See also [Vault CLI testing AppRole](https://gist.github.com/kamils-iRonin/d099908eaf0500de8ad9c2cea5658d01)
73
+
44
74
  ## Installation
45
75
 
46
76
  See [Plugins install instructions](https://theforeman.org/plugins/) for how to install Foreman plugins.
@@ -9,7 +9,7 @@ module ForemanVault
9
9
  class_methods do
10
10
  def vault_connection_params_filter
11
11
  Foreman::ParameterFilter.new(::VaultConnection).tap do |filter|
12
- filter.permit :name, :url, :token
12
+ filter.permit :name, :url, :token, :role_id, :secret_id
13
13
  end
14
14
  end
15
15
  end
@@ -4,7 +4,7 @@ module ForemanVault
4
4
  module Macros
5
5
  def vault_secret(vault_connection_name, secret_path)
6
6
  vault = VaultConnection.find_by!(name: vault_connection_name)
7
- raise VaultError.new(N_('Invalid token for %s'), vault.name) unless vault.token_valid?
7
+ raise VaultError.new(N_('Invalid token for %s'), vault.name) if vault.with_token? && !vault.token_valid?
8
8
 
9
9
  vault.fetch_secret(secret_path)
10
10
  rescue ActiveRecord::RecordNotFound => e
@@ -13,7 +13,7 @@ module ForemanVault
13
13
 
14
14
  def vault_issue_certificate(vault_connection_name, secret_path, *options)
15
15
  vault = VaultConnection.find_by!(name: vault_connection_name)
16
- raise VaultError.new(N_('Invalid token for %s'), vault.name) unless vault.token_valid?
16
+ raise VaultError.new(N_('Invalid token for %s'), vault.name) if vault.with_token? && !vault.token_valid?
17
17
  vault.issue_certificate(secret_path, *options)
18
18
  rescue ActiveRecord::RecordNotFound => e
19
19
  raise VaultError, e.message
@@ -16,6 +16,7 @@ module ForemanVault
16
16
 
17
17
  def queue_vault_push
18
18
  return if !managed? || errors.any?
19
+ return unless orchestration_enabled?
19
20
  return unless vault_policy.valid?
20
21
  return unless vault_auth_method.valid?
21
22
 
@@ -25,6 +26,8 @@ module ForemanVault
25
26
 
26
27
  def queue_vault_destroy
27
28
  return if !managed? || errors.any?
29
+ return unless orchestration_enabled?
30
+ return unless vault_auth_method.valid?
28
31
 
29
32
  queue.create(name: _('Clear %s Vault data') % self, priority: 60,
30
33
  action: [self, :del_vault])
@@ -35,11 +38,8 @@ module ForemanVault
35
38
  logger.info "Pushing #{name} data to Vault"
36
39
 
37
40
  vault_policy.save if vault_policy.new?
38
-
39
- if vault_auth_method.name != old&.vault_auth_method&.name
40
- old&.vault_auth_method&.delete
41
- vault_auth_method.save
42
- end
41
+ vault_auth_method.save
42
+ true
43
43
  rescue StandardError => e
44
44
  Foreman::Logging.exception("Failed to push #{name} data to Vault.", e)
45
45
  failure format(_('Failed to push %{name} data to Vault: %{message}\n '), name: name, message: e.message), e
@@ -54,6 +54,13 @@ module ForemanVault
54
54
  Foreman::Logging.exception("Failed to clear #{name} Vault data", e)
55
55
  failure format(_("Failed to clear %{name} Vault data: %{message}\n "), name: name, message: e.message), e
56
56
  end
57
+
58
+ def orchestration_enabled?
59
+ return false unless Setting[:vault_orchestration_enabled]
60
+ return false if vault_connection.nil?
61
+
62
+ true
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanVault
4
+ module ProvisioningTemplateExtensions
5
+ extend ActiveSupport::Concern
6
+
7
+ # rubocop:disable Metrics/ParameterLists
8
+ def render(renderer: Foreman::Renderer, host: nil, params: {}, variables: {}, mode: Foreman::Renderer::REAL_MODE, template_input_values: {}, source_klass: nil)
9
+ source_klass = Foreman::Renderer::Source::Database if template_kind == TemplateKind.find_by(name: 'VaultPolicy')
10
+
11
+ super
12
+ end
13
+ # rubocop:enable Metrics/ParameterLists
14
+ end
15
+ end
@@ -6,19 +6,37 @@ class Setting
6
6
  BLANK_ATTRS << 'vault_policy_template'
7
7
 
8
8
  def self.default_settings
9
- [set_vault_connection, set_vault_policy_template]
9
+ [set_vault_connection, set_vault_policy_template, set_vault_orchestration_enabled]
10
10
  end
11
11
 
12
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
12
13
  def self.load_defaults
13
14
  # Check the table exists
14
15
  return unless super
15
16
 
16
17
  transaction do
17
- default_settings.each { |s| create! s.update(category: 'Setting::Vault') }
18
+ default_settings.each do |s|
19
+ setting = create! s.update(category: 'Setting::Vault')
20
+
21
+ Foreman.try(:settings)&._add(
22
+ s[:name],
23
+ s.slice(:description, :default, :full_name, :encrypted)
24
+ .merge(category: 'Setting::Vault')
25
+ .yield_self do |params|
26
+ unless Gem::Version.new(SETTINGS[:version].notag) < Gem::Version.new('2.6')
27
+ params[:context] = :vault
28
+ params[:type] = setting.settings_type
29
+ end
30
+ params
31
+ end
32
+ )
33
+ end
18
34
  end
19
35
 
36
+ Foreman.try(:settings)&.load
20
37
  true
21
38
  end
39
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
22
40
 
23
41
  def self.humanized_category
24
42
  N_('Vault')
@@ -40,12 +58,15 @@ class Setting
40
58
  end
41
59
 
42
60
  def default_vault_connection
61
+ return nil unless VaultConnection.table_exists?
43
62
  return unless VaultConnection.unscoped.count == 1
44
63
 
45
64
  VaultConnection.unscoped.first.name
46
65
  end
47
66
 
48
67
  def vault_connections_collection
68
+ return [] unless VaultConnection.table_exists?
69
+
49
70
  proc { Hash[VaultConnection.unscoped.all.map { |vc| [vc.name, vc.name] }] }
50
71
  end
51
72
 
@@ -68,6 +89,15 @@ class Setting
68
89
  def vault_policy_templates_collection
69
90
  proc { Hash[ProvisioningTemplate.unscoped.of_kind(:VaultPolicy).map { |tmpl| [tmpl.name, tmpl.name] }] }
70
91
  end
92
+
93
+ def set_vault_orchestration_enabled
94
+ set(
95
+ 'vault_orchestration_enabled',
96
+ N_('Enable or disable the Vault orchestration step for managing policies and auth methods'),
97
+ false,
98
+ N_('Vault Orchestration enabled')
99
+ )
100
+ end
71
101
  end
72
102
  end
73
103
  end
@@ -5,20 +5,42 @@ class VaultConnection < ApplicationRecord
5
5
 
6
6
  validates_lengths_from_database
7
7
  validates :name, presence: true, uniqueness: true
8
- validates :url, :token, presence: true
8
+ validates :url, presence: true
9
9
  validates :url, format: URI.regexp(['http', 'https'])
10
10
 
11
- before_create :set_expire_time
12
- before_update :update_expire_time
11
+ validates :token, presence: true, if: -> { role_id.nil? || secret_id.nil? }
12
+ validates :token, inclusion: { in: [nil], message: _('AppRole or token must be blank') }, unless: -> { role_id.nil? || secret_id.nil? }
13
+ validates :role_id, presence: true, if: -> { token.nil? }
14
+ validates :role_id, inclusion: { in: [nil], message: _('AppRole or token must be blank') }, unless: -> { token.nil? }
15
+ validates :secret_id, presence: true, if: -> { token.nil? }
16
+ validates :secret_id, inclusion: { in: [nil], message: _('AppRole or token must be blank') }, unless: -> { token.nil? }
13
17
 
14
- scope :with_valid_token, -> { where(vault_error: nil).where('expire_time > ?', Time.zone.now) }
18
+ before_validation :normalize_blank_values
19
+ before_create :set_expire_time, unless: -> { token.nil? }
20
+ before_update :update_expire_time, unless: -> { token.nil? }
21
+
22
+ scope :with_approle, -> { where.not(role_id: nil).where.not(secret_id: nil) }
23
+ scope :with_token, -> { where.not(token: nil) }
24
+ scope :with_valid_token, -> { with_token.where(vault_error: nil).where('expire_time > ?', Time.zone.now) }
15
25
 
16
26
  delegate :fetch_expire_time, :fetch_secret, :issue_certificate,
17
27
  :policy, :policies, :put_policy, :delete_policy,
18
28
  :set_certificate, :certificates, :delete_certificate, to: :client
19
29
 
30
+ def with_token?
31
+ token.present?
32
+ end
33
+
34
+ def with_approle?
35
+ role_id.present? && secret_id.present?
36
+ end
37
+
20
38
  def token_valid?
21
- vault_error.nil? && expire_time && expire_time > Time.zone.now
39
+ return false unless with_token?
40
+ return false unless vault_error.nil?
41
+ return true unless expire_time
42
+
43
+ expire_time > Time.zone.now
22
44
  end
23
45
 
24
46
  def renew_token!
@@ -52,7 +74,13 @@ class VaultConnection < ApplicationRecord
52
74
  self.vault_error = e.message
53
75
  end
54
76
 
77
+ def normalize_blank_values
78
+ attributes.each do |column, _value|
79
+ self[column].present? || self[column] = nil
80
+ end
81
+ end
82
+
55
83
  def client
56
- @client ||= ForemanVault::VaultClient.new(url, token)
84
+ @client ||= ForemanVault::VaultClient.new(url, token, role_id, secret_id)
57
85
  end
58
86
  end
@@ -11,9 +11,9 @@ module ForemanVault
11
11
  end
12
12
 
13
13
  def name
14
- return if !host || !vault_policy_name
14
+ return unless host
15
15
 
16
- [host, vault_policy_name].join('-').parameterize
16
+ host.name.parameterize
17
17
  end
18
18
 
19
19
  def save
@@ -23,7 +23,7 @@ module ForemanVault
23
23
  end
24
24
 
25
25
  def delete
26
- return false unless name
26
+ return false unless valid?
27
27
 
28
28
  delete_certificate(name)
29
29
  end
@@ -2,9 +2,11 @@
2
2
 
3
3
  module ForemanVault
4
4
  class VaultClient
5
- def initialize(base_url, token)
5
+ def initialize(base_url, token, role_id, secret_id)
6
6
  @base_url = base_url
7
7
  @token = token
8
+ @role_id = role_id
9
+ @secret_id = secret_id
8
10
  end
9
11
 
10
12
  delegate :sys, :auth_tls, to: :client
@@ -13,7 +15,8 @@ module ForemanVault
13
15
 
14
16
  def fetch_expire_time
15
17
  response = client.auth_token.lookup_self
16
- Time.zone.parse(response.data[:expire_time])
18
+ expire_time = response.data[:expire_time]
19
+ expire_time && Time.zone.parse(expire_time)
17
20
  end
18
21
 
19
22
  def fetch_secret(secret_path)
@@ -38,10 +41,16 @@ module ForemanVault
38
41
  class VaultClientError < Foreman::Exception; end
39
42
  class NoDataError < VaultClientError; end
40
43
 
41
- attr_reader :base_url, :token
44
+ attr_reader :base_url, :token, :role_id, :secret_id
42
45
 
43
46
  def client
44
- @client ||= Vault::Client.new(address: base_url, token: token)
47
+ @client ||= if role_id.present? && secret_id.present?
48
+ Vault::Client.new(address: base_url).tap do |client|
49
+ client.auth.approle(role_id, secret_id)
50
+ end
51
+ else
52
+ Vault::Client.new(address: base_url, token: token)
53
+ end
45
54
  end
46
55
  end
47
56
  end
@@ -29,7 +29,7 @@ module ForemanVault
29
29
  end
30
30
 
31
31
  def delete
32
- return false unless name
32
+ return false unless valid?
33
33
 
34
34
  delete_policy(name)
35
35
  end
@@ -1,6 +1,6 @@
1
- # NAME: <%= @host.owner %>-<%= @host.environment %>
1
+ # NAME: <%= @host.owner %>-<%= @host.name %>
2
2
 
3
- # allow access to secrets from puppet hosts from <foreman_owner>-<puppet_environment>
4
- path "secrets/data/<%= @host.owner %>/<%= @host.environment %>/*" {
3
+ # allow access to secrets from puppet hosts from <foreman_owner>-<hostname>
4
+ path "secrets/data/<%= @host.owner %>/<%= @host.name %>/*" {
5
5
  capabilities = ["create", "read", "update"]
6
6
  }
@@ -1,7 +1,23 @@
1
- <%= form_for @vault_connection, :url => (@vault_connection.new_record? ? vault_connections_path : vault_connection_path(:id => @vault_connection)) do |f| %>
1
+ <%= form_for @vault_connection, url: (@vault_connection.new_record? ? vault_connections_path : vault_connection_path(id: @vault_connection)) do |f| %>
2
2
  <%= base_errors_for @vault_connection %>
3
- <%= text_f f, :name, :help_inline => _("Vault Connection name") %>
4
- <%= text_f f, :url, :help_inline => _("Vault Server url") %>
5
- <%= text_f f, :token, :help_inline => _("Vault Connection token") %>
3
+ <%= text_f f, :name, help_inline: _("Vault Connection name") %>
4
+ <%= text_f f, :url, help_inline: _("Vault Server url") %>
5
+ <div class="auth_methods">
6
+ <h4><%=_("Auth Methods")%></h4>
7
+ <%= alert(text: _('Please only fill in one auth method'), class: 'alert-info', close: false) %>
8
+ <ul class="nav nav-tabs" data-tabs="tabs">
9
+ <li class="active"><a href="#approle" data-toggle="tab"><%= _('AppRole') %></a></li>
10
+ <li><a href="#token" data-toggle="tab"><%= _('Token') %></a></li>
11
+ </ul>
12
+ <div class="tab-content">
13
+ <div class="tab-pane active" id="approle">
14
+ <%= text_f f, :role_id, label: _("Role ID"), help_inline: _("Vault Connection Role ID") %>
15
+ <%= text_f f, :secret_id, label: _("Secret ID"), help_inline: _("Vault Connection Secret ID") %>
16
+ </div>
17
+ <div class="tab-pane" id="token">
18
+ <%= text_f f, :token, help_inline: _("Vault Connection token") %>
19
+ </div>
20
+ </div>
21
+ </div>
6
22
  <%= submit_or_cancel f %>
7
23
  <% end %>
@@ -4,9 +4,11 @@
4
4
  <table class="<%= table_css_classes 'table-fixed' %>">
5
5
  <thead>
6
6
  <tr>
7
- <th class="col-md-9"><%= sort :name, :as => s_('Vault Connections|Name') %></th>
8
- <th class="col-md-1"><%= _('Valid') %></th>
9
- <th class="col-md-1"><%= _('Expire time') %></th>
7
+ <th class="col-md-2"><%= sort :name, as: s_('Vault Connections|Name') %></th>
8
+ <th class="col-md-2"><%= _('URL') %></th>
9
+ <th class="col-md-2"><%= _('Role ID') %></th>
10
+ <th class="col-md-1"><%= _('Token Valid') %></th>
11
+ <th class="col-md-1"><%= _('Token Expire time') %></th>
10
12
  <th class="col-md-1"><%= _('Actions') %></th>
11
13
  </tr>
12
14
  </thead>
@@ -14,17 +16,29 @@
14
16
  <% @vault_connections.each do |vault_connection| %>
15
17
  <tr>
16
18
  <td class="ellipsis">
17
- <%= link_to_if_authorized vault_connection.name, hash_for_edit_vault_connection_path(:id => vault_connection) %>
19
+ <%= link_to_if_authorized vault_connection.name, hash_for_edit_vault_connection_path(id: vault_connection) %>
20
+ </td>
21
+ <td class="ellipsis">
22
+ <code class="transparent"><%= vault_connection.url %></code>
23
+ </td>
24
+ <td class="ellipsis">
25
+ <% if vault_connection.with_approle? %>
26
+ <code class="transparent"><%= vault_connection.role_id %></code>
27
+ <% end %>
18
28
  </td>
19
29
  <td align='center'>
20
- <% if vault_connection.token_valid? %>
30
+ <% vault_connection.with_token? && if vault_connection.token_valid? %>
21
31
  <%= ('<span class="glyphicon glyphicon-ok"/>').html_safe %>
22
32
  <% else %>
23
33
  <%= ('<span class="glyphicon glyphicon-remove" title="%s"/>' % vault_connection.vault_error).html_safe %>
24
34
  <% end %>
25
35
  </td>
26
- <td><%= date_time_absolute(vault_connection.expire_time) %></td>
27
- <td><%= action_buttons display_delete_if_authorized hash_for_vault_connection_path(:id => vault_connection), :data => { :confirm => _("Delete %s?") % vault_connection.name } %></td>
36
+ <td>
37
+ <% if vault_connection.with_token? %>
38
+ <%= date_time_absolute(vault_connection.expire_time) %>
39
+ <% end %>
40
+ </td>
41
+ <td><%= action_buttons display_delete_if_authorized hash_for_vault_connection_path(id: vault_connection), data: { confirm: _("Delete %s?") % vault_connection.name } %></td>
28
42
  </tr>
29
43
  <% end %>
30
44
  </tbody>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddApproleToVaultConnection < ActiveRecord::Migration[5.1]
4
+ def change
5
+ add_column :vault_connections, :role_id, :string
6
+ add_column :vault_connections, :secret_id, :string
7
+ end
8
+ end
@@ -29,7 +29,7 @@ module ForemanVault
29
29
 
30
30
  initializer 'foreman_vault.register_plugin', before: :finisher_hook do |_app|
31
31
  Foreman::Plugin.register :foreman_vault do
32
- requires_foreman '>= 1.20'
32
+ requires_foreman '>= 2.3'
33
33
 
34
34
  apipie_documented_controllers ["#{ForemanVault::Engine.root}/app/controllers/api/v2/*.rb"]
35
35
 
@@ -55,6 +55,7 @@ module ForemanVault
55
55
  config.to_prepare do
56
56
  begin
57
57
  ::Host::Managed.include(ForemanVault::HostExtensions)
58
+ ::ProvisioningTemplate.include(ForemanVault::ProvisioningTemplateExtensions)
58
59
  ::Foreman::Renderer::Scope::Base.include(ForemanVault::Macros)
59
60
  ::Foreman::Renderer.configure { |c| c.allowed_generic_helpers += [:vault_secret, :vault_issue_certificate] }
60
61
  rescue StandardError => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanVault
4
- VERSION = '0.2.0'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -3,7 +3,50 @@
3
3
  require 'rake/testtask'
4
4
 
5
5
  # Tasks
6
- namespace :foreman_vault do
6
+ namespace :foreman_vault do # rubocop:disable Metrics/BlockLength
7
+ namespace :auth_methods do
8
+ desc 'Push auth methods for all hosts to Vault'
9
+ task push: :environment do
10
+ User.as_anonymous_admin do
11
+ hosts = Host::Managed.where(managed: true)
12
+
13
+ hosts.each_with_index do |host, index|
14
+ begin
15
+ result = host.reload.vault_auth_method.save
16
+ if result
17
+ puts "[#{index + 1}/#{hosts.count}] Auth-Method of \"#{host.name}\" pushed to Vault server \"#{host.vault_connection.url}\""
18
+ else
19
+ puts "[#{index + 1}/#{hosts.count}] Failed to push \"#{host.name}\": #{result}"
20
+ end
21
+ rescue StandardError => err
22
+ puts "[#{index + 1}/#{hosts.count}] Failed to push \"#{host.name}\": #{err}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ namespace :policies do
30
+ desc 'Push policies for all hosts to Vault'
31
+ task push: :environment do
32
+ User.as_anonymous_admin do
33
+ hosts = Host::Managed.where(managed: true)
34
+
35
+ hosts.each_with_index do |host, index|
36
+ begin
37
+ result = host.reload.vault_policy.save
38
+ if result
39
+ puts "[#{index + 1}/#{hosts.count}] Policy of \"#{host.name}\" pushed to Vault server \"#{host.vault_connection.url}\""
40
+ else
41
+ puts "[#{index + 1}/#{hosts.count}] Failed to push \"#{host.name}\": #{result}"
42
+ end
43
+ rescue StandardError => err
44
+ puts "[#{index + 1}/#{hosts.count}] Failed to push \"#{host.name}\": #{err}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
7
50
  end
8
51
 
9
52
  # Tests
@@ -8,7 +8,7 @@ FactoryBot.define do
8
8
  expire_time { Time.zone.now + 1.year }
9
9
 
10
10
  trait :invalid do
11
- expire_time { nil }
11
+ expire_time { Time.zone.now - 1.year }
12
12
  end
13
13
 
14
14
  trait :without_callbacks do
@@ -4,6 +4,7 @@ FactoryBot.modify do
4
4
  factory :provisioning_template do
5
5
  trait :vault_policy do
6
6
  name { Setting['vault_policy_template'] || 'Default Vault Policy' }
7
+ template_kind { TemplateKind.find_or_create_by(name: 'VaultPolicy') }
7
8
  template { File.read(File.join(ForemanVault::Engine.root, 'app/views/unattended/provisioning_templates/VaultPolicy/default.erb')) }
8
9
  end
9
10
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+ require 'rake'
5
+
6
+ module ForemanVault
7
+ class PushAuthMethodsTest < ActiveSupport::TestCase
8
+ TASK_NAME = 'foreman_vault:auth_methods:push'
9
+
10
+ let(:host) { FactoryBot.create(:host, :managed) }
11
+ let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
12
+
13
+ setup do
14
+ Rake.application.rake_require 'tasks/foreman_vault_tasks'
15
+
16
+ Rake::Task.define_task(:environment)
17
+ Rake::Task[TASK_NAME].reenable
18
+
19
+ FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
20
+
21
+ ForemanVault::VaultAuthMethod.any_instance.stubs(:vault_policy_name).returns('vault_policy_name')
22
+ ForemanVault::VaultAuthMethod.any_instance.stubs(:certificate).returns('cert')
23
+
24
+ ForemanVault::VaultClient.any_instance.stubs(:set_certificate).returns(true)
25
+ end
26
+
27
+ it 'does successfully push auth methods' do
28
+ host
29
+
30
+ stdout, _stderr = capture_io do
31
+ Rake::Task[TASK_NAME].invoke
32
+ end
33
+
34
+ assert_match("[1/1] Auth-Method of \"#{host.name}\" pushed to Vault server \"#{host.vault_connection.url}\"", stdout)
35
+ end
36
+
37
+ it 'does throw an error when host was deleted' do
38
+ host
39
+
40
+ Host::Managed.any_instance.stubs(:reload).raises(ActiveRecord::RecordNotFound)
41
+
42
+ stdout, _stderr = capture_io do
43
+ Rake::Task[TASK_NAME].invoke
44
+ end
45
+
46
+ assert_match("[1/1] Failed to push \"#{host.name}\"", stdout)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+ require 'rake'
5
+
6
+ module ForemanVault
7
+ class PushPoliciesTest < ActiveSupport::TestCase
8
+ TASK_NAME = 'foreman_vault:policies:push'
9
+
10
+ let(:host) { FactoryBot.create(:host, :managed) }
11
+ let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
12
+
13
+ setup do
14
+ Rake.application.rake_require 'tasks/foreman_vault_tasks'
15
+
16
+ Rake::Task.define_task(:environment)
17
+ Rake::Task[TASK_NAME].reenable
18
+
19
+ FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
20
+
21
+ ForemanVault::VaultPolicy.any_instance.stubs(:name).returns('vault_policy_name')
22
+ ForemanVault::VaultPolicy.any_instance.stubs(:rules).returns('rules')
23
+
24
+ ForemanVault::VaultClient.any_instance.stubs(:put_policy).returns(true)
25
+ end
26
+
27
+ it 'does successfully push policies' do
28
+ host
29
+
30
+ stdout, _stderr = capture_io do
31
+ Rake::Task[TASK_NAME].invoke
32
+ end
33
+
34
+ assert_match("[1/1] Policy of \"#{host.name}\" pushed to Vault server \"#{host.vault_connection.url}\"", stdout)
35
+ end
36
+
37
+ it 'does throw an error when host was deleted' do
38
+ host
39
+
40
+ Host::Managed.any_instance.stubs(:reload).raises(ActiveRecord::RecordNotFound)
41
+
42
+ stdout, _stderr = capture_io do
43
+ Rake::Task[TASK_NAME].invoke
44
+ end
45
+
46
+ assert_match("[1/1] Failed to push \"#{host.name}\"", stdout)
47
+ end
48
+ end
49
+ end
@@ -10,11 +10,18 @@ module ForemanVault
10
10
  let(:queue) { mock('queue') }
11
11
  let(:vault_policy) { mock('vault_policy') }
12
12
  let(:vault_auth_method) { mock('vault_auth_method') }
13
+ let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
13
14
 
14
15
  setup do
15
16
  host.stubs(:queue).returns(queue)
16
17
  host.stubs(:vault_policy).returns(vault_policy)
17
18
  host.stubs(:vault_auth_method).returns(vault_auth_method)
19
+ FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
20
+ if Setting.find_by(name: 'vault_orchestration_enabled')
21
+ Setting['vault_orchestration_enabled'] = true
22
+ else
23
+ FactoryBot.create(:setting, name: :vault_orchestration_enabled, value: true)
24
+ end
18
25
  end
19
26
 
20
27
  test 'should queue Vault orchestration' do
@@ -50,9 +57,50 @@ module ForemanVault
50
57
  end
51
58
  end
52
59
 
60
+ describe '#queue_vault_destroy' do
61
+ let(:host) { FactoryBot.create(:host, :managed) }
62
+ let(:queue) { mock('queue') }
63
+ let(:vault_policy) { mock('vault_policy') }
64
+ let(:vault_auth_method) { mock('vault_auth_method') }
65
+ let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
66
+
67
+ setup do
68
+ host.stubs(:queue).returns(queue)
69
+ host.stubs(:vault_policy).returns(vault_policy)
70
+ host.stubs(:vault_auth_method).returns(vault_auth_method)
71
+ FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
72
+ if Setting.find_by(name: 'vault_orchestration_enabled')
73
+ Setting['vault_orchestration_enabled'] = true
74
+ else
75
+ FactoryBot.create(:setting, name: :vault_orchestration_enabled, value: true)
76
+ end
77
+ end
78
+
79
+ context 'when auth_method is valid' do
80
+ test 'should queue del_vault' do
81
+ vault_auth_method.stubs(:valid?).returns(true)
82
+
83
+ queue.expects(:create).with(
84
+ name: "Clear #{host} Vault data",
85
+ priority: 60,
86
+ action: [host, :del_vault]
87
+ ).once
88
+ host.send(:queue_vault_destroy)
89
+ end
90
+ end
91
+
92
+ context 'when auth_method is not valid' do
93
+ test 'should not queue del_vault' do
94
+ vault_auth_method.stubs(:valid?).returns(false)
95
+
96
+ queue.expects(:create).never
97
+ host.send(:queue_vault_destroy)
98
+ end
99
+ end
100
+ end
101
+
53
102
  describe '#set_vault' do
54
- let(:environment) { FactoryBot.create(:environment, name: 'MyEnv') }
55
- let(:host) { FactoryBot.create(:host, :managed, environment: environment) }
103
+ let(:host) { FactoryBot.create(:host, :managed) }
56
104
  let(:vault_connection) { FactoryBot.create(:vault_connection, :without_callbacks) }
57
105
  let(:new_owner) { FactoryBot.create(:usergroup, name: 'MyOwner') }
58
106
 
@@ -64,16 +112,16 @@ module ForemanVault
64
112
  )
65
113
  end
66
114
 
67
- let(:new_policy_name) { "#{new_owner}-#{host.environment}".parameterize }
115
+ let(:new_policy_name) { "#{new_owner}-#{host.name}".parameterize }
68
116
  let(:put_policy_request) do
69
117
  url = "#{vault_connection.url}/v1/sys/policy/#{new_policy_name}"
70
118
  # rubocop:disable Metrics/LineLength
71
- rules = "# allow access to secrets from puppet hosts from <foreman_owner>-<puppet_environment>\npath \"secrets/data/MyOwner/MyEnv/*\" {\n capabilities = [\"create\", \"read\", \"update\"]\n}\n"
119
+ rules = "# allow access to secrets from puppet hosts from <foreman_owner>-<hostname>\npath \"secrets/data/MyOwner/#{host.name}/*\" {\n capabilities = [\"create\", \"read\", \"update\"]\n}\n"
72
120
  # rubocop:enable Metrics/LineLength
73
121
  stub_request(:put, url).with(body: JSON.fast_generate(rules: rules)).to_return(status: 200)
74
122
  end
75
123
 
76
- let(:new_auth_method_name) { "#{host}-#{new_policy_name}".parameterize }
124
+ let(:new_auth_method_name) { host.name.parameterize }
77
125
  let(:post_auth_method_request) do
78
126
  url = "#{vault_connection.url}/v1/auth/cert/certs/#{new_auth_method_name}"
79
127
  stub_request(:post, url).with(
@@ -85,14 +133,23 @@ module ForemanVault
85
133
  ).to_return(status: 200)
86
134
  end
87
135
 
88
- let(:delete_old_auth_method_request) do
136
+ let(:override_old_auth_method_request) do
89
137
  url = "#{vault_connection.url}/v1/auth/cert/certs/#{host.vault_auth_method.name}"
90
138
  stub_request(:delete, url).to_return(status: 200)
91
139
  end
92
140
 
93
141
  setup do
94
142
  Setting.find_by(name: 'ssl_ca_file').update(value: File.join(ForemanVault::Engine.root, 'test/fixtures/ca.crt'))
95
- FactoryBot.create(:setting, :vault_policy)
143
+ if Setting.find_by(name: 'vault_orchestration_enabled')
144
+ Setting['vault_orchestration_enabled'] = true
145
+ else
146
+ FactoryBot.create(:setting, name: :vault_orchestration_enabled, value: true)
147
+ end
148
+ if Setting.find_by(name: 'vault_policy_template')
149
+ Setting['vault_policy_template'] = 'Default Vault Policy'
150
+ else
151
+ FactoryBot.create(:setting, :vault_policy)
152
+ end
96
153
  FactoryBot.create(:provisioning_template, :vault_policy, name: Setting['vault_policy_template'])
97
154
  FactoryBot.create(:parameter, name: 'vault_connection', value: vault_connection.name)
98
155
  host.stubs(:skip_orchestration_for_testing?).returns(false)
@@ -100,13 +157,11 @@ module ForemanVault
100
157
  get_policies_request
101
158
  put_policy_request
102
159
  post_auth_method_request
103
- delete_old_auth_method_request
104
160
 
105
161
  host.update(owner: new_owner)
106
162
  end
107
163
 
108
164
  it { assert_requested(post_auth_method_request) }
109
- it { assert_requested(delete_old_auth_method_request) }
110
165
 
111
166
  context 'when policy already exists on Vault' do
112
167
  let(:vault_policies) { [new_policy_name] }
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class VaultPolicyTemplateTest < ActiveSupport::TestCase
6
+ let(:host) { FactoryBot.create(:host, :managed) }
7
+ let(:template) { FactoryBot.create(:provisioning_template, :vault_policy) }
8
+
9
+ it 'is rendered from a database' do
10
+ Foreman::Renderer.expects(:get_source).with(has_entry(klass: Foreman::Renderer::Source::Database))
11
+ Foreman::Renderer.stubs(:get_scope)
12
+ Foreman::Renderer.stubs(:render)
13
+
14
+ template.render
15
+ end
16
+
17
+ test 'render in default mode' do
18
+ assert_nothing_raised { template.render(host: host) }
19
+ end
20
+
21
+ test 'render in safe mode' do
22
+ assert_nothing_raised { template.render(renderer: Foreman::Renderer::SafeModeRenderer, host: host) }
23
+ end
24
+
25
+ test 'render in unsafe mode' do
26
+ assert_nothing_raised { template.render(renderer: Foreman::Renderer::UnsafeModeRenderer, host: host) }
27
+ end
28
+ end
@@ -8,26 +8,13 @@ class VaultAuthMethodTest < ActiveSupport::TestCase
8
8
  let(:host) { FactoryBot.create(:host, :managed) }
9
9
 
10
10
  describe '#name' do
11
- context 'with host and vault_policy_name' do
12
- setup do
13
- subject.stubs(:vault_policy_name).returns('vault_policy_name')
14
- end
15
-
16
- it { assert_equal "#{host}-vault_policy_name".parameterize, subject.name }
11
+ context 'with host' do
12
+ it { assert_equal host.name.parameterize, subject.name }
17
13
  end
18
14
 
19
15
  context 'without host' do
20
16
  setup do
21
17
  subject.stubs(:host).returns(nil)
22
- subject.stubs(:vault_policy_name).returns('vault_policy_name')
23
- end
24
-
25
- it { assert_nil subject.name }
26
- end
27
-
28
- context 'without vault_policy_name' do
29
- setup do
30
- subject.stubs(:vault_policy_name).returns(nil)
31
18
  end
32
19
 
33
20
  it { assert_nil subject.name }
@@ -91,18 +78,18 @@ class VaultAuthMethodTest < ActiveSupport::TestCase
91
78
  end
92
79
 
93
80
  describe '#delete' do
94
- context 'with name' do
81
+ context 'when valid' do
95
82
  it 'deletes Certificate' do
96
- subject.stubs(:name).returns('name')
83
+ subject.stubs(:valid?).returns(true)
97
84
 
98
85
  subject.expects(:delete_certificate).once.with(subject.name)
99
86
  subject.delete
100
87
  end
101
88
  end
102
89
 
103
- context 'without name' do
90
+ context 'when not valid' do
104
91
  it 'does not delete Certificate' do
105
- subject.stubs(:name).returns(nil)
92
+ subject.stubs(:valid?).returns(false)
106
93
 
107
94
  subject.expects(:delete_certificate).never
108
95
  subject.delete
@@ -3,10 +3,46 @@
3
3
  require 'test_plugin_helper'
4
4
 
5
5
  class VaultClientTest < ActiveSupport::TestCase
6
- setup do
7
- @subject = ForemanVault::VaultClient.new('http://127.0.0.1:8200', 'e57e5ef2-b25c-a0e5-65a6-863ab095dff6')
8
- @client = mock
9
- Vault::Client.expects(:new).returns(@client)
6
+ subject do
7
+ ForemanVault::VaultClient.new(base_url, token, nil, nil).tap do |vault_client|
8
+ vault_client.instance_variable_set(:@client, client)
9
+ end
10
+ end
11
+
12
+ let(:client) { Vault::Client }
13
+ let(:base_url) { 'http://127.0.0.1:8200' }
14
+ let(:token) { 's.opkr0MAqme5e5nr3i2or5wZC' }
15
+
16
+ describe 'auth with AppRole' do
17
+ subject { ForemanVault::VaultClient.new(base_url, nil, role_id, secret_id) }
18
+
19
+ let(:role_id) { '8403910c-e563-d2f2-1c77-6e26319be8b5' }
20
+ let(:secret_id) { '1058434b-b4aa-bf5a-b376-a15d9efb1059' }
21
+
22
+ setup do
23
+ stub_request(:post, "#{base_url}/v1/auth/approle/login").with(
24
+ body: {
25
+ role_id: role_id,
26
+ secret_id: secret_id
27
+ }
28
+ ).to_return(
29
+ status: 200,
30
+ headers: { 'Content-Type': 'application/json' },
31
+ body: {
32
+ auth: {
33
+ client_token: token
34
+ }
35
+ }.to_json
36
+ )
37
+ end
38
+
39
+ it { assert_equal token, subject.send(:client).token }
40
+ end
41
+
42
+ describe 'auth with token' do
43
+ subject { ForemanVault::VaultClient.new(base_url, token, nil, nil) }
44
+
45
+ it { assert_equal token, subject.send(:client).token }
10
46
  end
11
47
 
12
48
  describe '#fetch_expire_time' do
@@ -14,11 +50,11 @@ class VaultClientTest < ActiveSupport::TestCase
14
50
  @time = '2018-08-01T20:08:55.525830559+02:00'
15
51
  response = OpenStruct.new(data: { expire_time: @time })
16
52
  auth_token = mock.tap { |object| object.expects(:lookup_self).once.returns(response) }
17
- @client.expects(:auth_token).once.returns(auth_token)
53
+ client.expects(:auth_token).once.returns(auth_token)
18
54
  end
19
55
 
20
56
  test 'should return expire time' do
21
- assert_equal Time.zone.parse(@time), @subject.fetch_expire_time
57
+ assert_equal Time.zone.parse(@time), subject.fetch_expire_time
22
58
  end
23
59
  end
24
60
 
@@ -29,11 +65,11 @@ class VaultClientTest < ActiveSupport::TestCase
29
65
  response = OpenStruct.new(data: @data)
30
66
  logical = mock.tap { |object| object.expects(:read).once.with(@secret_path).returns(response) }
31
67
 
32
- @client.expects(:logical).once.returns(logical)
68
+ client.expects(:logical).once.returns(logical)
33
69
  end
34
70
 
35
71
  test 'should return expire time' do
36
- assert_equal @data, @subject.fetch_secret(@secret_path)
72
+ assert_equal @data, subject.fetch_secret(@secret_path)
37
73
  end
38
74
  end
39
75
 
@@ -52,11 +88,11 @@ class VaultClientTest < ActiveSupport::TestCase
52
88
  response = OpenStruct.new(data: @data)
53
89
  logical = mock.tap { |object| object.expects(:write).once.with(@pki_path).returns(response) }
54
90
 
55
- @client.expects(:logical).once.returns(logical)
91
+ client.expects(:logical).once.returns(logical)
56
92
  end
57
93
 
58
94
  test 'should return new certificate' do
59
- assert_equal @data, @subject.issue_certificate(@pki_path)
95
+ assert_equal @data, subject.issue_certificate(@pki_path)
60
96
  end
61
97
  end
62
98
  end
@@ -8,7 +8,11 @@ class VaultPolicyTest < ActiveSupport::TestCase
8
8
  let(:host) { FactoryBot.create(:host, :managed) }
9
9
 
10
10
  setup do
11
- FactoryBot.create(:setting, name: 'vault_policy_template', value: 'Default Vault Policy')
11
+ if Setting.find_by(name: 'vault_policy_template')
12
+ Setting['vault_policy_template'] = 'Default Vault Policy'
13
+ else
14
+ FactoryBot.create(:setting, name: 'vault_policy_template', value: 'Default Vault Policy')
15
+ end
12
16
  end
13
17
 
14
18
  describe 'valid?' do
@@ -112,18 +116,18 @@ class VaultPolicyTest < ActiveSupport::TestCase
112
116
  end
113
117
 
114
118
  describe '#delete' do
115
- context 'with name' do
119
+ context 'when valid' do
116
120
  it 'deletes Vault Policy' do
117
- subject.stubs(:name).returns('name')
121
+ subject.stubs(:valid?).returns(true)
118
122
 
119
123
  subject.expects(:delete_policy).once.with(subject.name)
120
124
  subject.delete
121
125
  end
122
126
  end
123
127
 
124
- context 'without name' do
128
+ context 'when not valid' do
125
129
  it 'does not delete Vault Policy' do
126
- subject.stubs(:name).returns(nil)
130
+ subject.stubs(:valid?).returns(false)
127
131
 
128
132
  subject.expects(:delete_policy).never
129
133
  subject.delete
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_vault
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dmTECH GmbH
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-03 00:00:00.000000000 Z
11
+ date: 2021-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: vault
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.54.0
55
- description:
55
+ description:
56
56
  email:
57
57
  - opensource@dm.de
58
58
  executables: []
@@ -70,6 +70,7 @@ files:
70
70
  - app/lib/foreman_vault/macros.rb
71
71
  - app/models/concerns/foreman_vault/host_extensions.rb
72
72
  - app/models/concerns/foreman_vault/orchestration/vault_policy.rb
73
+ - app/models/concerns/foreman_vault/provisioning_template_extensions.rb
73
74
  - app/models/setting/vault.rb
74
75
  - app/models/vault_connection.rb
75
76
  - app/services/foreman_vault/vault_auth_method.rb
@@ -91,6 +92,7 @@ files:
91
92
  - config/routes.rb
92
93
  - db/migrate/20180725072913_create_vault_connection.foreman_vault.rb
93
94
  - db/migrate/20180809172407_rename_vault_status_to_vault_error.foreman_vault.rb
95
+ - db/migrate/20201203220058_add_approle_to_vault_connection.rb
94
96
  - db/seeds.d/103-provisioning_templates.rb
95
97
  - lib/foreman_vault.rb
96
98
  - lib/foreman_vault/engine.rb
@@ -107,8 +109,11 @@ files:
107
109
  - test/functional/api/v2/vault_connections_controller_test.rb
108
110
  - test/jobs/refresh_vault_token_test.rb
109
111
  - test/jobs/refresh_vault_tokens_test.rb
112
+ - test/lib/tasks/push_auth_methods_test.rb
113
+ - test/lib/tasks/push_policies_test.rb
110
114
  - test/models/foreman_vault/orchestration/vault_policy_test.rb
111
115
  - test/models/vault_connection_test.rb
116
+ - test/models/vault_policy_template_test.rb
112
117
  - test/test_plugin_helper.rb
113
118
  - test/unit/lib/foreman_vault/macros_test.rb
114
119
  - test/unit/services/foreman_vault/vault_auth_method_test.rb
@@ -118,7 +123,7 @@ homepage: https://github.com/dm-drogeriemarkt/foreman_vault
118
123
  licenses:
119
124
  - GPL-3.0
120
125
  metadata: {}
121
- post_install_message:
126
+ post_install_message:
122
127
  rdoc_options: []
123
128
  require_paths:
124
129
  - lib
@@ -133,8 +138,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
138
  - !ruby/object:Gem::Version
134
139
  version: '0'
135
140
  requirements: []
136
- rubygems_version: 3.1.2
137
- signing_key:
141
+ rubygems_version: 3.2.28
142
+ signing_key:
138
143
  specification_version: 4
139
144
  summary: Adds support for using credentials from Hashicorp Vault
140
145
  test_files:
@@ -142,11 +147,14 @@ test_files:
142
147
  - test/unit/services/foreman_vault/vault_client_test.rb
143
148
  - test/unit/services/foreman_vault/vault_policy_test.rb
144
149
  - test/unit/services/foreman_vault/vault_auth_method_test.rb
150
+ - test/models/vault_policy_template_test.rb
145
151
  - test/models/vault_connection_test.rb
146
152
  - test/models/foreman_vault/orchestration/vault_policy_test.rb
147
153
  - test/factories/vault_policy_template.rb
148
154
  - test/factories/vault_connection.rb
149
155
  - test/factories/vault_setting.rb
156
+ - test/lib/tasks/push_policies_test.rb
157
+ - test/lib/tasks/push_auth_methods_test.rb
150
158
  - test/fixtures/ca.crt
151
159
  - test/test_plugin_helper.rb
152
160
  - test/jobs/refresh_vault_tokens_test.rb