cred_hubble 0.0.1.pre → 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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'