cred_hubble 0.0.1.pre → 0.1.0.pre

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +7 -1
  4. data/.travis.yml +3 -1
  5. data/README.md +353 -13
  6. data/cred_hubble.gemspec +3 -0
  7. data/lib/cred_hubble.rb +3 -2
  8. data/lib/cred_hubble/client.rb +119 -13
  9. data/lib/cred_hubble/http/client.rb +39 -4
  10. data/lib/cred_hubble/resources/certificate_credential.rb +25 -0
  11. data/lib/cred_hubble/resources/credential.rb +32 -0
  12. data/lib/cred_hubble/resources/credential_collection.rb +21 -0
  13. data/lib/cred_hubble/resources/credential_factory.rb +41 -0
  14. data/lib/cred_hubble/resources/immutable_resource.rb +2 -2
  15. data/lib/cred_hubble/resources/json_credential.rb +13 -0
  16. data/lib/cred_hubble/resources/password_credential.rb +13 -0
  17. data/lib/cred_hubble/resources/permission.rb +10 -0
  18. data/lib/cred_hubble/resources/permission_collection.rb +21 -0
  19. data/lib/cred_hubble/resources/resource.rb +10 -0
  20. data/lib/cred_hubble/resources/resources.rb +15 -0
  21. data/lib/cred_hubble/resources/{base_resource.rb → rest_resource.rb} +6 -2
  22. data/lib/cred_hubble/resources/rsa_credential.rb +24 -0
  23. data/lib/cred_hubble/resources/ssh_credential.rb +39 -0
  24. data/lib/cred_hubble/resources/user_credential.rb +39 -0
  25. data/lib/cred_hubble/resources/value_credential.rb +13 -0
  26. data/lib/cred_hubble/version.rb +1 -1
  27. data/spec/cred_hubble/client_spec.rb +487 -3
  28. data/spec/cred_hubble/http/client_spec.rb +347 -53
  29. data/spec/cred_hubble/resources/certificate_credential_spec.rb +49 -0
  30. data/spec/cred_hubble/resources/credential_collection_spec.rb +59 -0
  31. data/spec/cred_hubble/resources/credential_factory_spec.rb +154 -0
  32. data/spec/cred_hubble/resources/credential_spec.rb +10 -0
  33. data/spec/cred_hubble/resources/json_credential_spec.rb +52 -0
  34. data/spec/cred_hubble/resources/password_credential_spec.rb +41 -0
  35. data/spec/cred_hubble/resources/permission_collection_spec.rb +87 -0
  36. data/spec/cred_hubble/resources/permission_spec.rb +36 -0
  37. data/spec/cred_hubble/resources/rsa_credential_spec.rb +46 -0
  38. data/spec/cred_hubble/resources/ssh_credential_spec.rb +73 -0
  39. data/spec/cred_hubble/resources/user_credential_spec.rb +72 -0
  40. data/spec/cred_hubble/resources/value_credential_spec.rb +42 -0
  41. data/spec/support/shared_examples/resource_examples.rb +49 -0
  42. metadata +57 -5
data/lib/cred_hubble.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'cred_hubble/version'
2
2
  require 'cred_hubble/exceptions/error'
3
- require 'cred_hubble/resources/info'
4
- require 'cred_hubble/resources/health'
3
+ require 'cred_hubble/resources/resources'
4
+ require 'cred_hubble/resources/credential_factory'
5
+
5
6
  require 'cred_hubble/client'
@@ -1,12 +1,39 @@
1
- require 'cred_hubble/resources/info'
2
- require 'cred_hubble/resources/health'
1
+ require 'addressable'
2
+ require 'cred_hubble/resources/resources'
3
3
  require 'cred_hubble/http/client'
4
+ require 'openssl'
4
5
 
6
+ # rubocop:disable ClassLength
5
7
  module CredHubble
6
8
  class Client
7
- def initialize(credhub_url)
8
- @credhub_url = credhub_url
9
- @verify_ssl = true
9
+ def initialize(host:, port: 8844, auth_header_token: nil, ca_path: nil,
10
+ client_cert_path: nil, client_key_path: nil)
11
+
12
+ @host = host
13
+ @port = port
14
+ @auth_header_token = auth_header_token
15
+ @ca_path = ca_path
16
+ @client_cert_path = client_cert_path
17
+ @client_key_path = client_key_path
18
+ end
19
+
20
+ def self.new_from_token_auth(host:, port: 8844, auth_header_token:, ca_path: nil)
21
+ new(
22
+ auth_header_token: auth_header_token,
23
+ ca_path: ca_path,
24
+ host: host,
25
+ port: port
26
+ )
27
+ end
28
+
29
+ def self.new_from_mtls_auth(host:, port: 8844, client_cert_path:, client_key_path:, ca_path: nil)
30
+ new(
31
+ client_cert_path: client_cert_path,
32
+ client_key_path: client_key_path,
33
+ host: host,
34
+ ca_path: ca_path,
35
+ port: port
36
+ )
10
37
  end
11
38
 
12
39
  def info
@@ -19,20 +46,99 @@ module CredHubble
19
46
  CredHubble::Resources::Health.from_json(response)
20
47
  end
21
48
 
49
+ def credential_by_id(credential_id)
50
+ response = http_client.get("/api/v1/data/#{credential_id}").body
51
+ CredHubble::Resources::CredentialFactory.from_json(response)
52
+ end
53
+
54
+ def credentials_by_name(name, current: nil, versions: nil)
55
+ template = Addressable::Template.new('/api/v1/data{?query*}')
56
+
57
+ query_args = { name: name, current: current, versions: versions }.reject { |_, v| v.nil? }
58
+ path = template.expand(query: query_args).to_s
59
+
60
+ response = http_client.get(path).body
61
+ CredHubble::Resources::CredentialCollection.from_json(response)
62
+ end
63
+
64
+ def permissions_by_credential_name(credential_name)
65
+ template = Addressable::Template.new('/api/v1/permissions{?query*}')
66
+
67
+ query_args = { credential_name: credential_name }
68
+ path = template.expand(query: query_args).to_s
69
+
70
+ response = http_client.get(path).body
71
+ CredHubble::Resources::PermissionCollection.from_json(response)
72
+ end
73
+
74
+ def put_credential(credential, overwrite: nil, additional_permissions: [])
75
+ credential_body = credential.attributes_for_put
76
+ credential_body[:overwrite] = !!overwrite unless overwrite.nil?
77
+
78
+ unless additional_permissions.empty?
79
+ credential_body[:additional_permissions] = additional_permissions.map(&:attributes)
80
+ end
81
+
82
+ response = http_client.put('/api/v1/data', credential_body.to_json).body
83
+ CredHubble::Resources::CredentialFactory.from_json(response)
84
+ end
85
+
86
+ def interpolate_credentials(vcap_services_json)
87
+ http_client.post('/api/v1/interpolate', vcap_services_json).body
88
+ end
89
+
90
+ def delete_credential_by_name(name)
91
+ template = Addressable::Template.new('/api/v1/data{?query*}')
92
+
93
+ query_args = { name: name }
94
+ path = template.expand(query: query_args).to_s
95
+
96
+ http_client.delete(path).success?
97
+ end
98
+
99
+ def add_permissions(permission_collection)
100
+ response = http_client.post('/api/v1/permissions', permission_collection.to_json).body
101
+ CredHubble::Resources::PermissionCollection.from_json(response)
102
+ end
103
+
104
+ def delete_permissions(credential_name, actor)
105
+ template = Addressable::Template.new('/api/v1/permissions{?query*}')
106
+
107
+ query_args = { credential_name: credential_name, actor: actor }
108
+ path = template.expand(query: query_args).to_s
109
+
110
+ http_client.delete(path).success?
111
+ end
112
+
22
113
  private
23
114
 
24
- attr_reader :credhub_url, :verify_ssl
115
+ attr_reader :auth_header_token, :client_cert_path, :client_key_path, :ca_path, :host, :port
25
116
 
26
117
  def http_client
27
- CredHubble::Http::Client.new(credhub_url, verify_ssl: verify_ssl)
118
+ CredHubble::Http::Client.new(
119
+ credhub_url,
120
+ auth_header_token: auth_header_token,
121
+ ca_path: ca_path,
122
+ client_cert: client_cert,
123
+ client_key: client_key
124
+ )
125
+ end
126
+
127
+ def client_cert
128
+ return unless client_cert_path
129
+
130
+ OpenSSL::X509::Certificate.new(File.read(client_cert_path))
131
+ end
132
+
133
+ def client_key
134
+ return unless client_key_path
135
+
136
+ OpenSSL::PKey::RSA.new(File.read(client_key_path))
28
137
  end
29
138
 
30
- # TODO: Remove ability to disable ssl verification
31
- # Only leaving this in to simplify initial development
32
- # Will be removed before the 0.0.1 release since non-SSL + CredHub is not a good combo
33
- def unsafe_mode!
34
- @verify_ssl = false
35
- puts 'WARNING: SSL verification disabled!'
139
+ def credhub_url
140
+ Addressable::URI.new(scheme: 'https', host: host, port: port).to_s
36
141
  end
37
142
  end
38
143
  end
144
+ # rubocop:enable ClassLength
@@ -6,9 +6,12 @@ module CredHubble
6
6
  class Client
7
7
  DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze
8
8
 
9
- def initialize(url, verify_ssl: true)
9
+ def initialize(url, auth_header_token: nil, ca_path: nil, client_cert: nil, client_key: nil)
10
10
  @url = url
11
- @verify_ssl = verify_ssl
11
+ @auth_header_token = auth_header_token
12
+ @ca_path = ca_path
13
+ @client_cert = client_cert
14
+ @client_key = client_key
12
15
  end
13
16
 
14
17
  def get(path)
@@ -17,17 +20,49 @@ module CredHubble
17
20
  end
18
21
  end
19
22
 
23
+ def post(path, body)
24
+ with_error_handling do
25
+ connection.post(path, body)
26
+ end
27
+ end
28
+
29
+ def put(path, body)
30
+ with_error_handling do
31
+ connection.put(path, body)
32
+ end
33
+ end
34
+
35
+ def delete(path)
36
+ with_error_handling do
37
+ connection.delete(path)
38
+ end
39
+ end
40
+
20
41
  private
21
42
 
22
- attr_reader :url, :verify_ssl
43
+ attr_reader :auth_header_token, :client_cert, :client_key, :ca_path, :url
23
44
 
24
45
  def connection
25
- Faraday.new(url: url, headers: DEFAULT_HEADERS, ssl: { verify: verify_ssl }) do |faraday|
46
+ Faraday.new(url: url, headers: request_headers, ssl: ssl_config) do |faraday|
26
47
  faraday.request :url_encoded
27
48
  faraday.adapter Faraday.default_adapter
28
49
  end
29
50
  end
30
51
 
52
+ def request_headers
53
+ headers = DEFAULT_HEADERS
54
+ return headers unless auth_header_token
55
+
56
+ headers.merge('Authorization' => "bearer #{auth_header_token}")
57
+ end
58
+
59
+ def ssl_config
60
+ additional_config = { ca_file: ca_path, client_cert: client_cert, client_key: client_key }
61
+ additional_config.reject! { |_, v| v.nil? }
62
+
63
+ { verify: true }.merge(additional_config)
64
+ end
65
+
31
66
  def with_error_handling(&_block)
32
67
  response = yield
33
68
 
@@ -0,0 +1,25 @@
1
+ require 'cred_hubble/resources/credential'
2
+
3
+ module CredHubble
4
+ module Resources
5
+ class CertificateValue
6
+ include Virtus.model
7
+
8
+ attribute :ca, String
9
+ attribute :certificate, String
10
+ attribute :private_key, String
11
+
12
+ def to_json(options = {})
13
+ attributes.to_json(options)
14
+ end
15
+ end
16
+
17
+ class CertificateCredential < Credential
18
+ attribute :value, CertificateValue
19
+
20
+ def type
21
+ Credential::CERTIFICATE_TYPE
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ require 'cred_hubble/resources/resource'
2
+
3
+ module CredHubble
4
+ module Resources
5
+ class Credential < Resource
6
+ TYPES = [
7
+ VALUE_TYPE = 'value'.freeze,
8
+ JSON_TYPE = 'json'.freeze,
9
+ PASSWORD_TYPE = 'password'.freeze,
10
+ USER_TYPE = 'user'.freeze,
11
+ CERTIFICATE_TYPE = 'certificate'.freeze,
12
+ RSA_TYPE = 'rsa'.freeze,
13
+ SSH_TYPE = 'ssh'.freeze
14
+ ].freeze
15
+
16
+ attribute :id, String
17
+ attribute :name, String
18
+ attribute :type, String
19
+ attribute :version_created_at, String
20
+
21
+ def attributes_for_put
22
+ attributes.delete_if { |k, _| immutable_attributes.include?(k) }
23
+ end
24
+
25
+ private
26
+
27
+ def immutable_attributes
28
+ %i[id version_created_at]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'cred_hubble/resources/rest_resource'
2
+ require 'cred_hubble/resources/credential_factory'
3
+
4
+ module CredHubble
5
+ module Resources
6
+ class CredentialCollection < Resource
7
+ include Enumerable
8
+
9
+ def initialize(value_hash)
10
+ credentials_array = value_hash['data']
11
+ @data = credentials_array.map { |credential_data| CredentialFactory.credential_from_data(credential_data) }
12
+ end
13
+
14
+ def each(&block)
15
+ data.each(&block)
16
+ end
17
+
18
+ attr_reader :data
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'cred_hubble/resources/rest_resource'
2
+ require 'cred_hubble/resources/credential'
3
+ require 'cred_hubble/resources/value_credential'
4
+ require 'cred_hubble/resources/json_credential'
5
+ require 'cred_hubble/resources/password_credential'
6
+ require 'cred_hubble/resources/user_credential'
7
+ require 'cred_hubble/resources/certificate_credential'
8
+ require 'cred_hubble/resources/rsa_credential'
9
+ require 'cred_hubble/resources/ssh_credential'
10
+
11
+ module CredHubble
12
+ module Resources
13
+ class CredentialFactory < RestResource
14
+ def self.from_json(raw_json)
15
+ parsed_json = parse_json(raw_json)
16
+ credential_from_data(parsed_json)
17
+ end
18
+
19
+ def self.credential_from_data(credential_data)
20
+ case credential_data['type']
21
+ when Credential::VALUE_TYPE
22
+ ValueCredential.new(credential_data)
23
+ when Credential::JSON_TYPE
24
+ JsonCredential.new(credential_data)
25
+ when Credential::PASSWORD_TYPE
26
+ PasswordCredential.new(credential_data)
27
+ when Credential::USER_TYPE
28
+ UserCredential.new(credential_data)
29
+ when Credential::CERTIFICATE_TYPE
30
+ CertificateCredential.new(credential_data)
31
+ when Credential::RSA_TYPE
32
+ RsaCredential.new(credential_data)
33
+ when Credential::SSH_TYPE
34
+ SshCredential.new(credential_data)
35
+ else
36
+ Credential.new(credential_data)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,9 +1,9 @@
1
- require 'cred_hubble/resources/base_resource'
1
+ require 'cred_hubble/resources/rest_resource'
2
2
  require 'virtus'
3
3
 
4
4
  module CredHubble
5
5
  module Resources
6
- class ImmutableResource < BaseResource
6
+ class ImmutableResource < RestResource
7
7
  include ::Virtus.value_object
8
8
  end
9
9
  end
@@ -0,0 +1,13 @@
1
+ require 'cred_hubble/resources/credential'
2
+
3
+ module CredHubble
4
+ module Resources
5
+ class JsonCredential < Credential
6
+ attribute :value, Hash
7
+
8
+ def type
9
+ Credential::JSON_TYPE
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'cred_hubble/resources/credential'
2
+
3
+ module CredHubble
4
+ module Resources
5
+ class PasswordCredential < Credential
6
+ attribute :value, String
7
+
8
+ def type
9
+ Credential::PASSWORD_TYPE
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'cred_hubble/resources/rest_resource'
2
+
3
+ module CredHubble
4
+ module Resources
5
+ class Permission < Resource
6
+ attribute :actor, String
7
+ attribute :operations, Array[String]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ require 'cred_hubble/resources/rest_resource'
2
+ require 'cred_hubble/resources/permission'
3
+
4
+ module CredHubble
5
+ module Resources
6
+ class PermissionCollection < Resource
7
+ include Enumerable
8
+
9
+ attribute :credential_name, String
10
+ attribute :permissions, Array[Permission]
11
+
12
+ def each(&block)
13
+ permissions.each(&block)
14
+ end
15
+
16
+ def empty?
17
+ permissions.empty?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'cred_hubble/resources/rest_resource'
2
+ require 'virtus'
3
+
4
+ module CredHubble
5
+ module Resources
6
+ class Resource < RestResource
7
+ include ::Virtus.model
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ require 'cred_hubble/resources/info'
2
+ require 'cred_hubble/resources/health'
3
+
4
+ require 'cred_hubble/resources/credential'
5
+ require 'cred_hubble/resources/credential_collection'
6
+ require 'cred_hubble/resources/value_credential'
7
+ require 'cred_hubble/resources/json_credential'
8
+ require 'cred_hubble/resources/password_credential'
9
+ require 'cred_hubble/resources/user_credential'
10
+ require 'cred_hubble/resources/certificate_credential'
11
+ require 'cred_hubble/resources/rsa_credential'
12
+ require 'cred_hubble/resources/ssh_credential'
13
+
14
+ require 'cred_hubble/resources/permission'
15
+ require 'cred_hubble/resources/permission_collection'