vault 0.10.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7e5fd83ffdf4937b80770ad21d25b1bab18b036b
4
- data.tar.gz: 7d82f65b6e31bd4f8f4887bdc2224040159fc402
2
+ SHA256:
3
+ metadata.gz: 46c570463a1aba190e789e5b2516b4140d48961611ff058235d3b9744e6a6b24
4
+ data.tar.gz: c84a96cf71d9f405281f56629e0fb68a6ce051740ea46da60e35cabf37d8b44e
5
5
  SHA512:
6
- metadata.gz: bad1f9d3302041a5532145af4c1cd07d3f7fe289c5a81d3edd0cd410b0f0130d59f09a8ceea82955cf28b4f36dbefb6fa73cb1145d5e117a01dff537b9a3ab3b
7
- data.tar.gz: 4c0b02bf433aa26c3573f8d6b9728662efc2a0d7fd8138cb486e4c4c044ba6d728c4405b910582f7c34feb3c0788aeb89874ea6999027379502821782e86b018
6
+ metadata.gz: 98a20e963ec212e2269d1c28b581c24b356495789b4b37b20ebcb829c17904b518fc32f9cd2dadfcd59b957361410e7aa61f88e7ad419d72533d0ac1bd0ec68d
7
+ data.tar.gz: 35f0126a7e7ba6173662222a9006cd02bc2f78d6d674533546b68ad87420f99b1e26f1f160058b2a051c36a5faac219921ab24191f9165212ddc8f15c440e0a6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # Vault Ruby Changelog
2
2
 
3
+ ## v0.16.0 (??? ??, 2021)
4
+
5
+ IMPROVEMENTS
6
+
7
+ - The timeout used to get a connection from the connection pool that talks with vault is now configurable. Using `Vault.pool_timeout` or the env var `VAULT_POOL_TIMEOUT`.
8
+
9
+ ## v0.15.0 (July 29, 2020)
10
+
11
+ IMPROVEMENTS
12
+
13
+ - Added support for Resource Quotas
14
+
15
+ ## v0.14.0 (May 28, 2020)
16
+
17
+ IMPROVEMENTS
18
+
19
+ - Added support for the Transform Secrets Engine
20
+
21
+ ## v0.13.2 (May 7, 2020)
22
+
23
+ BUG FIXES
24
+
25
+ - Fixed the ability to use namespace as an option for each request. Previously, that option was ignored.
26
+ - aws-sigv4 gem was unlocked after a bug in 1.1.2 broke CI
27
+
28
+ ## v0.13.1 (April 28, 2020)
29
+
30
+ IMPROVEMENTS
31
+
32
+ - Added support for defining a namespace when initializing the client, as well as options for changing the namespace via method.
33
+ - Added support for sys/namespaces API. Ability to Get, Create, Delete, and List namespaces has been provided.
34
+
35
+ ## v0.13.0 (October 1, 2019)
36
+
37
+ IMPROVEMENTS
38
+
39
+ - Add support for versioned KV secrets in the client
40
+
41
+ ## v0.12.0 (August 14, 2018)
42
+
43
+ IMPROVEMENTS
44
+
45
+ - Expose the github login path as an optional argument
46
+ - Support HTTP basic auth [GH-181]
47
+ - Expose the AWS IAM path to use [GH-180]
48
+ - Add GCP Auth [GH-173]
49
+ - Add shutdown functionality to close persistent connections [GH-175]
50
+
51
+ BUG FIXES
52
+
53
+ - Specifing the hostname for SNI didn't work. The functionality has been disabled for now.
54
+
55
+ ## v0.11.0 (March 19, 2018)
56
+
57
+ IMPROVEMENTS
58
+
59
+ - Access to health has been added.
60
+ - Added ability to handle a Base64 encoded PEM (useful for certs in environment variables)
61
+ - Added IAM EC2 authentication support
62
+ - Add custom mount path support to TLS authentication
63
+
64
+ ## v0.10.1 (May 8, 2017)
65
+
66
+ IMPROVEMENTS
67
+
68
+ - `vault-ruby` is licensed under Mozilla Public License 2.0, and has been for over 2 years. This patch release updates the gemspec to use the correct SPDX ID string for reporting this license, but **no change to the licensing of this gem has occurred**.
69
+
70
+
3
71
  ## v0.10.0 (April 19, 2017)
4
72
 
5
73
  IMPROVEMENTS
data/README.md CHANGED
@@ -1,15 +1,17 @@
1
- Vault Ruby Client [![Build Status](https://secure.travis-ci.org/hashicorp/vault-ruby.svg)](http://travis-ci.org/hashicorp/vault-ruby)
1
+ Vault Ruby Client [![Build Status](https://circleci.com/gh/hashicorp/vault-ruby.svg?style=shield)](https://circleci.com/gh/hashicorp/vault-ruby)
2
2
  =================
3
3
 
4
4
  Vault is the official Ruby client for interacting with [Vault](https://vaultproject.io) by HashiCorp.
5
5
 
6
- **The documentation in this README corresponds to the master branch of the Vault Ruby client. It may contain unreleased features or different APIs than the most recently released version. Please see the Git tag that corresponds to your version of the Vault Ruby client for the proper documentation.**
6
+ **If you're viewing this README from GitHub on the `master` branch, know that it may contain unreleased features or
7
+ different APIs than the most recently released version. Please see the Git tag that corresponds to your version of the
8
+ Vault Ruby client for the proper documentation.**
7
9
 
8
10
  Quick Start
9
11
  -----------
10
12
  Install Ruby 2.0+: [Guide](https://www.ruby-lang.org/en/documentation/installation/).
11
13
 
12
- > Please note that Vault Ruby may work on older Ruby installations like Ruby 1.9, but you **should not** use these versions of Ruby when communicating with a Vault server. Ruby 1.9 has [reached EOL](https://www.ruby-lang.org/en/news/2014/01/10/ruby-1-9-3-will-end-on-2015/) and will no longer receive important security patches or maintenance updates. There _are known security vulnerabilities_ specifically around SSL ciphers, which this library uses to communicate with a Vault server. While many distros still ship with Ruby 1.9 as the default, you are **highly discouraged** from using this library on any version of Ruby lower than Ruby 2.0.
14
+ > Please note that as of Vault Ruby version 0.14.0 versions of Ruby prior to 2.0 are no longer supported.
13
15
 
14
16
  Install via Rubygems:
15
17
 
@@ -18,7 +20,7 @@ Install via Rubygems:
18
20
  or add it to your Gemfile if you're using Bundler:
19
21
 
20
22
  ```ruby
21
- gem "vault", "~> 0.1"
23
+ gem "vault"
22
24
  ```
23
25
 
24
26
  and then run the `bundle` command to install.
@@ -28,6 +30,8 @@ Start a Vault client:
28
30
  ```ruby
29
31
  Vault.address = "http://127.0.0.1:8200" # Also reads from ENV["VAULT_ADDR"]
30
32
  Vault.token = "abcd-1234" # Also reads from ENV["VAULT_TOKEN"]
33
+ # Optional - if using the Namespace enterprise feature
34
+ # Vault.namespace = "my-namespace" # Also reads from ENV["VAULT_NAMESPACE"]
31
35
 
32
36
  Vault.sys.mounts #=> { :secret => #<struct Vault::Mount type="generic", description="generic secret storage"> }
33
37
  ```
@@ -43,6 +47,8 @@ Vault.configure do |config|
43
47
 
44
48
  # The token to authenticate with Vault, also read as ENV["VAULT_TOKEN"]
45
49
  config.token = "abcd-1234"
50
+ # Optional - if using the Namespace enterprise feature
51
+ # config.namespace = "my-namespace" # Also reads from ENV["VAULT_NAMESPACE"]
46
52
 
47
53
  # Proxy connection information, also read as ENV["VAULT_PROXY_(thing)"]
48
54
  config.proxy_address = "..."
@@ -53,6 +59,10 @@ Vault.configure do |config|
53
59
  # Custom SSL PEM, also read as ENV["VAULT_SSL_CERT"]
54
60
  config.ssl_pem_file = "/path/on/disk.pem"
55
61
 
62
+ # As an alternative to a pem file, you can provide the raw PEM string, also read in the following order of preference:
63
+ # ENV["VAULT_SSL_PEM_CONTENTS_BASE64"] then ENV["VAULT_SSL_PEM_CONTENTS"]
64
+ config.ssl_pem_contents = "-----BEGIN ENCRYPTED..."
65
+
56
66
  # Use SSL verification, also read as ENV["VAULT_SSL_VERIFY"]
57
67
  config.ssl_verify = false
58
68
 
@@ -75,6 +85,17 @@ client_1 = Vault::Client.new(address: "https://vault.mycompany.com")
75
85
  client_2 = Vault::Client.new(address: "https://other-vault.mycompany.com")
76
86
  ```
77
87
 
88
+ And if you want to authenticate with a `AWS EC2` :
89
+
90
+ ```ruby
91
+ # Export VAULT_ADDR to ENV then
92
+ # Get the pkcs7 value from AWS
93
+ signature = `curl http://169.254.169.254/latest/dynamic/instance-identity/pkcs7`
94
+ iam_role = `curl http://169.254.169.254/latest/meta-data/iam/security-credentials/`
95
+ vault_token = Vault.auth.aws_ec2(iam_role, signature, nil)
96
+ vault_client = Vault::Client.new(address: ENV["VAULT_ADDR"], token: vault_token.auth.client_token)
97
+ ```
98
+
78
99
  ### Making requests
79
100
  All of the methods and API calls are heavily documented with examples inline using YARD. In order to keep the examples versioned with the code, the README only lists a few examples for using the Vault gem. Please see the inline documentation for the full API documentation. The tests in the 'spec' directory are an additional source of examples.
80
101
 
@@ -103,7 +124,9 @@ For advanced users, the first argument of the block is the attempt number and th
103
124
 
104
125
  ```ruby
105
126
  Vault.with_retries(Vault::HTTPConnectionError, Vault::HTTPError) do |attempt, e|
106
- log "Received exception #{e} from Vault - attempt #{attempt}"
127
+ if e
128
+ log "Received exception #{e} from Vault - attempt #{attempt}"
129
+ end
107
130
  Vault.logical.read("secret/bacon")
108
131
  end
109
132
  ```
@@ -192,7 +215,8 @@ Development
192
215
 
193
216
  Important Notes:
194
217
 
195
- - **All new features must include test coverage.** At a bare minimum, Unit tests are required. It is preferred if you include acceptance tests as well.
196
- - **The tests must be be idempotent.** The HTTP calls made during a test should be able to be run over and over.
218
+ - **All new features must include test coverage.** At a bare minimum, Unit tests are required. It is preferred if you include integration tests as well.
219
+ - **The tests must be idempotent.** The HTTP calls made during a test should be able to be run over and over.
197
220
  - **Tests are order independent.** The default RSpec configuration randomizes the test order, so this should not be a problem.
198
221
  - **Integration tests require Vault** Vault must be available in the path for the integration tests to pass.
222
+ - **In order to be considered an integration test:** The test MUST use the `vault_test_client` or `vault_redirect_test_client` as the client. This spawns a process, or uses an already existing process from another test, to run against.
data/lib/vault/api.rb CHANGED
@@ -5,8 +5,10 @@ module Vault
5
5
  require_relative "api/auth_tls"
6
6
  require_relative "api/auth"
7
7
  require_relative "api/help"
8
+ require_relative "api/kv"
8
9
  require_relative "api/logical"
9
10
  require_relative "api/secret"
10
11
  require_relative "api/sys"
12
+ require_relative "api/transform"
11
13
  end
12
14
  end
@@ -155,9 +155,9 @@ module Vault
155
155
  # @param [String] github_token
156
156
  #
157
157
  # @return [Secret]
158
- def github(github_token)
158
+ def github(github_token, path="/v1/auth/github/login")
159
159
  payload = {token: github_token}
160
- json = client.post("/v1/auth/github/login", JSON.fast_generate(payload))
160
+ json = client.post(path, JSON.fast_generate(payload))
161
161
  secret = Secret.decode(json)
162
162
  client.token = secret.auth.client_token
163
163
  return secret
@@ -173,12 +173,95 @@ module Vault
173
173
  # @param [String] role
174
174
  # @param [String] pkcs7
175
175
  # pkcs7 returned by the instance identity document (with line breaks removed)
176
- # @param [String] nonce
176
+ # @param [String] nonce optional
177
+ # @param [String] route optional
177
178
  #
178
179
  # @return [Secret]
179
- def aws_ec2(role, pkcs7, nonce)
180
- payload = { role: role, pkcs7: pkcs7, nonce: nonce }
181
- json = client.post('/v1/auth/aws-ec2/login', JSON.fast_generate(payload))
180
+ def aws_ec2(role, pkcs7, nonce = nil, route = nil)
181
+ route ||= '/v1/auth/aws-ec2/login'
182
+ payload = { role: role, pkcs7: pkcs7 }
183
+ # Set a custom nonce if client is providing one
184
+ payload[:nonce] = nonce if nonce
185
+ json = client.post(route, JSON.fast_generate(payload))
186
+ secret = Secret.decode(json)
187
+ client.token = secret.auth.client_token
188
+ return secret
189
+ end
190
+
191
+ # Authenticate via AWS IAM auth method by providing a AWS CredentialProvider (either ECS, AssumeRole, etc.)
192
+ # If authentication is successful, the resulting token will be stored on the client and used
193
+ # for future requests.
194
+ #
195
+ # @example
196
+ # Vault.auth.aws_iam("dev-role-iam", Aws::InstanceProfileCredentials.new, "vault.example.com", "https://sts.us-east-2.amazonaws.com") #=> #<Vault::Secret lease_id="">
197
+ #
198
+ # @param [String] role
199
+ # @param [CredentialProvider] credentials_provider
200
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CredentialProvider.html
201
+ # @param [String] iam_auth_header_value optional
202
+ # As of Jan 2018, Vault will accept ANY or NO header if none is configured by the Vault server admin
203
+ # @param [String] sts_endpoint optional
204
+ # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
205
+ # @param [String] route optional
206
+ # @return [Secret]
207
+ def aws_iam(role, credentials_provider, iam_auth_header_value = nil, sts_endpoint = 'https://sts.amazonaws.com', route = nil)
208
+ require "aws-sigv4"
209
+ require "base64"
210
+
211
+ request_body = 'Action=GetCallerIdentity&Version=2011-06-15'
212
+ request_method = 'POST'
213
+
214
+ route ||= '/v1/auth/aws/login'
215
+
216
+ vault_headers = {
217
+ 'User-Agent' => Vault::Client::USER_AGENT,
218
+ 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'
219
+ }
220
+
221
+ vault_headers['X-Vault-AWS-IAM-Server-ID'] = iam_auth_header_value if iam_auth_header_value
222
+
223
+ sig4_headers = Aws::Sigv4::Signer.new(
224
+ service: 'sts',
225
+ region: region_from_sts_endpoint(sts_endpoint),
226
+ credentials_provider: credentials_provider
227
+ ).sign_request(
228
+ http_method: request_method,
229
+ url: sts_endpoint,
230
+ headers: vault_headers,
231
+ body: request_body
232
+ ).headers
233
+
234
+ payload = {
235
+ role: role,
236
+ iam_http_request_method: request_method,
237
+ iam_request_url: Base64.strict_encode64(sts_endpoint),
238
+ iam_request_headers: Base64.strict_encode64(vault_headers.merge(sig4_headers).to_json),
239
+ iam_request_body: Base64.strict_encode64(request_body)
240
+ }
241
+
242
+ json = client.post(route, JSON.fast_generate(payload))
243
+ secret = Secret.decode(json)
244
+ client.token = secret.auth.client_token
245
+ return secret
246
+ end
247
+
248
+ # Authenticate via the GCP authentication method. If authentication is
249
+ # successful, the resulting token will be stored on the client and used
250
+ # for future requests.
251
+ #
252
+ # @example
253
+ # Vault.auth.gcp("read-only", "jwt", "gcp") #=> #<Vault::Secret lease_id="">
254
+ #
255
+ # @param [String] role
256
+ # @param [String] jwt
257
+ # jwt returned by the instance identity metadata, or iam api
258
+ # @param [String] path optional
259
+ # the path were the gcp auth backend is mounted
260
+ #
261
+ # @return [Secret]
262
+ def gcp(role, jwt, path = 'gcp')
263
+ payload = { role: role, jwt: jwt }
264
+ json = client.post("/v1/auth/#{CGI.escape(path)}/login", JSON.fast_generate(payload))
182
265
  secret = Secret.decode(json)
183
266
  client.token = secret.auth.client_token
184
267
  return secret
@@ -194,18 +277,40 @@ module Vault
194
277
  # @example Reading a pem from disk
195
278
  # Vault.auth.tls(File.read("/path/to/my/certificate.pem")) #=> #<Vault::Secret lease_id="">
196
279
  #
280
+ # @example Sending to a cert authentication backend mounted at a custom location
281
+ # Vault.auth.tls(pem_contents, 'custom/location') #=> #<Vault::Secret lease_id="">
282
+ #
197
283
  # @param [String] pem (default: the configured SSL pem file or contents)
198
284
  # The raw pem contents to use for the login procedure.
199
285
  #
286
+ # @param [String] path (default: 'cert')
287
+ # The path to the auth backend to use for the login procedure.
288
+ #
200
289
  # @return [Secret]
201
- def tls(pem = nil)
290
+ def tls(pem = nil, path = 'cert')
202
291
  new_client = client.dup
203
292
  new_client.ssl_pem_contents = pem if !pem.nil?
204
293
 
205
- json = new_client.post("/v1/auth/cert/login")
294
+ json = new_client.post("/v1/auth/#{CGI.escape(path)}/login")
206
295
  secret = Secret.decode(json)
207
296
  client.token = secret.auth.client_token
208
297
  return secret
209
298
  end
299
+
300
+ private
301
+
302
+ # Parse an AWS region from a STS endpoint
303
+ # STS in the China (Beijing) region (cn-north-1) is sts.cn-north-1.amazonaws.com.cn
304
+ # Take care changing below regex with that edge case in mind
305
+ #
306
+ # @param [String] sts_endpoint
307
+ # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
308
+ #
309
+ # @return [String] aws region
310
+ def region_from_sts_endpoint(sts_endpoint)
311
+ valid_sts_endpoint = %r{https:\/\/sts\.?(.*).amazonaws.com}.match(sts_endpoint)
312
+ raise "Unable to parse STS endpoint #{sts_endpoint}" unless valid_sts_endpoint
313
+ valid_sts_endpoint[1].empty? ? 'us-east-1' : valid_sts_endpoint[1]
314
+ end
210
315
  end
211
316
  end
@@ -102,7 +102,7 @@ module Vault
102
102
  # Lookup information about the current token.
103
103
  #
104
104
  # @example
105
- # Vault.auth_token.lookup_self("abcd-...") #=> #<Vault::Secret lease_id="">
105
+ # Vault.auth_token.lookup("abcd-...") #=> #<Vault::Secret lease_id="">
106
106
  #
107
107
  # @param [String] token
108
108
  # @param [Hash] options
@@ -215,7 +215,7 @@ module Vault
215
215
  # @return [true]
216
216
  def revoke_accessor(accessor, options = {})
217
217
  headers = extract_headers!(options)
218
- client.put("/v1/auth/accessor/revoke-accessor", JSON.fast_generate(
218
+ client.put("/v1/auth/token/revoke-accessor", JSON.fast_generate(
219
219
  accessor: accessor,
220
220
  ), headers)
221
221
  return true
@@ -0,0 +1,207 @@
1
+ require_relative "secret"
2
+ require_relative "../client"
3
+ require_relative "../request"
4
+ require_relative "../response"
5
+
6
+ module Vault
7
+ class Client
8
+ # A proxy to the {KV} methods.
9
+ # @return [KV]
10
+ def kv(mount)
11
+ KV.new(self, mount)
12
+ end
13
+ end
14
+
15
+ class KV < Request
16
+ attr_reader :mount
17
+
18
+ def initialize(client, mount)
19
+ super client
20
+
21
+ @mount = mount
22
+ end
23
+
24
+ # List the names of secrets at the given path, if the path supports
25
+ # listing. If the the path does not exist, an empty array will be returned.
26
+ #
27
+ # @example
28
+ # Vault.kv("secret").list("foo") #=> ["bar", "baz"]
29
+ #
30
+ # @param [String] path
31
+ # the path to list
32
+ #
33
+ # @return [Array<String>]
34
+ def list(path = "", options = {})
35
+ headers = extract_headers!(options)
36
+ json = client.list("/v1/#{mount}/metadata/#{encode_path(path)}", {}, headers)
37
+ json[:data][:keys] || []
38
+ rescue HTTPError => e
39
+ return [] if e.code == 404
40
+ raise
41
+ end
42
+
43
+ # Read the secret at the given path. If the secret does not exist, +nil+
44
+ # will be returned. The latest version is returned by default, but you
45
+ # can request a specific version.
46
+ #
47
+ # @example
48
+ # Vault.kv("secret").read("password") #=> #<Vault::Secret lease_id="">
49
+ #
50
+ # @param [String] path
51
+ # the path to read
52
+ # @param [Integer] version
53
+ # the version of the secret
54
+ #
55
+ # @return [Secret, nil]
56
+ def read(path, version = nil, options = {})
57
+ headers = extract_headers!(options)
58
+ params = {}
59
+ params[:version] = version unless version.nil?
60
+
61
+ json = client.get("/v1/#{mount}/data/#{encode_path(path)}", params, headers)
62
+ return Secret.decode(json[:data])
63
+ rescue HTTPError => e
64
+ return nil if e.code == 404
65
+ raise
66
+ end
67
+
68
+ # Read the metadata of a secret at the given path. If the secret does not
69
+ # exist, nil will be returned.
70
+ #
71
+ # @example
72
+ # Vault.kv("secret").read_metadata("password") => {...}
73
+ #
74
+ # @param [String] path
75
+ # the path to read
76
+ #
77
+ # @return [Hash, nil]
78
+ def read_metadata(path)
79
+ client.get("/v1/#{mount}/metadata/#{encode_path(path)}")[:data]
80
+ rescue HTTPError => e
81
+ return nil if e.code == 404
82
+ raise
83
+ end
84
+
85
+ # Write the secret at the given path with the given data. Note that the
86
+ # data must be a {Hash}!
87
+ #
88
+ # @example
89
+ # Vault.logical.write("secret/password", value: "secret") #=> #<Vault::Secret lease_id="">
90
+ #
91
+ # @param [String] path
92
+ # the path to write
93
+ # @param [Hash] data
94
+ # the data to write
95
+ #
96
+ # @return [Secret]
97
+ def write(path, data = {}, options = {})
98
+ headers = extract_headers!(options)
99
+ json = client.post("/v1/#{mount}/data/#{encode_path(path)}", JSON.fast_generate(:data => data), headers)
100
+ if json.nil?
101
+ return true
102
+ else
103
+ return Secret.decode(json)
104
+ end
105
+ end
106
+
107
+ # Write the metadata of a secret at the given path. Note that the data must
108
+ # be a {Hash}.
109
+ #
110
+ # @example
111
+ # Vault.kv("secret").write_metadata("password", max_versions => 3)
112
+ #
113
+ # @param [String] path
114
+ # the path to write
115
+ # @param [Hash] metadata
116
+ # the metadata to write
117
+ #
118
+ # @return [true]
119
+ def write_metadata(path, metadata = {})
120
+ client.post("/v1/#{mount}/metadata/#{encode_path(path)}", JSON.fast_generate(metadata))
121
+
122
+ true
123
+ end
124
+
125
+ # Delete the secret at the given path. If the secret does not exist, vault
126
+ # will still return true.
127
+ #
128
+ # @example
129
+ # Vault.logical.delete("secret/password") #=> true
130
+ #
131
+ # @param [String] path
132
+ # the path to delete
133
+ #
134
+ # @return [true]
135
+ def delete(path)
136
+ client.delete("/v1/#{mount}/data/#{encode_path(path)}")
137
+
138
+ true
139
+ end
140
+
141
+ # Mark specific versions of a secret as deleted.
142
+ #
143
+ # @example
144
+ # Vault.kv("secret").delete_versions("password", [1, 2])
145
+ #
146
+ # @param [String] path
147
+ # the path to remove versions from
148
+ # @param [Array<Integer>] versions
149
+ # an array of versions to remove
150
+ #
151
+ # @return [true]
152
+ def delete_versions(path, versions)
153
+ client.post("/v1/#{mount}/delete/#{encode_path(path)}", JSON.fast_generate(versions: versions))
154
+
155
+ true
156
+ end
157
+
158
+ # Mark specific versions of a secret as active.
159
+ #
160
+ # @example
161
+ # Vault.kv("secret").undelete_versions("password", [1, 2])
162
+ #
163
+ # @param [String] path
164
+ # the path to enable versions for
165
+ # @param [Array<Integer>] versions
166
+ # an array of versions to mark as undeleted
167
+ #
168
+ # @return [true]
169
+ def undelete_versions(path, versions)
170
+ client.post("/v1/#{mount}/undelete/#{encode_path(path)}", JSON.fast_generate(versions: versions))
171
+
172
+ true
173
+ end
174
+
175
+ # Completely remove a secret and its metadata.
176
+ #
177
+ # @example
178
+ # Vault.kv("secret").destroy("password")
179
+ #
180
+ # @param [String] path
181
+ # the path to remove
182
+ #
183
+ # @return [true]
184
+ def destroy(path)
185
+ client.delete("/v1/#{mount}/metadata/#{encode_path(path)}")
186
+
187
+ true
188
+ end
189
+
190
+ # Completely remove specific versions of a secret.
191
+ #
192
+ # @example
193
+ # Vault.kv("secret").destroy_versions("password", [1, 2])
194
+ #
195
+ # @param [String] path
196
+ # the path to remove versions from
197
+ # @param [Array<Integer>] versions
198
+ # an array of versions to destroy
199
+ #
200
+ # @return [true]
201
+ def destroy_versions(path, versions)
202
+ client.post("/v1/#{mount}/destroy/#{encode_path(path)}", JSON.fast_generate(versions: versions))
203
+
204
+ true
205
+ end
206
+ end
207
+ end