vault_api 1.0.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.
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # VaultApi::Client::Policies
4
+ module VaultApi
5
+ class Client
6
+ module Policies
7
+ def create_initial_user_policy(username)
8
+ puts "Creating #{username}_policy"
9
+ if VaultApi.put_policy("#{username}_policy", policy_json(username))
10
+ puts "Created #{username}_policy"
11
+ true
12
+ else
13
+ false
14
+ end
15
+ end
16
+
17
+ def read_policy(username)
18
+ VaultApi.policy("#{username}_policy")
19
+ end
20
+
21
+ def create_policy(username, path = '', capabilities = [])
22
+ policy_rules = {}
23
+ policy_rules[:path] ||= {}
24
+ policy_rules[:path][path.to_s] ||= {}
25
+ policy_rules[:path][path.to_s][:capabilities] = capabilities
26
+ VaultApi.put_policy("#{username}_policy", policy_rules.to_json)
27
+ end
28
+
29
+ def update_policy(username, path = '', capabilities = [])
30
+ policy = VaultApi.policy("#{username}_policy")
31
+ policy_rules = JSON.parse(policy.rules).with_indifferent_access
32
+ policy_rules[:path][path.to_s] ||= {}
33
+ policy_rules[:path][path.to_s][:capabilities] = capabilities
34
+ VaultApi.put_policy("#{username}_policy", policy_rules.to_json)
35
+ end
36
+
37
+ def delete_policy(username)
38
+ VaultApi.delete_policy("#{username}_policy")
39
+ end
40
+
41
+ private
42
+
43
+ def policy_json(username)
44
+ {
45
+ path: {
46
+ "secret/#{VaultApi.env}/#{username}/*" => {
47
+ capabilities: %w[create read update delete list]
48
+ },
49
+ "#{VaultApi.secret_global_base_path}/*" => {
50
+ capabilities: %w[read list]
51
+ },
52
+ :'secret/*' => {
53
+ capabilities: %w[read list]
54
+ },
55
+ :'auth/token/lookup-self' => {
56
+ capabilities: %w[read]
57
+ },
58
+ :'sys/capabilities-self' => {
59
+ capabilities: %w[update read]
60
+ },
61
+ :'sys/mounts' => {
62
+ capabilities: %w[read]
63
+ },
64
+ :'sys/auth' => {
65
+ capabilities: %w[read]
66
+ },
67
+ "sys/policy/#{username}_policy" => {
68
+ capabilities: %w[read]
69
+ }
70
+ }
71
+ }.to_json
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ # VaultApi::Client::Secrets
6
+ module VaultApi
7
+ class Client
8
+ module Secrets
9
+ def secrets(user_name = nil)
10
+ VaultApi.list(VaultApi.secret_base_path(user_name))
11
+ end
12
+
13
+ def read_secret(config_name, user_name = nil)
14
+ VaultApi.read("#{VaultApi.secret_base_path(user_name)}/#{config_name}").data
15
+ end
16
+
17
+ def add_secret(config_file_path, user_name = nil)
18
+ file_basename = File.basename(config_file_path, '.yml')
19
+ secret_path = "#{VaultApi.secret_base_path(user_name)}/#{file_basename}"
20
+
21
+ output_json = JSON.dump(YAML.load_file(config_file_path))
22
+ obj = JSON.parse(output_json)
23
+ content = obj[VaultApi.env.to_s]
24
+ VaultApi.write(secret_path, content)
25
+ end
26
+
27
+ def update_secret(config_file_path, user_name = nil)
28
+ add_secret(config_file_path, user_name) # overwrites existing file
29
+ end
30
+
31
+ def upload_secrets(config_folder_path, user_name = nil)
32
+ Dir.chdir config_folder_path
33
+ Dir.glob('*.yml').each do |file|
34
+ add_secret("#{config_folder_path}/#{file}", user_name)
35
+ end
36
+ end
37
+
38
+ def delete_secret(config_name, user_name = nil)
39
+ VaultApi.delete("#{VaultApi.secret_base_path(user_name)}/#{config_name}")
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'securerandom'
6
+
7
+ # VaultApi::Client::Users
8
+ module VaultApi
9
+ class Client
10
+ module Users
11
+ def create_user(username)
12
+ secure_password = SecureRandom.hex(12)
13
+
14
+ creds = {
15
+ 'password' => secure_password.to_s,
16
+ 'policies' => "#{username}_policy"
17
+ }
18
+ uri = URI.parse("#{VaultApi.address}/v1/#{VaultApi.auth_users_path}/#{username}")
19
+
20
+ http = Net::HTTP.new(uri.host, uri.port)
21
+ http.use_ssl = true
22
+
23
+ request = Net::HTTP::Post.new(uri.request_uri)
24
+ request.body = creds.to_json
25
+ request['X-Vault-Token'] = VaultApi.token.to_s
26
+
27
+ http.request(request)
28
+
29
+ creds
30
+ end
31
+
32
+ def create_user_with_secret(username)
33
+ users = VaultApi.list(VaultApi.auth_users_path)
34
+
35
+ if users.include? username.to_s
36
+ puts "Vault user '#{username}' already exists."
37
+ # exit 1
38
+ else
39
+ create_initial_user_policy(username)
40
+ creds = create_user(username)
41
+ add_secrets_to_user_from_global(username)
42
+
43
+ creds
44
+ end
45
+ end
46
+
47
+ def add_secrets_to_user_from_global(username)
48
+ global_path = VaultApi.secret_global_base_path
49
+ secrets = VaultApi.list(global_path)
50
+
51
+ secrets.each do |filename|
52
+ path_admin = "#{global_path}/#{filename}"
53
+ data = VaultApi.read(path_admin).data
54
+ user_path = "secret/#{VaultApi.env}/#{username}/#{filename}"
55
+ VaultApi.write(user_path, data)
56
+ end
57
+ end
58
+
59
+ def delete_user(username)
60
+ VaultApi.delete("/#{VaultApi.auth_users_path}/#{username}")
61
+ delete_policy(username)
62
+ delete_path(VaultApi.secret_user_base_path(username))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('api', __dir__)
4
+
5
+ module VaultApi
6
+ # Wrapper for the VaultApi REST API.
7
+ class Client < API
8
+ Dir[File.expand_path('client/*.rb', __dir__)].each { |f| require f }
9
+
10
+ include VaultApi::Client::Paths
11
+ include VaultApi::Client::Users
12
+ include VaultApi::Client::Entries
13
+ include VaultApi::Client::Secrets
14
+ include VaultApi::Client::Policies
15
+ end
16
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VaultApi
4
+ module Configuration
5
+ VALID_OPTIONS_KEYS = %i[
6
+ address
7
+ token
8
+ user
9
+ password
10
+ env
11
+
12
+ logger
13
+ ].freeze
14
+
15
+ # Use the default Faraday adapter.
16
+ # DEFAULT_ADAPTER = Faraday.default_adapter
17
+
18
+ # By default use the main api URL.
19
+ DEFAULT_ADDRESS = ''
20
+
21
+ attr_accessor *VALID_OPTIONS_KEYS
22
+
23
+ # Convenience method to allow configuration options to be set in a block
24
+ def configure
25
+ yield self
26
+ end
27
+
28
+ def options
29
+ VALID_OPTIONS_KEYS.each_with_object({}) do |key, option|
30
+ option[key] = send(key)
31
+ end
32
+ end
33
+
34
+ # When this module is extended, reset all settings.
35
+ def self.extended(base)
36
+ base.reset
37
+ end
38
+
39
+ # Reset all configuration settings to default values.
40
+ def reset
41
+ self.address = DEFAULT_ADDRESS
42
+ # self.adapter = DEFAULT_ADAPTER
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vault'
4
+ module VaultApi
5
+ module Connection
6
+ # private
7
+
8
+ def connection
9
+ if token
10
+ connection_obj = Vault::Client.new(address: address, token: token)
11
+ else
12
+ connection_obj = Vault::Client.new(address: address)
13
+ connection_obj.auth.userpass(user, password)
14
+ end
15
+ connection_obj
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VaultApi
4
+ class Error < StandardError
5
+ def initialize(e)
6
+ @wrapped_exception = nil
7
+
8
+ if e.respond_to?(:backtrace)
9
+ super(e.message)
10
+ @wrapped_exception = e
11
+ else
12
+ super(e.to_s)
13
+ end
14
+ end
15
+
16
+ def backtrace
17
+ if @wrapped_exception
18
+ @wrapped_exception.backtrace
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def inspect
25
+ inner = ''
26
+ inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
27
+ inner << " #{super}" if inner.empty?
28
+ %(#<#{self.class}#{inner}>)
29
+ end
30
+ end
31
+
32
+ class ConnectionError < Error; end
33
+ class AuthorizationError < Error; end
34
+ class BadRequestError < Error; end
35
+ class RecordNotFoundError < Error; end
36
+
37
+ class TimeoutError < Error; end
38
+ class NotFoundError < Error; end
39
+ class SSLError < Error; end
40
+ class ParseError < Error; end
41
+ class UnauthorizedError < Error; end
42
+
43
+ %i[Error
44
+ ConnectionError AuthorizationError BadRequestError RecordNotFoundError
45
+ TimeoutError NotFoundError SSLError ParseError UnauthorizedError].each do |const|
46
+ Error.const_set(const, VaultApi.const_get(const))
47
+ end
48
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VaultApi
4
+ module Request
5
+ def list(params)
6
+ request(:list, params)
7
+ end
8
+
9
+ def read(params)
10
+ request(:read, params)
11
+ end
12
+
13
+ def write(path, config)
14
+ request(:write, path, config)
15
+ end
16
+
17
+ def delete(params)
18
+ request(:delete, params)
19
+ end
20
+
21
+ def policy(params)
22
+ request_sys(:policy, params)
23
+ end
24
+
25
+ def put_policy(params, rules)
26
+ request_sys(:put_policy, params, rules)
27
+ end
28
+
29
+ def delete_policy(params)
30
+ request_sys(:delete_policy, params)
31
+ end
32
+
33
+ private
34
+
35
+ def request(method, *params)
36
+ begin
37
+ response = case method
38
+ when :write
39
+ connection.logical.send(method, params[0], params[1])
40
+ else
41
+ connection.logical.send(method, params[0])
42
+ end
43
+ rescue StandardError => e
44
+ raise Error, e
45
+ end
46
+
47
+ response
48
+ end
49
+
50
+ def request_sys(method, *params)
51
+ begin
52
+ response = case method
53
+ when :put_policy
54
+ connection.sys.send(method, params[0], params[1])
55
+ else
56
+ connection.sys.send(method, params[0])
57
+ end
58
+ rescue StandardError => e
59
+ raise Error, e
60
+ end
61
+
62
+ response
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VaultApi
4
+ VERSION = '1.0.0'
5
+ end
data/lib/vault_api.rb ADDED
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pry'
4
+ require 'active_support/all'
5
+
6
+ require 'vault_api/api'
7
+ require 'vault_api/client'
8
+ require 'vault_api/version'
9
+
10
+ require File.expand_path('vault_api/configuration', __dir__)
11
+ require File.expand_path('vault_api/api', __dir__)
12
+ require File.expand_path('vault_api/client', __dir__)
13
+ require File.expand_path('vault_api/error', __dir__)
14
+
15
+ module VaultApi
16
+ extend Configuration
17
+ # Alias for VaultApi::Client.new
18
+ # @return [VaultApi::Client]
19
+ def self.client(options = {})
20
+ VaultApi::Client.new(options)
21
+ end
22
+
23
+ # Delegate to VaultApi::Client
24
+ def self.method_missing(method, *args, &block)
25
+ return super unless client.respond_to?(method)
26
+
27
+ client.send(method, *args, &block)
28
+ end
29
+
30
+ def self.secret_base_path(user_name = nil)
31
+ if user_name.present?
32
+ secret_user_base_path(user_name)
33
+ elsif VaultApi.user.present? # && VaultApi.password.present?
34
+ secret_user_base_path
35
+ elsif VaultApi.token.present?
36
+ secret_global_base_path
37
+ else
38
+ ''
39
+ end
40
+ end
41
+
42
+ def self.secret_global_base_path
43
+ "secret/global/#{VaultApi.env}"
44
+ end
45
+
46
+ def self.secret_user_base_path(user_name = nil)
47
+ if user_name.present?
48
+ "secret/#{VaultApi.env}/#{user_name}"
49
+ else
50
+ "secret/#{VaultApi.env}/#{VaultApi.user}"
51
+ end
52
+ end
53
+
54
+ def self.auth_users_path
55
+ 'auth/userpass/users'
56
+ end
57
+ end
data/vault_api.gemspec ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'vault_api/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'vault_api'
9
+ spec.version = VaultApi::VERSION
10
+ spec.authors = ['Sachin S Wagh']
11
+ spec.email = ['sachinwagh2392@gmail.com']
12
+
13
+ spec.summary = 'Client for the vault_api API'
14
+ spec.description = 'Client for the vault_api API'
15
+ spec.homepage = ''
16
+ spec.license = 'MIT'
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
+ else
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ # spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
31
+
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
34
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
35
+ spec.require_paths = ['lib']
36
+
37
+ spec.add_development_dependency 'bundler'
38
+ spec.add_development_dependency 'byebug'
39
+ spec.add_development_dependency 'rake', '~> 10.0'
40
+ spec.add_development_dependency 'rspec', '~> 3.0'
41
+ spec.add_development_dependency 'rspec-expectations'
42
+ spec.add_development_dependency 'rspec-mocks'
43
+ spec.add_development_dependency 'rspec_junit_formatter'
44
+
45
+ ###
46
+ spec.add_development_dependency 'awesome_print'
47
+ spec.add_development_dependency 'pry'
48
+ spec.add_development_dependency 'rubocop'
49
+ spec.add_development_dependency 'rubocop-rspec'
50
+
51
+ spec.add_runtime_dependency 'activesupport'
52
+ # spec.add_runtime_dependency 'faraday'
53
+ # spec.add_runtime_dependency 'faraday_middleware'
54
+ spec.add_runtime_dependency 'hashie'
55
+ spec.add_runtime_dependency 'nokogiri'
56
+ # spec.add_runtime_dependency 'vcr'
57
+ spec.add_runtime_dependency 'rest-client'
58
+ spec.add_runtime_dependency 'vcr', '3.0'
59
+ ###
60
+
61
+ spec.add_runtime_dependency('vault')
62
+ spec.add_runtime_dependency('webmock')
63
+ end