dd-vault 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +26 -0
  5. data/CHANGELOG.md +228 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +362 -0
  8. data/README.md +214 -0
  9. data/Rakefile +6 -0
  10. data/lib/vault/api/approle.rb +218 -0
  11. data/lib/vault/api/auth.rb +316 -0
  12. data/lib/vault/api/auth_tls.rb +92 -0
  13. data/lib/vault/api/auth_token.rb +242 -0
  14. data/lib/vault/api/help.rb +33 -0
  15. data/lib/vault/api/logical.rb +150 -0
  16. data/lib/vault/api/secret.rb +156 -0
  17. data/lib/vault/api/sys/audit.rb +91 -0
  18. data/lib/vault/api/sys/auth.rb +116 -0
  19. data/lib/vault/api/sys/health.rb +63 -0
  20. data/lib/vault/api/sys/init.rb +83 -0
  21. data/lib/vault/api/sys/leader.rb +48 -0
  22. data/lib/vault/api/sys/lease.rb +49 -0
  23. data/lib/vault/api/sys/mount.rb +103 -0
  24. data/lib/vault/api/sys/policy.rb +92 -0
  25. data/lib/vault/api/sys/seal.rb +81 -0
  26. data/lib/vault/api/sys.rb +25 -0
  27. data/lib/vault/api.rb +12 -0
  28. data/lib/vault/client.rb +447 -0
  29. data/lib/vault/configurable.rb +48 -0
  30. data/lib/vault/defaults.rb +197 -0
  31. data/lib/vault/encode.rb +19 -0
  32. data/lib/vault/errors.rb +72 -0
  33. data/lib/vault/persistent/connection.rb +42 -0
  34. data/lib/vault/persistent/pool.rb +48 -0
  35. data/lib/vault/persistent/timed_stack_multi.rb +70 -0
  36. data/lib/vault/persistent.rb +1158 -0
  37. data/lib/vault/request.rb +43 -0
  38. data/lib/vault/response.rb +89 -0
  39. data/lib/vault/vendor/connection_pool/timed_stack.rb +178 -0
  40. data/lib/vault/vendor/connection_pool/version.rb +5 -0
  41. data/lib/vault/vendor/connection_pool.rb +150 -0
  42. data/lib/vault/version.rb +3 -0
  43. data/lib/vault.rb +49 -0
  44. data/vault.gemspec +30 -0
  45. metadata +185 -0
@@ -0,0 +1,43 @@
1
+ module Vault
2
+ class Request
3
+ attr_reader :client
4
+
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ # @return [String]
10
+ def to_s
11
+ "#<#{self.class.name}>"
12
+ end
13
+
14
+ # @return [String]
15
+ def inspect
16
+ "#<#{self.class.name}:0x#{"%x" % (self.object_id << 1)}>"
17
+ end
18
+
19
+ private
20
+
21
+ include EncodePath
22
+
23
+ # Removes the given header fields from options and returns the result. This
24
+ # modifies the given options in place.
25
+ #
26
+ # @param [Hash] options
27
+ #
28
+ # @return [Hash]
29
+ def extract_headers!(options = {})
30
+ extract = {
31
+ wrap_ttl: Vault::Client::WRAP_TTL_HEADER,
32
+ }
33
+
34
+ {}.tap do |h|
35
+ extract.each do |k,v|
36
+ if options[k]
37
+ h[v] = options.delete(k)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,89 @@
1
+ module Vault
2
+ class Response
3
+ # Defines a new field. This is designed to be used by the subclass as a
4
+ # mini-DSL.
5
+ #
6
+ # @example Default
7
+ # field :data
8
+ #
9
+ # @example With a mutator
10
+ # field :present, as: :present?
11
+ #
12
+ # @param n [Symbol] the name of the field
13
+ # @option opts [Symbol] :as alias for method name
14
+ #
15
+ # @!visibility private
16
+ def self.field(n, opts = {})
17
+ self.fields[n] = opts
18
+
19
+ if opts[:as].nil?
20
+ attr_reader n
21
+ else
22
+ define_method(opts[:as]) do
23
+ instance_variable_get(:"@#{n}")
24
+ end
25
+ end
26
+ end
27
+
28
+ # Returns the list of fields defined on this subclass.
29
+ # @!visibility private
30
+ def self.fields
31
+ @fields ||= {}
32
+ end
33
+
34
+ # Decodes the given object (usually a Hash) into an instance of this class.
35
+ #
36
+ # @param object [Hash<Symbol, Object>]
37
+ def self.decode(object)
38
+ self.new(object)
39
+ end
40
+
41
+ def initialize(opts = {})
42
+ # Initialize all fields as nil to start
43
+ self.class.fields.each do |k, _|
44
+ instance_variable_set(:"@#{k}", nil)
45
+ end
46
+
47
+ # For each supplied option, set the instance variable if it was defined
48
+ # as a field.
49
+ opts.each do |k, v|
50
+ if self.class.fields.key?(k)
51
+ opts = self.class.fields[k]
52
+
53
+ if (m = opts[:load]) && !v.nil?
54
+ v = m.call(v)
55
+ end
56
+
57
+ if opts[:freeze]
58
+ v = v.freeze
59
+ end
60
+
61
+ instance_variable_set(:"@#{k}", v)
62
+ end
63
+ end
64
+ end
65
+
66
+ # Create a hash-bashed representation of this response.
67
+ #
68
+ # @return [Hash]
69
+ def to_h
70
+ self.class.fields.inject({}) do |h, (k, opts)|
71
+ if opts[:as].nil?
72
+ h[k] = self.public_send(k)
73
+ else
74
+ h[k] = self.public_send(opts[:as])
75
+ end
76
+
77
+ if !h[k].nil? && !h[k].is_a?(Array) && h[k].respond_to?(:to_h)
78
+ h[k] = h[k].to_h
79
+ end
80
+
81
+ h
82
+ end
83
+ end
84
+
85
+ def ==(other)
86
+ self.to_h == other.to_h
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,178 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ module Vault; end
5
+
6
+ ##
7
+ # Raised when you attempt to retrieve a connection from a pool that has been
8
+ # shut down.
9
+
10
+ class Vault::ConnectionPool::PoolShuttingDownError < RuntimeError; end
11
+
12
+ ##
13
+ # The TimedStack manages a pool of homogeneous connections (or any resource
14
+ # you wish to manage). Connections are created lazily up to a given maximum
15
+ # number.
16
+
17
+ # Examples:
18
+ #
19
+ # ts = TimedStack.new(1) { MyConnection.new }
20
+ #
21
+ # # fetch a connection
22
+ # conn = ts.pop
23
+ #
24
+ # # return a connection
25
+ # ts.push conn
26
+ #
27
+ # conn = ts.pop
28
+ # ts.pop timeout: 5
29
+ # #=> raises Timeout::Error after 5 seconds
30
+
31
+ module Vault
32
+ class ConnectionPool::TimedStack
33
+
34
+ ##
35
+ # Creates a new pool with +size+ connections that are created from the given
36
+ # +block+.
37
+
38
+ def initialize(size = 0, &block)
39
+ @create_block = block
40
+ @created = 0
41
+ @que = []
42
+ @max = size
43
+ @mutex = Mutex.new
44
+ @resource = ConditionVariable.new
45
+ @shutdown_block = nil
46
+ end
47
+
48
+ ##
49
+ # Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be
50
+ # used by subclasses that extend TimedStack.
51
+
52
+ def push(obj, options = {})
53
+ @mutex.synchronize do
54
+ if @shutdown_block
55
+ @shutdown_block.call(obj)
56
+ else
57
+ store_connection obj, options
58
+ end
59
+
60
+ @resource.broadcast
61
+ end
62
+ end
63
+ alias_method :<<, :push
64
+
65
+ ##
66
+ # Retrieves a connection from the stack. If a connection is available it is
67
+ # immediately returned. If no connection is available within the given
68
+ # timeout a Timeout::Error is raised.
69
+ #
70
+ # +:timeout+ is the only checked entry in +options+ and is preferred over
71
+ # the +timeout+ argument (which will be removed in a future release). Other
72
+ # options may be used by subclasses that extend TimedStack.
73
+
74
+ def pop(timeout = 0.5, options = {})
75
+ options, timeout = timeout, 0.5 if Hash === timeout
76
+ timeout = options.fetch :timeout, timeout
77
+
78
+ deadline = Time.now + timeout
79
+ @mutex.synchronize do
80
+ loop do
81
+ raise ConnectionPool::PoolShuttingDownError if @shutdown_block
82
+ return fetch_connection(options) if connection_stored?(options)
83
+
84
+ connection = try_create(options)
85
+ return connection if connection
86
+
87
+ to_wait = deadline - Time.now
88
+ raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
89
+ @resource.wait(@mutex, to_wait)
90
+ end
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Shuts down the TimedStack which prevents connections from being checked
96
+ # out. The +block+ is called once for each connection on the stack.
97
+
98
+ def shutdown(&block)
99
+ raise ArgumentError, "shutdown must receive a block" unless block_given?
100
+
101
+ @mutex.synchronize do
102
+ @shutdown_block = block
103
+ @resource.broadcast
104
+
105
+ shutdown_connections
106
+ end
107
+ end
108
+
109
+ ##
110
+ # Returns +true+ if there are no available connections.
111
+
112
+ def empty?
113
+ (@created - @que.length) >= @max
114
+ end
115
+
116
+ ##
117
+ # The number of connections available on the stack.
118
+
119
+ def length
120
+ @max - @created + @que.length
121
+ end
122
+
123
+ private
124
+
125
+ ##
126
+ # This is an extension point for TimedStack and is called with a mutex.
127
+ #
128
+ # This method must returns true if a connection is available on the stack.
129
+
130
+ def connection_stored?(options = nil)
131
+ !@que.empty?
132
+ end
133
+
134
+ ##
135
+ # This is an extension point for TimedStack and is called with a mutex.
136
+ #
137
+ # This method must return a connection from the stack.
138
+
139
+ def fetch_connection(options = nil)
140
+ @que.pop
141
+ end
142
+
143
+ ##
144
+ # This is an extension point for TimedStack and is called with a mutex.
145
+ #
146
+ # This method must shut down all connections on the stack.
147
+
148
+ def shutdown_connections(options = nil)
149
+ while connection_stored?(options)
150
+ conn = fetch_connection(options)
151
+ @shutdown_block.call(conn)
152
+ end
153
+ end
154
+
155
+ ##
156
+ # This is an extension point for TimedStack and is called with a mutex.
157
+ #
158
+ # This method must return +obj+ to the stack.
159
+
160
+ def store_connection(obj, options = nil)
161
+ @que.push obj
162
+ end
163
+
164
+ ##
165
+ # This is an extension point for TimedStack and is called with a mutex.
166
+ #
167
+ # This method must create a connection if and only if the total number of
168
+ # connections allowed has not been met.
169
+
170
+ def try_create(options = nil)
171
+ unless @created == @max
172
+ object = @create_block.call
173
+ @created += 1
174
+ object
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,5 @@
1
+ module Vault
2
+ class ConnectionPool
3
+ VERSION = "2.2.0"
4
+ end
5
+ end
@@ -0,0 +1,150 @@
1
+ require_relative 'connection_pool/version'
2
+ require_relative 'connection_pool/timed_stack'
3
+
4
+
5
+ # Generic connection pool class for e.g. sharing a limited number of network connections
6
+ # among many threads. Note: Connections are lazily created.
7
+ #
8
+ # Example usage with block (faster):
9
+ #
10
+ # @pool = ConnectionPool.new { Redis.new }
11
+ #
12
+ # @pool.with do |redis|
13
+ # redis.lpop('my-list') if redis.llen('my-list') > 0
14
+ # end
15
+ #
16
+ # Using optional timeout override (for that single invocation)
17
+ #
18
+ # @pool.with(:timeout => 2.0) do |redis|
19
+ # redis.lpop('my-list') if redis.llen('my-list') > 0
20
+ # end
21
+ #
22
+ # Example usage replacing an existing connection (slower):
23
+ #
24
+ # $redis = ConnectionPool.wrap { Redis.new }
25
+ #
26
+ # def do_work
27
+ # $redis.lpop('my-list') if $redis.llen('my-list') > 0
28
+ # end
29
+ #
30
+ # Accepts the following options:
31
+ # - :size - number of connections to pool, defaults to 5
32
+ # - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
33
+ #
34
+ module Vault
35
+ class ConnectionPool
36
+ DEFAULTS = {size: 5, timeout: 5}
37
+
38
+ class Error < RuntimeError
39
+ end
40
+
41
+ def self.wrap(options, &block)
42
+ Wrapper.new(options, &block)
43
+ end
44
+
45
+ def initialize(options = {}, &block)
46
+ raise ArgumentError, 'Connection pool requires a block' unless block
47
+
48
+ options = DEFAULTS.merge(options)
49
+
50
+ @size = options.fetch(:size)
51
+ @timeout = options.fetch(:timeout)
52
+
53
+ @available = TimedStack.new(@size, &block)
54
+ @key = :"current-#{@available.object_id}"
55
+ end
56
+
57
+ if Thread.respond_to?(:handle_interrupt)
58
+
59
+ # MRI
60
+ def with(options = {})
61
+ Thread.handle_interrupt(Exception => :never) do
62
+ conn = checkout(options)
63
+ begin
64
+ Thread.handle_interrupt(Exception => :immediate) do
65
+ yield conn
66
+ end
67
+ ensure
68
+ checkin
69
+ end
70
+ end
71
+ end
72
+
73
+ else
74
+
75
+ # jruby 1.7.x
76
+ def with(options = {})
77
+ conn = checkout(options)
78
+ begin
79
+ yield conn
80
+ ensure
81
+ checkin
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ def checkout(options = {})
88
+ conn = if stack.empty?
89
+ timeout = options[:timeout] || @timeout
90
+ @available.pop(timeout: timeout)
91
+ else
92
+ stack.last
93
+ end
94
+
95
+ stack.push conn
96
+ conn
97
+ end
98
+
99
+ def checkin
100
+ conn = pop_connection # mutates stack, must be on its own line
101
+ @available.push(conn) if stack.empty?
102
+
103
+ nil
104
+ end
105
+
106
+ def shutdown(&block)
107
+ @available.shutdown(&block)
108
+ end
109
+
110
+ private
111
+
112
+ def pop_connection
113
+ if stack.empty?
114
+ raise ConnectionPool::Error, 'no connections are checked out'
115
+ else
116
+ stack.pop
117
+ end
118
+ end
119
+
120
+ def stack
121
+ ::Thread.current[@key] ||= []
122
+ end
123
+
124
+ class Wrapper < ::BasicObject
125
+ METHODS = [:with, :pool_shutdown]
126
+
127
+ def initialize(options = {}, &block)
128
+ @pool = ::ConnectionPool.new(options, &block)
129
+ end
130
+
131
+ def with(&block)
132
+ @pool.with(&block)
133
+ end
134
+
135
+ def pool_shutdown(&block)
136
+ @pool.shutdown(&block)
137
+ end
138
+
139
+ def respond_to?(id, *args)
140
+ METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
141
+ end
142
+
143
+ def method_missing(name, *args, &block)
144
+ with do |connection|
145
+ connection.send(name, *args, &block)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,3 @@
1
+ module Vault
2
+ VERSION = "0.12.0"
3
+ end
data/lib/vault.rb ADDED
@@ -0,0 +1,49 @@
1
+ module Vault
2
+ require_relative "vault/errors"
3
+ require_relative "vault/client"
4
+ require_relative "vault/configurable"
5
+ require_relative "vault/defaults"
6
+ require_relative "vault/response"
7
+ require_relative "vault/version"
8
+
9
+ require_relative "vault/api"
10
+
11
+ class << self
12
+ # API client object based off the configured options in {Configurable}.
13
+ #
14
+ # @return [Vault::Client]
15
+ attr_reader :client
16
+
17
+ def setup!
18
+ @client = Vault::Client.new
19
+
20
+ # Set secure SSL options
21
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options].tap do |opts|
22
+ opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
23
+ opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
24
+ opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
25
+ opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
26
+ end
27
+
28
+ self
29
+ end
30
+
31
+ # Delegate all methods to the client object, essentially making the module
32
+ # object behave like a {Client}.
33
+ def method_missing(m, *args, &block)
34
+ if @client.respond_to?(m)
35
+ @client.send(m, *args, &block)
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ # Delegating +respond_to+ to the {Client}.
42
+ def respond_to_missing?(m, include_private = false)
43
+ @client.respond_to?(m, include_private) || super
44
+ end
45
+ end
46
+ end
47
+
48
+ # Load the initial default values
49
+ Vault.setup!
data/vault.gemspec ADDED
@@ -0,0 +1,30 @@
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 = "dd-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