foreman_vault 0.2.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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