vault 0.11.0 → 0.14.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: dcb5948ae3d3f53115a8e0aef77b70d18c042550
4
- data.tar.gz: 143ffc1b71f99550e83548ba92f1edb0e7e4ef0a
2
+ SHA256:
3
+ metadata.gz: cd39dd364209eee99a269200710d73fbdd4d9fe7007b5b8db78d1861b5671c53
4
+ data.tar.gz: 3b9cd61321644f7a45d87e7f08dadd7cb6c20da6537e46a01f82b2975d302852
5
5
  SHA512:
6
- metadata.gz: a7473e4c1791e62f8814a677d0c71bce2ca9b51a32c534dfd11830bcbbd3a2b8e993dacf3df52bc747fc1473931801733731de5e3a00c1078a9e59f2cbaa75b8
7
- data.tar.gz: 7c7a6793019c4f67a7927f01e839cbe9d0ac288dea9affe6e564d6482eedb4da5553cf01a823b9615c572e06a937bef532fd4a80777feb2488e5b3f41cd7e9a6
6
+ metadata.gz: 9a5e084c51cf4528095d4911acf8b983e1b61673441862fee7a58c640faae85bd81157a2a53d64db3fe8387136f8ceff7da2ea33e295ad6c8813b1e9fd3ff4a0
7
+ data.tar.gz: '0256808c555dece8ff08124fbdfad7a92b054db0c85f3e0e3d907d28b30c9d3f5d4b9e9a96287c1f2b8697e06dfdab57f8ddc0ee66a0947e780b7833e196bbc2'
@@ -0,0 +1,42 @@
1
+ version: 2.1
2
+
3
+ references:
4
+ images:
5
+ ubuntu: &UBUNTU_IMAGE ubuntu-1604:201903-01
6
+
7
+ jobs:
8
+ test:
9
+ machine:
10
+ image: *UBUNTU_IMAGE
11
+ parameters:
12
+ ruby-version:
13
+ type: string
14
+ vault-version:
15
+ type: string
16
+ steps:
17
+ - checkout
18
+ - run:
19
+ name: Install vault
20
+ command: |
21
+ curl -sLo vault.zip https://releases.hashicorp.com/vault/<< parameters.vault-version >>/vault_<< parameters.vault-version >>_linux_amd64.zip
22
+ unzip vault.zip
23
+ mkdir -p ~/bin
24
+ mv vault ~/bin
25
+ export PATH="~/bin:$PATH"
26
+ - run:
27
+ name: Run tests
28
+ command: |
29
+ export VAULT_VERSION=<< parameters.vault-version >>
30
+ rvm use << parameters.ruby-version >> --install --binary --fuzzy
31
+ bundle install --jobs=3 --retry=3 --path=vendor/bundle
32
+ bundle exec rake
33
+
34
+ workflows:
35
+ run-tests:
36
+ jobs:
37
+ - test:
38
+ matrix:
39
+ parameters:
40
+ ruby-version: ["2.2", "2.3", "2.4"]
41
+ vault-version: ["1.0.3", "1.1.5", "1.2.4", "1.3.0"]
42
+ name: test-ruby-<< matrix.ruby-version >>-vault-<< matrix.vault-version >>
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.gem
3
3
  *.rbc
4
4
  /.config
5
+ /.vscode
5
6
  /coverage/
6
7
  /InstalledFiles
7
8
  /pkg/
@@ -1,5 +1,45 @@
1
1
  # Vault Ruby Changelog
2
2
 
3
+ ## v0.14.0 (May 28, 2020)
4
+
5
+ IMPROVEMENTS
6
+
7
+ - Added support for the Transform Secrets Engine
8
+
9
+ ## v0.13.2 (May 7, 2020)
10
+
11
+ BUG FIXES
12
+
13
+ - Fixed the ability to use namespace as an option for each request. Previously, that option was ignored.
14
+ - aws-sigv4 gem was unlocked after a bug in 1.1.2 broke CI
15
+
16
+ ## v0.13.1 (April 28, 2020)
17
+
18
+ IMPROVEMENTS
19
+
20
+ - Added support for defining a namespace when initializing the client, as well as options for changing the namespace via method.
21
+ - Added support for sys/namespaces API. Ability to Get, Create, Delete, and List namespaces has been provided.
22
+
23
+ ## v0.13.0 (October 1, 2019)
24
+
25
+ IMPROVEMENTS
26
+
27
+ - Add support for versioned KV secrets in the client
28
+
29
+ ## v0.12.0 (August 14, 2018)
30
+
31
+ IMPROVEMENTS
32
+
33
+ - Expose the github login path as an optional argument
34
+ - Support HTTP basic auth [GH-181]
35
+ - Expose the AWS IAM path to use [GH-180]
36
+ - Add GCP Auth [GH-173]
37
+ - Add shutdown functionality to close persistent connections [GH-175]
38
+
39
+ BUG FIXES
40
+
41
+ - Specifing the hostname for SNI didn't work. The functionality has been disabled for now.
42
+
3
43
  ## v0.11.0 (March 19, 2018)
4
44
 
5
45
  IMPROVEMENTS
data/README.md CHANGED
@@ -28,6 +28,8 @@ Start a Vault client:
28
28
  ```ruby
29
29
  Vault.address = "http://127.0.0.1:8200" # Also reads from ENV["VAULT_ADDR"]
30
30
  Vault.token = "abcd-1234" # Also reads from ENV["VAULT_TOKEN"]
31
+ # Optional - if using the Namespace enterprise feature
32
+ # Vault.namespace = "my-namespace" # Also reads from ENV["VAULT_NAMESPACE"]
31
33
 
32
34
  Vault.sys.mounts #=> { :secret => #<struct Vault::Mount type="generic", description="generic secret storage"> }
33
35
  ```
@@ -43,6 +45,8 @@ Vault.configure do |config|
43
45
 
44
46
  # The token to authenticate with Vault, also read as ENV["VAULT_TOKEN"]
45
47
  config.token = "abcd-1234"
48
+ # Optional - if using the Namespace enterprise feature
49
+ # config.namespace = "my-namespace" # Also reads from ENV["VAULT_NAMESPACE"]
46
50
 
47
51
  # Proxy connection information, also read as ENV["VAULT_PROXY_(thing)"]
48
52
  config.proxy_address = "..."
@@ -85,7 +89,8 @@ And if you want to authenticate with a `AWS EC2` :
85
89
  # Export VAULT_ADDR to ENV then
86
90
  # Get the pkcs7 value from AWS
87
91
  signature = `curl http://169.254.169.254/latest/dynamic/instance-identity/pkcs7`
88
- vault_token = Vault.auth.aws_ec2(ENV['EC2_ROLE'], signature, nil)
92
+ iam_role = `curl http://169.254.169.254/latest/meta-data/iam/security-credentials/`
93
+ vault_token = Vault.auth.aws_ec2(iam_role, signature, nil)
89
94
  vault_client = Vault::Client.new(address: ENV["VAULT_ADDR"], token: vault_token.auth.client_token)
90
95
  ```
91
96
 
@@ -117,7 +122,9 @@ For advanced users, the first argument of the block is the attempt number and th
117
122
 
118
123
  ```ruby
119
124
  Vault.with_retries(Vault::HTTPConnectionError, Vault::HTTPError) do |attempt, e|
120
- log "Received exception #{e} from Vault - attempt #{attempt}"
125
+ if e
126
+ log "Received exception #{e} from Vault - attempt #{attempt}"
127
+ end
121
128
  Vault.logical.read("secret/bacon")
122
129
  end
123
130
  ```
@@ -206,7 +213,8 @@ Development
206
213
 
207
214
  Important Notes:
208
215
 
209
- - **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.
216
+ - **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.
210
217
  - **The tests must be be idempotent.** The HTTP calls made during a test should be able to be run over and over.
211
218
  - **Tests are order independent.** The default RSpec configuration randomizes the test order, so this should not be a problem.
212
219
  - **Integration tests require Vault** Vault must be available in the path for the integration tests to pass.
220
+ - **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.
@@ -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
@@ -193,7 +193,7 @@ module Vault
193
193
  # for future requests.
194
194
  #
195
195
  # @example
196
- # Vault.auth.aws_iam("dev-role-iam", Aws::AssumeRoleCredentials.new, "vault.example.com", "https://sts.us-east-2.amazonaws.com") #=> #<Vault::Secret lease_id="">
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
197
  #
198
198
  # @param [String] role
199
199
  # @param [CredentialProvider] credentials_provider
@@ -202,14 +202,17 @@ module Vault
202
202
  # As of Jan 2018, Vault will accept ANY or NO header if none is configured by the Vault server admin
203
203
  # @param [String] sts_endpoint optional
204
204
  # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
205
+ # @param [String] route optional
205
206
  # @return [Secret]
206
- def aws_iam(role, credentials_provider, iam_auth_header_value = nil, sts_endpoint = 'https://sts.amazonaws.com')
207
+ def aws_iam(role, credentials_provider, iam_auth_header_value = nil, sts_endpoint = 'https://sts.amazonaws.com', route = nil)
207
208
  require "aws-sigv4"
208
209
  require "base64"
209
210
 
210
211
  request_body = 'Action=GetCallerIdentity&Version=2011-06-15'
211
212
  request_method = 'POST'
212
213
 
214
+ route ||= '/v1/auth/aws/login'
215
+
213
216
  vault_headers = {
214
217
  'User-Agent' => Vault::Client::USER_AGENT,
215
218
  'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'
@@ -236,7 +239,29 @@ module Vault
236
239
  iam_request_body: Base64.strict_encode64(request_body)
237
240
  }
238
241
 
239
- json = client.post('/v1/auth/aws/login', JSON.fast_generate(payload))
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))
240
265
  secret = Secret.decode(json)
241
266
  client.token = secret.auth.client_token
242
267
  return secret
@@ -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
@@ -32,6 +32,18 @@ module Vault
32
32
  # @return [Hash<Symbol, Object>]
33
33
  field :data, freeze: true
34
34
 
35
+ # @!attribute [r] metadata
36
+ # Read-only metadata information related to the secret.
37
+ #
38
+ # @example Reading metadata
39
+ # secret = Vault.logical(:versioned).read("secret", "foo")
40
+ # secret.metadata[:created_time] #=> "2018-12-08T04:22:54.168065Z"
41
+ # secret.metadata[:version] #=> 1
42
+ # secret.metadata[:destroyed] #=> false
43
+ #
44
+ # @return [Hash<Symbol, Object>]
45
+ field :metadata, freeze: true
46
+
35
47
  # @!attribute [r] lease_duration
36
48
  # The number of seconds this lease is valid. If this number is 0 or nil,
37
49
  # the secret does not expire.
@@ -21,5 +21,6 @@ require_relative "sys/init"
21
21
  require_relative "sys/leader"
22
22
  require_relative "sys/lease"
23
23
  require_relative "sys/mount"
24
+ require_relative "sys/namespace"
24
25
  require_relative "sys/policy"
25
26
  require_relative "sys/seal"
@@ -16,6 +16,11 @@ module Vault
16
16
  # Type of the mount.
17
17
  # @return [String]
18
18
  field :type
19
+
20
+ # @!attribute [r] type
21
+ # Options given to the mount.
22
+ # @return [Hash<Symbol, Object>]
23
+ field :options
19
24
  end
20
25
 
21
26
  class Sys < Request
@@ -44,8 +49,8 @@ module Vault
44
49
  # the type of mount
45
50
  # @param [String] description
46
51
  # a human-friendly description (optional)
47
- def mount(path, type, description = nil)
48
- payload = { type: type }
52
+ def mount(path, type, description = nil, options = {})
53
+ payload = options.merge type: type
49
54
  payload[:description] = description if !description.nil?
50
55
 
51
56
  client.post("/v1/sys/mounts/#{encode_path(path)}", JSON.fast_generate(payload))
@@ -0,0 +1,85 @@
1
+ module Vault
2
+ class Namespace < Response
3
+ # @!attribute [r] id
4
+ # ID of the namespace
5
+ # @return [String]
6
+ field :id
7
+
8
+ # @!attribute [r] path
9
+ # Path of the namespace, includes parent paths if nested.
10
+ # @return [String]
11
+ field :path
12
+ end
13
+
14
+ class Sys
15
+ # List all namespaces in a given scope. Ignores nested namespaces.
16
+ #
17
+ # @example
18
+ # Vault.sys.namespaces #=> { :foo => #<struct Vault::Namespace id="xxxx1", path="foo/" }
19
+ #
20
+ # @return [Hash<Symbol, Namespace>]
21
+ #
22
+ # NOTE: Due to a bug in Vault Enterprise, to be fixed soon, this method CAN return a pure JSON string if a scoping namespace is provided.
23
+ def namespaces(scoped=nil)
24
+ path = ["v1", scoped, "sys", "namespaces"].compact
25
+ json = client.list(path.join("/"))
26
+ json = json[:data] if json[:data]
27
+ if json[:key_info]
28
+ json = json[:key_info]
29
+ hash = {}
30
+ json.each do |k,v|
31
+ hash[k.to_s.chomp("/").to_sym] = Namespace.decode(v)
32
+ end
33
+ hash
34
+ else
35
+ json
36
+ end
37
+ end
38
+
39
+ # Create a namespace. Nests the namespace if a namespace header is provided.
40
+ #
41
+ # @example
42
+ # Vault.sys.create_namespace("foo")
43
+ #
44
+ # @param [String] namespace
45
+ # the potential path of the namespace, without any parent path provided
46
+ #
47
+ # @return [true]
48
+ def create_namespace(namespace)
49
+ client.put("/v1/sys/namespaces/#{namespace}", {})
50
+ return true
51
+ end
52
+
53
+ # Delete a namespace. Raises an error if the namespace provided is not empty.
54
+ #
55
+ # @example
56
+ # Vault.sys.delete_namespace("foo")
57
+ #
58
+ # @param [String] namespace
59
+ # the path of the namespace to be deleted
60
+ #
61
+ # @return [true]
62
+ def delete_namespace(namespace)
63
+ client.delete("/v1/sys/namespaces/#{namespace}")
64
+ return true
65
+ end
66
+
67
+ # Retrieve a namespace by path.
68
+ #
69
+ # @example
70
+ # Vault.sys.get_namespace("foo")
71
+ #
72
+ # @param [String] namespace
73
+ # the path of the namespace ot be retrieved
74
+ #
75
+ # @return [Namespace]
76
+ def get_namespace(namespace)
77
+ json = client.get("/v1/sys/namespaces/#{namespace}")
78
+ if data = json.dig(:data)
79
+ Namespace.decode(data)
80
+ else
81
+ json
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../client'
2
+ require_relative '../request'
3
+
4
+ module Vault
5
+ class Client
6
+ # A proxy to the {Transform} methods.
7
+ # @return [Transform]
8
+ def transform
9
+ @transform ||= Transform.new(self)
10
+ end
11
+ end
12
+
13
+ class Transform < Request
14
+ def encode(role_name:, **opts)
15
+ opts ||= {}
16
+ client.post("/v1/transform/encode/#{encode_path(role_name)}", JSON.fast_generate(opts))
17
+ end
18
+
19
+ def decode(role_name:, **opts)
20
+ opts ||= {}
21
+ client.post("/v1/transform/decode/#{encode_path(role_name)}", JSON.fast_generate(opts))
22
+ end
23
+ end
24
+ end
25
+
26
+ require_relative 'transform/alphabet'
27
+ require_relative 'transform/role'
28
+ require_relative 'transform/template'
29
+ require_relative 'transform/transformation'
@@ -0,0 +1,43 @@
1
+ require_relative '../../request'
2
+ require_relative '../../response'
3
+
4
+ module Vault
5
+ class Transform < Request
6
+ class Alphabet < Response
7
+ # @!attribute [r] id
8
+ # String listing all possible characters of the alphabet
9
+ # @return [String]
10
+ field :alphabet
11
+ end
12
+
13
+ def create_alphabet(name, alphabet:, **opts)
14
+ opts ||= {}
15
+ opts[:alphabet] = alphabet
16
+ client.post("/v1/transform/alphabet/#{encode_path(name)}", JSON.fast_generate(opts))
17
+ return true
18
+ end
19
+
20
+ def get_alphabet(name)
21
+ json = client.get("/v1/transform/alphabet/#{encode_path(name)}")
22
+ if data = json.dig(:data)
23
+ Alphabet.decode(data)
24
+ else
25
+ json
26
+ end
27
+ end
28
+
29
+ def delete_alphabet(name)
30
+ client.delete("/v1/transform/alphabet/#{encode_path(name)}")
31
+ true
32
+ end
33
+
34
+ def alphabets
35
+ json = client.list("/v1/transform/alphabet")
36
+ if keys = json.dig(:data, :keys)
37
+ keys
38
+ else
39
+ json
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ require_relative '../../request'
2
+ require_relative '../../response'
3
+
4
+ module Vault
5
+ class Transform < Request
6
+ class Role < Response
7
+ # @!attribute [r] transformations
8
+ # Array of all transformations the role has access to
9
+ # @return [Array<String>]
10
+ field :transformations
11
+ end
12
+
13
+ def create_role(name, **opts)
14
+ opts ||= {}
15
+ client.post("/v1/transform/role/#{encode_path(name)}", JSON.fast_generate(opts))
16
+ return true
17
+ end
18
+
19
+ def get_role(name)
20
+ json = client.get("/v1/transform/role/#{encode_path(name)}")
21
+ if data = json.dig(:data)
22
+ Role.decode(data)
23
+ else
24
+ json
25
+ end
26
+ end
27
+
28
+ def delete_role(name)
29
+ client.delete("/v1/transform/role/#{encode_path(name)}")
30
+ true
31
+ end
32
+
33
+ def roles
34
+ json = client.list("/v1/transform/role")
35
+ if keys = json.dig(:data, :keys)
36
+ keys
37
+ else
38
+ json
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ require_relative '../../request'
2
+ require_relative '../../response'
3
+
4
+ module Vault
5
+ class Transform < Request
6
+ class Template < Response
7
+ # @!attribute [r] alphabet
8
+ # Name of the alphabet to be used in the template
9
+ # @return [String]
10
+ field :alphabet
11
+
12
+ # @!attribute [r] pattern
13
+ # Regex string to detect and match for the template
14
+ # @return [String]
15
+ field :pattern
16
+
17
+ # @!attribute [r] type
18
+ # Type of the template, currently, only "regex" is supported
19
+ # @return [String]
20
+ field :type
21
+ end
22
+
23
+ def create_template(name, type:, pattern:, **opts)
24
+ opts ||= {}
25
+ opts[:type] = type
26
+ opts[:pattern] = pattern
27
+ client.post("/v1/transform/template/#{encode_path(name)}", JSON.fast_generate(opts))
28
+ return true
29
+ end
30
+
31
+ def get_template(name)
32
+ json = client.get("/v1/transform/template/#{encode_path(name)}")
33
+ if data = json.dig(:data)
34
+ Template.decode(data)
35
+ else
36
+ json
37
+ end
38
+ end
39
+
40
+ def delete_template(name)
41
+ client.delete("/v1/transform/template/#{encode_path(name)}")
42
+ true
43
+ end
44
+
45
+ def templates
46
+ json = client.list("/v1/transform/template")
47
+ if keys = json.dig(:data, :keys)
48
+ keys
49
+ else
50
+ json
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../../request'
2
+ require_relative '../../response'
3
+
4
+ module Vault
5
+ class Transform < Request
6
+ class Transformation < Response
7
+ # @!attribute [r] allowed_roles
8
+ # Array of role names that are allowed to use this transformation
9
+ # @return [Array<String>]
10
+ field :allowed_roles
11
+
12
+ # @!attribute [r] templates
13
+ # Array of template names accessible to this transformation
14
+ # @return [Array<String>]
15
+ field :templates
16
+
17
+ # @!attribute [r] tweak_source
18
+ # String representing how a tweak is provided for this transformation.
19
+ # Available tweaks are "supplied", "generated", and "internal"
20
+ # @return [String]
21
+ field :tweak_source
22
+
23
+ # @!attribute [r] type
24
+ # String representing the type of transformation this is.
25
+ # Available types are "fpe", and "masking"
26
+ # @return [String]
27
+ field :type
28
+ end
29
+
30
+ def create_transformation(name, type:, template:, **opts)
31
+ opts ||= {}
32
+ opts[:type] = type
33
+ opts[:template] = template
34
+ client.post("/v1/transform/transformation/#{encode_path(name)}", JSON.fast_generate(opts))
35
+ return true
36
+ end
37
+
38
+ def get_transformation(name)
39
+ json = client.get("/v1/transform/transformation/#{encode_path(name)}")
40
+ if data = json.dig(:data)
41
+ Transformation.decode(data)
42
+ else
43
+ json
44
+ end
45
+ end
46
+
47
+ def delete_transformation(name)
48
+ client.delete("/v1/transform/transformation/#{encode_path(name)}")
49
+ true
50
+ end
51
+
52
+ def transformations
53
+ json = client.list("/v1/transform/transformation")
54
+ if keys = json.dig(:data, :keys)
55
+ keys
56
+ else
57
+ json
58
+ end
59
+ end
60
+ end
61
+ end
@@ -16,6 +16,9 @@ module Vault
16
16
  # The name of the header used to hold the Vault token.
17
17
  TOKEN_HEADER = "X-Vault-Token".freeze
18
18
 
19
+ # The name of the header used to hold the Namespace.
20
+ NAMESPACE_HEADER = "X-Vault-Namespace".freeze
21
+
19
22
  # The name of the header used to hold the wrapped request ttl.
20
23
  WRAP_TTL_HEADER = "X-Vault-Wrap-TTL".freeze
21
24
 
@@ -85,10 +88,6 @@ module Vault
85
88
 
86
89
  @nhp = PersistentHTTP.new("vault-ruby", nil, pool_size)
87
90
 
88
- if hostname
89
- @nhp.hostname = hostname
90
- end
91
-
92
91
  if proxy_address
93
92
  proxy_uri = URI.parse "http://#{proxy_address}"
94
93
 
@@ -158,6 +157,12 @@ module Vault
158
157
 
159
158
  private :pool
160
159
 
160
+ # Shutdown any open pool connections. Pool will be recreated upon next request.
161
+ def shutdown
162
+ @nhp.shutdown()
163
+ @nhp = nil
164
+ end
165
+
161
166
  # Creates and yields a new client object with the given token. This may be
162
167
  # used safely in a threadsafe manner because the original client remains
163
168
  # unchanged. The value of the block is returned.
@@ -236,6 +241,9 @@ module Vault
236
241
  # Build the URI and request object from the given information
237
242
  uri = build_uri(verb, path, data)
238
243
  request = class_for_request(verb).new(uri.request_uri)
244
+ if uri.userinfo()
245
+ request.basic_auth uri.user, uri.password
246
+ end
239
247
 
240
248
  if proxy_address and uri.scheme.downcase == "https"
241
249
  raise SecurityError, "no direct https connection to vault"
@@ -250,6 +258,12 @@ module Vault
250
258
  headers[TOKEN_HEADER] ||= token
251
259
  end
252
260
 
261
+ # Add the Vault Namespace header - users could still override this on a
262
+ # per-request basis
263
+ if !namespace.nil?
264
+ headers[NAMESPACE_HEADER] ||= namespace
265
+ end
266
+
253
267
  # Add headers
254
268
  headers.each do |key, value|
255
269
  request.add_field(key, value)
@@ -7,6 +7,7 @@ module Vault
7
7
  :address,
8
8
  :token,
9
9
  :hostname,
10
+ :namespace,
10
11
  :open_timeout,
11
12
  :proxy_address,
12
13
  :proxy_password,
@@ -61,6 +61,13 @@ module Vault
61
61
  nil
62
62
  end
63
63
 
64
+
65
+ # Vault Namespace, if any.
66
+ # @return [String, nil]
67
+ def namespace
68
+ ENV["VAULT_NAMESPACE"]
69
+ end
70
+
64
71
  # The SNI host to use when connecting to Vault via TLS.
65
72
  # @return [String, nil]
66
73
  def hostname
@@ -29,6 +29,7 @@ module Vault
29
29
  def extract_headers!(options = {})
30
30
  extract = {
31
31
  wrap_ttl: Vault::Client::WRAP_TTL_HEADER,
32
+ namespace: Vault::Client::NAMESPACE_HEADER,
32
33
  }
33
34
 
34
35
  {}.tap do |h|
@@ -1,3 +1,3 @@
1
1
  module Vault
2
- VERSION = "0.11.0"
2
+ VERSION = "0.14.0"
3
3
  end
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency "aws-sigv4"
23
23
 
24
- spec.add_development_dependency "bundler"
25
- spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "bundler", "~> 2"
25
+ spec.add_development_dependency "pry", "~> 0.13.1"
26
26
  spec.add_development_dependency "rake", "~> 12.0"
27
27
  spec.add_development_dependency "rspec", "~> 3.5"
28
- spec.add_development_dependency "yard"
29
- spec.add_development_dependency "webmock", "~> 2.3"
28
+ spec.add_development_dependency "yard", "~> 0.9.24"
29
+ spec.add_development_dependency "webmock", "~> 3.8.3"
30
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vault
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Vargo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-19 00:00:00.000000000 Z
11
+ date: 2020-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sigv4
@@ -28,30 +28,30 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '2'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.13.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.13.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -84,30 +84,30 @@ dependencies:
84
84
  name: yard
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 0.9.24
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 0.9.24
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: webmock
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '2.3'
103
+ version: 3.8.3
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '2.3'
110
+ version: 3.8.3
111
111
  description: Vault is a Ruby API client for interacting with a Vault server.
112
112
  email:
113
113
  - sethvargo@gmail.com
@@ -115,9 +115,9 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
+ - ".circleci/config.yml"
118
119
  - ".gitignore"
119
120
  - ".rspec"
120
- - ".travis.yml"
121
121
  - CHANGELOG.md
122
122
  - Gemfile
123
123
  - LICENSE
@@ -130,6 +130,7 @@ files:
130
130
  - lib/vault/api/auth_tls.rb
131
131
  - lib/vault/api/auth_token.rb
132
132
  - lib/vault/api/help.rb
133
+ - lib/vault/api/kv.rb
133
134
  - lib/vault/api/logical.rb
134
135
  - lib/vault/api/secret.rb
135
136
  - lib/vault/api/sys.rb
@@ -140,8 +141,14 @@ files:
140
141
  - lib/vault/api/sys/leader.rb
141
142
  - lib/vault/api/sys/lease.rb
142
143
  - lib/vault/api/sys/mount.rb
144
+ - lib/vault/api/sys/namespace.rb
143
145
  - lib/vault/api/sys/policy.rb
144
146
  - lib/vault/api/sys/seal.rb
147
+ - lib/vault/api/transform.rb
148
+ - lib/vault/api/transform/alphabet.rb
149
+ - lib/vault/api/transform/role.rb
150
+ - lib/vault/api/transform/template.rb
151
+ - lib/vault/api/transform/transformation.rb
145
152
  - lib/vault/client.rb
146
153
  - lib/vault/configurable.rb
147
154
  - lib/vault/defaults.rb
@@ -177,8 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
184
  - !ruby/object:Gem::Version
178
185
  version: '0'
179
186
  requirements: []
180
- rubyforge_project:
181
- rubygems_version: 2.6.14
187
+ rubygems_version: 3.1.2
182
188
  signing_key:
183
189
  specification_version: 4
184
190
  summary: Vault is a Ruby API client for interacting with a Vault server.
@@ -1,26 +0,0 @@
1
- dist: trusty
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
-
6
- env:
7
- - VAULT_VERSION=0.8.3
8
- - VAULT_VERSION=0.7.3
9
- - VAULT_VERSION=0.6.5
10
- - VAULT_VERSION=0.5.3
11
-
12
- before_install:
13
- - curl -sLo vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
14
- - unzip vault.zip
15
- - mkdir -p ~/bin
16
- - mv vault ~/bin
17
- - export PATH="~/bin:$PATH"
18
-
19
- branches:
20
- only:
21
- - master
22
-
23
- rvm:
24
- - 2.2
25
- - 2.3
26
- - 2.4