vault 0.12.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: 5eeb054b599872ef7a225999772c0b486554f339
4
- data.tar.gz: b1524f9c83ea5c2e5c3c74f66fafaa69e47b8c94
2
+ SHA256:
3
+ metadata.gz: 46c570463a1aba190e789e5b2516b4140d48961611ff058235d3b9744e6a6b24
4
+ data.tar.gz: c84a96cf71d9f405281f56629e0fb68a6ce051740ea46da60e35cabf37d8b44e
5
5
  SHA512:
6
- metadata.gz: d49f63c294a4165babfa3c6f2d2dfd90baabff86a964b895eb16b4ab08bd493fb07266c53f95fe2a255e2435df6f2ba42a921a97dfa1b94fe235a8c43e6aa77f
7
- data.tar.gz: c4aa7afc3b28a1aa5912ee184c7a24d07d6c19d61ae922c409cd0c0aaf6cf3efff3c5dd2269ab1d65251a7f1ce35f8cd8810c7b4d516bfd83fff93153ad39ed1
6
+ metadata.gz: 98a20e963ec212e2269d1c28b581c24b356495789b4b37b20ebcb829c17904b518fc32f9cd2dadfcd59b957361410e7aa61f88e7ad419d72533d0ac1bd0ec68d
7
+ data.tar.gz: 35f0126a7e7ba6173662222a9006cd02bc2f78d6d674533546b68ad87420f99b1e26f1f160058b2a051c36a5faac219921ab24191f9165212ddc8f15c440e0a6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
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
+
3
41
  ## v0.12.0 (August 14, 2018)
4
42
 
5
43
  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 = "..."
@@ -85,7 +91,8 @@ And if you want to authenticate with a `AWS EC2` :
85
91
  # Export VAULT_ADDR to ENV then
86
92
  # Get the pkcs7 value from AWS
87
93
  signature = `curl http://169.254.169.254/latest/dynamic/instance-identity/pkcs7`
88
- vault_token = Vault.auth.aws_ec2(ENV['EC2_ROLE'], signature, nil)
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)
89
96
  vault_client = Vault::Client.new(address: ENV["VAULT_ADDR"], token: vault_token.auth.client_token)
90
97
  ```
91
98
 
@@ -117,7 +124,9 @@ For advanced users, the first argument of the block is the attempt number and th
117
124
 
118
125
  ```ruby
119
126
  Vault.with_retries(Vault::HTTPConnectionError, Vault::HTTPError) do |attempt, e|
120
- log "Received exception #{e} from Vault - attempt #{attempt}"
127
+ if e
128
+ log "Received exception #{e} from Vault - attempt #{attempt}"
129
+ end
121
130
  Vault.logical.read("secret/bacon")
122
131
  end
123
132
  ```
@@ -206,7 +215,8 @@ Development
206
215
 
207
216
  Important Notes:
208
217
 
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.
210
- - **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.
211
220
  - **Tests are order independent.** The default RSpec configuration randomizes the test order, so this should not be a problem.
212
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
@@ -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.
data/lib/vault/api/sys.rb CHANGED
@@ -21,5 +21,7 @@ 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"
26
+ require_relative "sys/quota"
25
27
  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,83 @@
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
+ def namespaces(scoped=nil)
22
+ path = ["v1", scoped, "sys", "namespaces"].compact
23
+ json = client.list(path.join("/"))
24
+ json = json[:data] if json[:data]
25
+ if json[:key_info]
26
+ json = json[:key_info]
27
+ hash = {}
28
+ json.each do |k,v|
29
+ hash[k.to_s.chomp("/").to_sym] = Namespace.decode(v)
30
+ end
31
+ hash
32
+ else
33
+ json
34
+ end
35
+ end
36
+
37
+ # Create a namespace. Nests the namespace if a namespace header is provided.
38
+ #
39
+ # @example
40
+ # Vault.sys.create_namespace("foo")
41
+ #
42
+ # @param [String] namespace
43
+ # the potential path of the namespace, without any parent path provided
44
+ #
45
+ # @return [true]
46
+ def create_namespace(namespace)
47
+ client.put("/v1/sys/namespaces/#{namespace}", {})
48
+ return true
49
+ end
50
+
51
+ # Delete a namespace. Raises an error if the namespace provided is not empty.
52
+ #
53
+ # @example
54
+ # Vault.sys.delete_namespace("foo")
55
+ #
56
+ # @param [String] namespace
57
+ # the path of the namespace to be deleted
58
+ #
59
+ # @return [true]
60
+ def delete_namespace(namespace)
61
+ client.delete("/v1/sys/namespaces/#{namespace}")
62
+ return true
63
+ end
64
+
65
+ # Retrieve a namespace by path.
66
+ #
67
+ # @example
68
+ # Vault.sys.get_namespace("foo")
69
+ #
70
+ # @param [String] namespace
71
+ # the path of the namespace ot be retrieved
72
+ #
73
+ # @return [Namespace]
74
+ def get_namespace(namespace)
75
+ json = client.get("/v1/sys/namespaces/#{namespace}")
76
+ if data = json.dig(:data)
77
+ Namespace.decode(data)
78
+ else
79
+ json
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,107 @@
1
+ module Vault
2
+ class Quota < Response
3
+ # @!attribute [r] name
4
+ # Name of the quota rule.
5
+ # @return [String]
6
+ field :name
7
+
8
+ # @!attribute [r] path
9
+ # Namespace/Path combination the quota applies to.
10
+ # @return [String]
11
+ field :path
12
+
13
+ # @!attribute [r] type
14
+ # Type of the quota rule, must be one of "lease-count" or "rate-limit"
15
+ # @return [String]
16
+ field :type
17
+ end
18
+
19
+ class RateLimitQuota < Quota
20
+ # @!attribute [r] rate
21
+ # The rate at which allowed requests are refilled per second by the quota
22
+ # rule.
23
+ # @return [Float]
24
+ field :rate
25
+
26
+ # @!attribute [r] burst
27
+ # The maximum number of requests at any given second allowed by the quota
28
+ # rule.
29
+ # @return [Int]
30
+ field :burst
31
+ end
32
+
33
+ class LeaseCountQuota < Quota
34
+ # @!attribute [r] counter
35
+ # Number of currently active leases for the quota.
36
+ # @return [Int]
37
+ field :counter
38
+
39
+ # @!attribute [r] max_leases
40
+ # The maximum number of allowed leases for this quota.
41
+ # @return [Int]
42
+ field :max_leases
43
+ end
44
+
45
+ class Sys
46
+ def quotas(type)
47
+ path = generate_path(type)
48
+ json = client.list(path)
49
+ if data = json.dig(:data, :key_info)
50
+ data.map do |item|
51
+ type_class(type).decode(item)
52
+ end
53
+ else
54
+ json
55
+ end
56
+ end
57
+
58
+ def create_quota(type, name, opts={})
59
+ path = generate_path(type, name)
60
+ client.post(path, JSON.fast_generate(opts))
61
+ return true
62
+ end
63
+
64
+ def delete_quota(type, name)
65
+ path = generate_path(type, name)
66
+ client.delete(path)
67
+ return true
68
+ end
69
+
70
+ def get_quota(type, name)
71
+ path = generate_path(type, name)
72
+ response = client.get(path)
73
+ if data = response[:data]
74
+ type_class(type).decode(data)
75
+ end
76
+ end
77
+
78
+ def get_quota_config
79
+ client.get("v1/sys/quotas/config")
80
+ end
81
+
82
+ def update_quota_config(opts={})
83
+ client.post("v1/sys/quotas/config", JSON.fast_generate(opts))
84
+ return true
85
+ end
86
+
87
+ private
88
+
89
+ def generate_path(type, name=nil)
90
+ verify_type(type)
91
+ path = ["v1", "sys", "quotas", type, name].compact
92
+ path.join("/")
93
+ end
94
+
95
+ def verify_type(type)
96
+ return if ["rate-limit", "lease-count"].include?(type)
97
+ raise ArgumentError, "type must be one of \"rate-limit\" or \"lease-count\""
98
+ end
99
+
100
+ def type_class(type)
101
+ case type
102
+ when "lease-count" then LeaseCountQuota
103
+ when "rate-limit" then RateLimitQuota
104
+ end
105
+ end
106
+ end
107
+ 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
data/lib/vault/client.rb CHANGED
@@ -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
 
@@ -83,7 +86,7 @@ module Vault
83
86
  @lock.synchronize do
84
87
  return @nhp if @nhp
85
88
 
86
- @nhp = PersistentHTTP.new("vault-ruby", nil, pool_size)
89
+ @nhp = PersistentHTTP.new("vault-ruby", nil, pool_size, pool_timeout)
87
90
 
88
91
  if proxy_address
89
92
  proxy_uri = URI.parse "http://#{proxy_address}"
@@ -255,6 +258,12 @@ module Vault
255
258
  headers[TOKEN_HEADER] ||= token
256
259
  end
257
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
+
258
267
  # Add headers
259
268
  headers.each do |key, value|
260
269
  request.add_field(key, value)
@@ -7,12 +7,14 @@ 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,
13
14
  :proxy_port,
14
15
  :proxy_username,
15
16
  :pool_size,
17
+ :pool_timeout,
16
18
  :read_timeout,
17
19
  :ssl_ciphers,
18
20
  :ssl_pem_contents,
@@ -30,6 +30,9 @@ module Vault
30
30
  # The default size of the connection pool
31
31
  DEFAULT_POOL_SIZE = 16
32
32
 
33
+ # The default timeout in seconds for retrieving a connection from the connection pool
34
+ DEFAULT_POOL_TIMEOUT = 0.5
35
+
33
36
  # The set of exceptions that are detect and retried by default
34
37
  # with `with_retries`
35
38
  RETRIED_EXCEPTIONS = [HTTPServerError]
@@ -61,6 +64,13 @@ module Vault
61
64
  nil
62
65
  end
63
66
 
67
+
68
+ # Vault Namespace, if any.
69
+ # @return [String, nil]
70
+ def namespace
71
+ ENV["VAULT_NAMESPACE"]
72
+ end
73
+
64
74
  # The SNI host to use when connecting to Vault via TLS.
65
75
  # @return [String, nil]
66
76
  def hostname
@@ -78,12 +88,22 @@ module Vault
78
88
  # @return Integer
79
89
  def pool_size
80
90
  if var = ENV["VAULT_POOL_SIZE"]
81
- return var.to_i
91
+ var.to_i
82
92
  else
83
93
  DEFAULT_POOL_SIZE
84
94
  end
85
95
  end
86
96
 
97
+ # The timeout for getting a connection from the connection pool that communicates with Vault
98
+ # @return Float
99
+ def pool_timeout
100
+ if var = ENV["VAULT_POOL_TIMEOUT"]
101
+ var.to_f
102
+ else
103
+ DEFAULT_POOL_TIMEOUT
104
+ end
105
+ end
106
+
87
107
  # The HTTP Proxy server address as a string
88
108
  # @return [String, nil]
89
109
  def proxy_address
@@ -202,11 +202,6 @@ class PersistentHTTP
202
202
 
203
203
  HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
204
204
 
205
- ##
206
- # The default connection pool size is 1/4 the allowed open files.
207
-
208
- DEFAULT_POOL_SIZE = 16
209
-
210
205
  ##
211
206
  # The version of PersistentHTTP you are using
212
207
 
@@ -505,7 +500,7 @@ class PersistentHTTP
505
500
  # Defaults to 1/4 the number of allowed file handles. You can have no more
506
501
  # than this many threads with active HTTP transactions.
507
502
 
508
- def initialize name=nil, proxy=nil, pool_size=DEFAULT_POOL_SIZE
503
+ def initialize name=nil, proxy=nil, pool_size=Vault::Defaults::DEFAULT_POOL_SIZE, pool_timeout=Vault::Defaults::DEFAULT_POOL_TIMEOUT
509
504
  @name = name
510
505
 
511
506
  @debug_output = nil
@@ -525,7 +520,7 @@ class PersistentHTTP
525
520
  @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
526
521
  Socket.const_defined? :TCP_NODELAY
527
522
 
528
- @pool = PersistentHTTP::Pool.new size: pool_size do |http_args|
523
+ @pool = PersistentHTTP::Pool.new size: pool_size, timeout: pool_timeout do |http_args|
529
524
  PersistentHTTP::Connection.new Net::HTTP, http_args, @ssl_generation
530
525
  end
531
526
 
@@ -31,7 +31,7 @@ class PersistentHTTP::Pool < Vault::ConnectionPool # :nodoc:
31
31
  stack = stacks[net_http_args]
32
32
 
33
33
  if stack.empty? then
34
- conn = @available.pop connection_args: net_http_args
34
+ conn = @available.pop @timeout, connection_args: net_http_args
35
35
  else
36
36
  conn = stack.last
37
37
  end
data/lib/vault/request.rb CHANGED
@@ -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|
data/lib/vault/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Vault
2
- VERSION = "0.12.0"
2
+ VERSION = "0.16.0"
3
3
  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.12.0
4
+ version: 0.16.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-08-14 00:00:00.000000000 Z
11
+ date: 2021-03-17 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,14 +115,9 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
- - ".gitignore"
119
- - ".rspec"
120
- - ".travis.yml"
121
118
  - CHANGELOG.md
122
- - Gemfile
123
119
  - LICENSE
124
120
  - README.md
125
- - Rakefile
126
121
  - lib/vault.rb
127
122
  - lib/vault/api.rb
128
123
  - lib/vault/api/approle.rb
@@ -130,6 +125,7 @@ files:
130
125
  - lib/vault/api/auth_tls.rb
131
126
  - lib/vault/api/auth_token.rb
132
127
  - lib/vault/api/help.rb
128
+ - lib/vault/api/kv.rb
133
129
  - lib/vault/api/logical.rb
134
130
  - lib/vault/api/secret.rb
135
131
  - lib/vault/api/sys.rb
@@ -140,8 +136,15 @@ files:
140
136
  - lib/vault/api/sys/leader.rb
141
137
  - lib/vault/api/sys/lease.rb
142
138
  - lib/vault/api/sys/mount.rb
139
+ - lib/vault/api/sys/namespace.rb
143
140
  - lib/vault/api/sys/policy.rb
141
+ - lib/vault/api/sys/quota.rb
144
142
  - lib/vault/api/sys/seal.rb
143
+ - lib/vault/api/transform.rb
144
+ - lib/vault/api/transform/alphabet.rb
145
+ - lib/vault/api/transform/role.rb
146
+ - lib/vault/api/transform/template.rb
147
+ - lib/vault/api/transform/transformation.rb
145
148
  - lib/vault/client.rb
146
149
  - lib/vault/configurable.rb
147
150
  - lib/vault/defaults.rb
@@ -157,7 +160,6 @@ files:
157
160
  - lib/vault/vendor/connection_pool/timed_stack.rb
158
161
  - lib/vault/vendor/connection_pool/version.rb
159
162
  - lib/vault/version.rb
160
- - vault.gemspec
161
163
  homepage: https://github.com/hashicorp/vault-ruby
162
164
  licenses:
163
165
  - MPL-2.0
@@ -170,15 +172,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
170
172
  requirements:
171
173
  - - ">="
172
174
  - !ruby/object:Gem::Version
173
- version: '0'
175
+ version: '2.0'
174
176
  required_rubygems_version: !ruby/object:Gem::Requirement
175
177
  requirements:
176
178
  - - ">="
177
179
  - !ruby/object:Gem::Version
178
180
  version: '0'
179
181
  requirements: []
180
- rubyforge_project:
181
- rubygems_version: 2.6.14
182
+ rubygems_version: 3.2.3
182
183
  signing_key:
183
184
  specification_version: 4
184
185
  summary: Vault is a Ruby API client for interacting with a Vault server.
data/.gitignore DELETED
@@ -1,42 +0,0 @@
1
- ### Ruby ###
2
- *.gem
3
- *.rbc
4
- /.config
5
- /.vscode
6
- /coverage/
7
- /InstalledFiles
8
- /pkg/
9
- /spec/reports/
10
- /test/tmp/
11
- /test/version_tmp/
12
- /tmp/
13
- /vendor/bundle/
14
- /vendor/ruby/
15
-
16
- ## Specific to RubyMotion:
17
- .dat*
18
- .repl_history
19
- build/
20
-
21
- ## Documentation cache and generated files:
22
- /.yardoc/
23
- /_yardoc/
24
- /doc/
25
- /rdoc/
26
-
27
- ## Environment normalisation:
28
- /.bundle/
29
- /vendor/bundle
30
- /lib/bundler/man/
31
-
32
- # for a library or gem, you might want to ignore these files since the code is
33
- # intended to run in multiple environments; otherwise, check them in:
34
- Gemfile.lock
35
- .ruby-version
36
- .ruby-gemset
37
-
38
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
- .rvmrc
40
-
41
- # Project-specific
42
- spec/tmp
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
data/.travis.yml DELETED
@@ -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
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task default: :spec
data/vault.gemspec DELETED
@@ -1,30 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "vault/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "vault"
8
- spec.version = Vault::VERSION
9
- spec.authors = ["Seth Vargo"]
10
- spec.email = ["sethvargo@gmail.com"]
11
- spec.licenses = ["MPL-2.0"]
12
-
13
- spec.summary = "Vault is a Ruby API client for interacting with a Vault server."
14
- spec.description = spec.summary
15
- spec.homepage = "https://github.com/hashicorp/vault-ruby"
16
-
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
21
-
22
- spec.add_runtime_dependency "aws-sigv4"
23
-
24
- spec.add_development_dependency "bundler"
25
- spec.add_development_dependency "pry"
26
- spec.add_development_dependency "rake", "~> 12.0"
27
- spec.add_development_dependency "rspec", "~> 3.5"
28
- spec.add_development_dependency "yard"
29
- spec.add_development_dependency "webmock", "~> 2.3"
30
- end