dsv-sdk 0.0.4 → 0.0.6.pre.dev

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