summaryrefslogtreecommitdiff
path: root/lib/chef/resource/chef_vault_secret.rb
blob: a2292439e6b464677e5e1d014660de166c127c84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#
# Author:: Joshua Timberman <joshua@chef.io>
# Copyright:: 2014-2020, Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "../resource"
require "chef-vault"

class Chef
  class Resource
    class ChefVaultSecret < Chef::Resource
      resource_name :chef_vault_secret
      provides :chef_vault_secret

      introduced "16.0"
      description "Use the chef_vault_secret resource to store secrets in Chef Vault items. Where possible and relevant, this resource attempts to map behavior and functionality to the knife vault sub-commands."
      examples <<~DOC
        To create a 'foo' item in an existing 'bar' data bag:

        ```ruby
        chef_vault_secret 'foo' do
          data_bag 'bar'
          raw_data({'auth' => 'baz'})
          admins 'jtimberman'
          search '*:*'
        end
        ```

        To allow multiple admins access to an item:

        ```ruby
        chef_vault_secret 'root-password' do
          admins 'jtimberman,paulmooring'
          data_bag 'secrets'
          raw_data({'auth' => 'DontUseThisPasswordForRoot'})
          search '*:*'
        end
        ```
      DOC

      property :id, String, name_property: true,
        description: "The name of the data bag item if it differs from the name of the resource block"

      property :data_bag, String, required: true, desired_state: false,
        description: "The data bag that contains the item."

      property :admins, [String, Array], required: true, desired_state: false,
        description: "A list of admin users who should have access to the item. Corresponds to the 'admin' option when using the chef-vault knife plugin. Can be specified as a comma separated string or an array."

      property :clients, [String, Array], desired_state: false,
        description: "A search query for the nodes' API clients that should have access to the item."

      property :search, String, default: "*:*", desired_state: false,
        description: "Search query that would match the same used for the clients, gets stored as a field in the item."

      property :raw_data, [Hash, Mash], default: {},
        description: "The raw data, as a Ruby Hash, that will be stored in the item."

      property :environment, [String, NilClass], desired_state: false,
        description: "The Chef environment of the data if storing per environment values."

      load_current_value do
        begin
          item = ChefVault::Item.load(data_bag, id)
          raw_data item.raw_data
          clients item.get_clients
          admins item.get_admins
          search item.search
        rescue ChefVault::Exceptions::KeysNotFound
          current_value_does_not_exist!
        rescue Net::HTTPServerException => e
          current_value_does_not_exist! if e.response_code == "404"
        end
      end

      action :create do
        description "Creates the item, or updates it if it already exists."

        converge_if_changed do
          item = ChefVault::Item.new(new_resource.data_bag, new_resource.id)

          Chef::Log.debug("#{new_resource.id} environment: '#{new_resource.environment}'")
          item.raw_data = if new_resource.environment.nil?
                            new_resource.raw_data.merge("id" => new_resource.id)
                          else
                            { "id" => new_resource.id, new_resource.environment => new_resource.raw_data }
                          end

          Chef::Log.debug("#{new_resource.id} search query: '#{new_resource.search}'")
          item.search(new_resource.search)
          Chef::Log.debug("#{new_resource.id} clients: '#{new_resource.clients}'")
          item.clients([new_resource.clients].flatten.join(",")) unless new_resource.clients.nil?
          Chef::Log.debug("#{new_resource.id} admins (users): '#{new_resource.admins}'")
          item.admins([new_resource.admins].flatten.join(","))
          item.save
        end
      end

      action :create_if_missing do
        description "Calls the create action unless it exists."

        action_create if current_resource.nil?
      end

      action :delete do
        description "Deletes the item and the item's keys ('id'_keys)."

        converge_by("remove #{new_resource.id} and #{new_resource.id}_keys from #{new_resource.data_bag}") do
          chef_data_bag_item new_resource.id do
            data_bag new_resource.data_bag
            action :delete
          end

          chef_data_bag_item [new_resource.id, "keys"].join("_") do
            data_bag new_resource.data_bag
            action :delete
          end
        end
      end
    end
  end
end