dsv-sdk 0.0.1.pre.SNAPSHOT

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 202d144ecdbd1258d681b6fb2af3bed61324742348466746f432d38d1860b0bc
4
+ data.tar.gz: acb8f986eec3d37aaa0a01424865b5fd08aa7a04f3a1308c4bcecbcb8850b6ad
5
+ SHA512:
6
+ metadata.gz: 89c1554fce29a350a0f46f94a36c54a69de9f4fe5185682d9df457fcba608d054b3276dc62ae756fda9e05bf84e39111a4d2d6e22550cc119acd46b7b8dd76df
7
+ data.tar.gz: 7074d4be9fa997ef7935f03d9c347de6163c9c8aa5272d49e08500b6a71ab5e4e63c5fd7c5c3c45a8ab0289cce90e8a4f63e2cebcafe0ccf5d3f59d41acf62b0
@@ -0,0 +1,33 @@
1
+ class Vault::Client
2
+ CLIENTS_RESOURCE = "clients".freeze
3
+
4
+ # Fetch desired client information from the specified Vault
5
+ #
6
+ # @param vault [Vault] The initialized Vault
7
+ # @param id [String] The ID of the client
8
+ #
9
+ # @return client [Hash]
10
+ def self.fetch(vault, id)
11
+ @vault = vault
12
+ client = @vault.accessResource("GET", CLIENTS_RESOURCE, id, nil)
13
+ end
14
+
15
+ # Mark the client as ready to be removed
16
+ #
17
+ # @param vault [Vault] The initialized Vault
18
+ # @param id [String] The ID of the client
19
+ def self.delete(vault, id)
20
+ vault.accessResource("DELETE", CLIENTS_RESOURCE, id, nil, nil)
21
+ end
22
+
23
+ # Create the client for the desired Vault
24
+ #
25
+ # @param vault [Vault] The initialized Vault
26
+ # @param role_Name [String] Name of the role to create
27
+ def self.create(vault, role_name)
28
+ client_data = {
29
+ role: role_name
30
+ }
31
+ vault.accessResource("POST", CLIENTS_RESOURCE, "/", client_data)
32
+ end
33
+ end
data/lib/vault/role.rb ADDED
@@ -0,0 +1,16 @@
1
+ require './lib/vault.rb'
2
+ class Vault::Role
3
+ ROLES_RESOURCE = "roles".freeze
4
+
5
+ # Return the specified role
6
+ #
7
+ # @param vault [Vault] The initialized Vault
8
+ # @param name [String] Name of the role
9
+ #
10
+ # @return role [Hash]
11
+ def self.fetch(vault, name)
12
+ @vault = vault
13
+
14
+ role = @vault.accessResource("GET", ROLES_RESOURCE, name, nil)
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+
2
+ # Utilized to fetch secrets from an initialzed +Vault+
3
+ class Vault::Secret
4
+ SECRETS_RESOURCE = "secrets".freeze
5
+
6
+ # Fetch secrets from the server
7
+ #
8
+ # @param vault [Server]
9
+ # @param path [String] path to the secret
10
+ #
11
+ # @return [Hash] of the secret
12
+ def self.fetch(vault, path)
13
+ @vault = vault
14
+
15
+ @secret = @vault.accessResource("GET", SECRETS_RESOURCE, path, nil)
16
+ end
17
+ end
@@ -0,0 +1,183 @@
1
+ require 'json'
2
+ require 'faraday'
3
+ require 'logger'
4
+
5
+ ##
6
+ # If we're in the +test+ environment just dump logs to Null
7
+ # Otherwise, use stdout
8
+ if ENV['test']
9
+ $logger = Logger.new(IO::NULL)
10
+ else
11
+ $logger = Logger.new(STDOUT)
12
+ end
13
+
14
+ ##
15
+ # Invoked when the API returns an +API_AccessDenied+ error
16
+ class AccessDeniedException < StandardError; end;
17
+ class ResourceNotFoundException < StandardError; end;
18
+ class InvalidConfigurationException < StandardError; end;
19
+ class InvalidCredentialsException < StandardError; end;
20
+ class InvalidMethodTypeException < StandardError; end;
21
+ class UnrecognizedResourceException < StandardError; end;
22
+
23
+ class Vault
24
+ DEFAULT_URL_TEMPLATE = "https://%s.secretsvaultcloud.%s/v1/%s%s"
25
+ DEFAULT_TLD = "com"
26
+
27
+ # Initialize a +Vault+ object with provided configuration.
28
+ #
29
+ # @param config [Hash]
30
+ # - client_id: ''
31
+ # - client_secret: ''
32
+ # - tld: 'com'
33
+ # - tenant: ''
34
+ def initialize(config = nil)
35
+
36
+ if config
37
+ @configuration = config.collect{|k,v| [k.to_s, v]}.to_h
38
+ else
39
+ @configuration = {}
40
+
41
+ @configuration['client_id'] = ENV['DSV_CLIENT_ID']
42
+ @configuration['client_secret'] = ENV['DSV_CLIENT_SECRET']
43
+ @configuration['tenant'] = ENV['DSV_TENANT']
44
+ @configuration['tld'] = ENV['DSV_TLD']
45
+ end
46
+
47
+ if @configuration['client_id'].nil? || @configuration['client_secret'].nil?
48
+ $logger.error("Must provide client_id and client_secret")
49
+ raise InvalidConfigurationException
50
+ end
51
+
52
+ $logger.debug("Vault is configured for client_id: #{@configuration['client_id']}")
53
+ end
54
+
55
+ # Helper method to access a resource via API
56
+ #
57
+ # @param method [String] HTTP Request Method
58
+ # @param resource [String] The resource type to invoke
59
+ # @param path [String] The API Path to request
60
+ # @param input [String] Input to send via POST/PUT body
61
+ #
62
+ # @return [Hash] - return Hash of JSON contents
63
+ #
64
+ # - +AccessDeniedException+ is raised if the server responds with an +API_AccessDenied+ error
65
+ # - +InvalidMethodTypeException+ is raised if a method other than +["GET", "POST", "PUT", "DELETE"]+ is provided
66
+ # - +UnrecognizedResourceException+ is raised if a resource other than +["clients", "roles", "secrets"]+ is requested
67
+ def accessResource(method, resource, path, input, parse_json=true)
68
+ unless ["GET", "POST", "PUT", "DELETE"].include?(method.upcase)
69
+ $logger.error "Invalid request method: #{method}"
70
+ raise InvalidMethodTypeException
71
+ end
72
+
73
+ unless ["clients", "roles", "secrets"].include? resource
74
+ message = "unrecognized resource"
75
+ $logger.debug "#{message}, #{resource}"
76
+
77
+ raise UnrecognizedResourceException
78
+ end
79
+
80
+ body = ""
81
+
82
+ unless input.nil?
83
+ body = input.to_json
84
+ end
85
+
86
+ accessToken = getAccessToken
87
+
88
+ # Yikes, normally not a fan of metaprogramming
89
+ # We first ensured that `method` is legit to prevent
90
+ # arbitrary method invocation
91
+ url = urlFor(resource, path)
92
+ $logger.debug "Sending request to: #{url}"
93
+ resp = Faraday.send(method.downcase, url) do | req |
94
+ req.headers['Authorization'] = "Bearer #{accessToken}"
95
+ req.body = body unless body.empty?
96
+
97
+ if ["POST", "PUT"].include?(method.upcase)
98
+ req.headers['Content-Type'] = 'application/json'
99
+ end
100
+ end
101
+
102
+ data = resp.body
103
+
104
+ return data unless parse_json
105
+
106
+ begin
107
+ hash = JSON.parse(data)
108
+
109
+ if hash['errorCode'] == "API_AccessDenied"
110
+ raise AccessDeniedException
111
+ end
112
+
113
+ if hash['message'] == "Invalid permissions"
114
+ raise AccessDeniedException
115
+ end
116
+
117
+ if hash['code'] == 404
118
+ raise ResourceNotFoundException
119
+ end
120
+
121
+ rescue JSON::ParserError => e
122
+ $logger.error "Error parsing JSON: #{e.to_s}"
123
+ raise e
124
+ end
125
+
126
+ return hash
127
+ end
128
+
129
+ # Query API for OAuth token
130
+ #
131
+ # - +InvalidCredentialsException+ is returned if the provided credentials are not valid
132
+ #
133
+ # @return [String]
134
+ def getAccessToken
135
+ grantRequest = {
136
+ grant_type: "client_credentials",
137
+ client_id: @configuration['client_id'],
138
+ client_secret: @configuration['client_secret']
139
+ }.to_json
140
+
141
+ url = urlFor("token")
142
+
143
+ $logger.debug "calling #{url} with client_id #{@configuration['client_id']}"
144
+
145
+ response = Faraday.post(
146
+ url,
147
+ grantRequest,
148
+ "Content-Type" => "application/json"
149
+ )
150
+
151
+ unless response.status == 200
152
+ $logger.debug "grant response error: #{response.body}"
153
+ raise InvalidCredentialsException
154
+ end
155
+
156
+ begin
157
+ grant = JSON.parse(response.body)
158
+ return grant['accessToken']
159
+ rescue JSON::ParserError => e
160
+ $logger.error "Error parsing JSON: #{e.to_s}"
161
+ raise e
162
+ end
163
+ end
164
+
165
+ # Generate the URL for a specific request.
166
+ # This factors in several configuration options including:
167
+ # - +tenant+
168
+ # - +tld+
169
+ #
170
+ # @param resource [String] The specific API resource to request
171
+ # @param path [String] The path to the resource
172
+ #
173
+ # @return [String] The generated URL for the request
174
+ def urlFor(resource, path=nil)
175
+
176
+ if path != nil
177
+ path = "/#{path.delete_prefix("/")}"
178
+ end
179
+
180
+ sprintf(DEFAULT_URL_TEMPLATE, @configuration['tenant'], @configuration['tld'] || DEFAULT_TLD, resource, path)
181
+ end
182
+
183
+ end
data/lib/vault.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Vault
2
+ require 'vault/vault'
3
+
4
+ require 'vault/client'
5
+ require 'vault/secret'
6
+ require 'vault/role'
7
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dsv-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre.SNAPSHOT
5
+ platform: ruby
6
+ authors:
7
+ - John Poulin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-04-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.7'
27
+ description: The Thycotic DevOps Secrets Vault SDK for Ruby
28
+ email: john.m.poulin@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/vault.rb
34
+ - lib/vault/client.rb
35
+ - lib/vault/role.rb
36
+ - lib/vault/secret.rb
37
+ - lib/vault/vault.rb
38
+ homepage: https://rubygems.org/gems/hola
39
+ licenses:
40
+ - Apache-2.0
41
+ metadata:
42
+ github_repo: ssh://github.com/forced-request/dsv-ruby-sdk
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">"
55
+ - !ruby/object:Gem::Version
56
+ version: 1.3.1
57
+ requirements: []
58
+ rubygems_version: 3.0.3
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: dsv-sdk
62
+ test_files: []