conjur-api 5.0.0 → 5.1.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/Dockerfile +2 -0
  5. data/Jenkinsfile +2 -8
  6. data/README.md +85 -2
  7. data/Rakefile +9 -3
  8. data/ci/configure_v4.sh +12 -0
  9. data/ci/configure_v5.sh +14 -0
  10. data/conjur-api.gemspec +1 -1
  11. data/docker-compose.yml +47 -12
  12. data/example/demo_v4.rb +49 -0
  13. data/example/demo_v5.rb +57 -0
  14. data/features/authn_local.feature +32 -0
  15. data/features/support/env.rb +1 -0
  16. data/features/variable_value.feature +6 -13
  17. data/features_v4/authn_local.feature +27 -0
  18. data/features_v4/exists.feature +29 -0
  19. data/features_v4/host.feature +18 -0
  20. data/features_v4/host_factory_token.feature +49 -0
  21. data/features_v4/members.feature +39 -0
  22. data/features_v4/permitted.feature +15 -0
  23. data/features_v4/permitted_roles.feature +8 -0
  24. data/features_v4/resource_fields.feature +47 -0
  25. data/features_v4/rotate_api_key.feature +13 -0
  26. data/features_v4/step_definitions/api_steps.rb +17 -0
  27. data/features_v4/step_definitions/result_steps.rb +3 -0
  28. data/features_v4/support/env.rb +23 -0
  29. data/features_v4/support/policy.yml +34 -0
  30. data/features_v4/support/world.rb +12 -0
  31. data/features_v4/variable_fields.feature +11 -0
  32. data/features_v4/variable_value.feature +54 -0
  33. data/lib/conjur-api/version.rb +1 -1
  34. data/lib/conjur/acts_as_resource.rb +3 -17
  35. data/lib/conjur/acts_as_role.rb +2 -4
  36. data/lib/conjur/acts_as_user.rb +1 -2
  37. data/lib/conjur/api.rb +1 -0
  38. data/lib/conjur/api/authn.rb +22 -8
  39. data/lib/conjur/api/host_factories.rb +2 -5
  40. data/lib/conjur/api/policies.rb +1 -1
  41. data/lib/conjur/api/pubkeys.rb +1 -9
  42. data/lib/conjur/api/resources.rb +1 -6
  43. data/lib/conjur/api/router/v4.rb +149 -0
  44. data/lib/conjur/api/router/v5.rb +150 -0
  45. data/lib/conjur/api/variables.rb +2 -8
  46. data/lib/conjur/base.rb +61 -18
  47. data/lib/conjur/base_object.rb +1 -6
  48. data/lib/conjur/configuration.rb +26 -0
  49. data/lib/conjur/group.rb +7 -1
  50. data/lib/conjur/has_attributes.rb +11 -3
  51. data/lib/conjur/host_factory.rb +1 -1
  52. data/lib/conjur/routing.rb +29 -0
  53. data/lib/conjur/user.rb +7 -1
  54. data/lib/conjur/variable.rb +26 -11
  55. data/spec/has_attributes_spec.rb +4 -2
  56. data/test.sh +25 -11
  57. metadata +33 -12
  58. data/ci/wait_for_server.sh +0 -10
  59. data/dev/docker-compose.yml +0 -23
  60. data/dev/empty.yml +0 -2
  61. data/dev/start.sh +0 -15
  62. data/dev/stop.sh +0 -6
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "5.0.0"
22
+ VERSION = "5.1.0"
23
23
  end
24
24
  end
@@ -61,7 +61,7 @@ module Conjur
61
61
  # @return [Boolean] does it exist?
62
62
  def exists?
63
63
  begin
64
- rbac_resource_resource.head
64
+ url_for(:resources_resource, credentials, id).head
65
65
  true
66
66
  rescue RestClient::Forbidden
67
67
  true
@@ -88,10 +88,7 @@ module Conjur
88
88
  # @param privilege [String] the privilege
89
89
  # @return [Array<String>] the ids of roles that have `privilege` on this resource.
90
90
  def permitted_roles privilege
91
- options = {}
92
- options[:permitted_roles] = true
93
- options[:privilege] = privilege
94
- result = JSON.parse rbac_resource_resource[options_querystring options].get
91
+ result = JSON.parse url_for(:resources_permitted_roles, credentials, id, privilege).get
95
92
  if result.is_a?(Hash) && ( count = result['count'] )
96
93
  count
97
94
  else
@@ -115,23 +112,12 @@ module Conjur
115
112
  # instead of checking +api.current_role+.
116
113
  # @return [Boolean]
117
114
  def permitted? privilege, role: nil
118
- options = {}
119
- options[:check] = true
120
- options[:privilege] = privilege
121
- options[:role] = cast_to_id(role) if role
122
- rbac_resource_resource[options_querystring options].get
115
+ url_for(:resources_check, credentials, id, privilege, role)
123
116
  true
124
117
  rescue RestClient::Forbidden
125
118
  false
126
119
  rescue RestClient::ResourceNotFound
127
120
  false
128
121
  end
129
-
130
- private
131
-
132
- # RestClient::Resource for RBAC resource operations.
133
- def rbac_resource_resource
134
- RestClient::Resource.new(Conjur.configuration.core_url, credentials)['resources'][id.to_url_path]
135
- end
136
122
  end
137
123
  end
@@ -132,9 +132,7 @@ module Conjur
132
132
  if result.is_a?(Hash) && ( count = result['count'] )
133
133
  count
134
134
  else
135
- result['members'].collect do |json|
136
- RoleGrant.parse_from_json(json, credentials)
137
- end
135
+ parser_for(:members, credentials, result)
138
136
  end
139
137
  end
140
138
 
@@ -142,7 +140,7 @@ module Conjur
142
140
 
143
141
  # RestClient::Resource for RBAC role operations.
144
142
  def rbac_role_resource
145
- RestClient::Resource.new(Conjur.configuration.core_url, credentials)['roles'][id.to_url_path]
143
+ url_for(:roles_role, credentials, id)
146
144
  end
147
145
  end
148
146
  end
@@ -58,8 +58,7 @@ module Conjur
58
58
  #
59
59
  # @return [String] the new API key for this user.
60
60
  def rotate_api_key
61
- path = "authn/#{path_escape account}/api_key?role=#{id}"
62
- core_resource[path].put('').body
61
+ url_for(:authn_rotate_api_key, credentials, account, id).put("").body
63
62
  end
64
63
  end
65
64
  end
@@ -22,6 +22,7 @@ require 'active_support'
22
22
  require 'active_support/deprecation'
23
23
 
24
24
  require 'conjur/configuration'
25
+ require 'conjur/routing'
25
26
  require 'conjur/id'
26
27
  require 'conjur/base'
27
28
  require 'conjur/exceptions'
@@ -47,7 +47,7 @@ module Conjur
47
47
  if Conjur.log
48
48
  Conjur.log << "Logging in #{username} to account #{account} via Basic authentication\n"
49
49
  end
50
- RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)[fully_escape account]['login'].get
50
+ url_for(:authn_login, account, username, password).get
51
51
  end
52
52
 
53
53
  # Exchanges Conjur the API key (refresh token) for an access token. The access token can
@@ -62,7 +62,25 @@ module Conjur
62
62
  if Conjur.log
63
63
  Conjur.log << "Authenticating #{username} to account #{account}\n"
64
64
  end
65
- JSON::parse(RestClient::Resource.new(Conjur.configuration.authn_url)[fully_escape account][fully_escape username]['authenticate'].post api_key, content_type: 'text/plain')
65
+ JSON.parse url_for(:authn_authenticate, account, username).post(api_key, content_type: 'text/plain')
66
+ end
67
+
68
+ # Obtains an access token from the +authn_local+ service. The access token can
69
+ # then be used to authenticate further API calls.
70
+ #
71
+ # @param [String] username The username or host id for which we want a token
72
+ # @param [String] account The organization account.
73
+ # @return [String] A JSON formatted authentication token.
74
+ def authenticate_local username, account: Conjur.configuration.account, expiration: nil, cidr: nil
75
+ account ||= Conjur.configuration.account
76
+ if Conjur.log
77
+ Conjur.log << "Authenticating #{username} to account #{account} using authn_local\n"
78
+ end
79
+
80
+ require 'json'
81
+ require 'socket'
82
+ message = url_for(:authn_authenticate_local, username, account, expiration, cidr)
83
+ JSON.parse(UNIXSocket.open(Conjur.configuration.authn_local_socket) {|s| s.puts message; s.gets })
66
84
  end
67
85
 
68
86
  # Change a user's password. To do this, you must have the user's current password. This does not change or rotate
@@ -78,7 +96,7 @@ module Conjur
78
96
  if Conjur.log
79
97
  Conjur.log << "Updating password for #{username} in account #{account}\n"
80
98
  end
81
- RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)[fully_escape account]['password'].put new_password
99
+ url_for(:authn_update_password, account, username, password).put new_password
82
100
  end
83
101
 
84
102
  #@!endgroup
@@ -98,11 +116,7 @@ module Conjur
98
116
  Conjur.log << "Rotating API key for self (#{username} in account #{account})\n"
99
117
  end
100
118
 
101
- RestClient::Resource.new(
102
- Conjur.configuration.authn_url,
103
- user: username,
104
- password: password
105
- )[fully_escape account]['api_key'].put('').body
119
+ url_for(:authn_rotate_own_api_key, account, username, password).put('').body
106
120
  end
107
121
 
108
122
  #@!endgroup
@@ -40,10 +40,7 @@ module Conjur
40
40
  # @return [Host]
41
41
  def host_factory_create_host token, id, options = {}
42
42
  token = token.token if token.is_a?(HostFactoryToken)
43
- http_options = {
44
- headers: { authorization: %Q(Token token="#{token}") }
45
- }
46
- response = RestClient::Resource.new(Conjur.configuration.core_url, http_options)["host_factories"]["hosts"].post(options.merge(id: id)).body
43
+ response = url_for(:host_factory_create_host, token).post(options.merge(id: id)).body
47
44
  attributes = JSON.parse(response)
48
45
  Host.new(attributes['id'], {}).tap do |host|
49
46
  host.attributes = attributes
@@ -56,7 +53,7 @@ module Conjur
56
53
  # @param [Hash] credentials authentication credentials of the current user.
57
54
  # @param [String] token the host factory token.
58
55
  def revoke_host_factory_token credentials, token
59
- RestClient::Resource.new(Conjur.configuration.core_url, credentials)['host_factory_tokens'][token].delete
56
+ url_for(:host_factory_revoke_token, credentials, token).delete
60
57
  end
61
58
  end
62
59
 
@@ -47,7 +47,7 @@ module Conjur
47
47
  # @param account [String] Conjur organization account
48
48
  # @param method [Symbol] Policy load method to use: {POLICY_METHOD_POST} (default), {POLICY_METHOD_PATCH}, or {POLICY_METHOD_PUT}.
49
49
  def load_policy id, policy, account: Conjur.configuration.account, method: POLICY_METHOD_POST
50
- request = RestClient::Resource.new(Conjur.configuration.core_url, credentials)['policies'][path_escape account]['policy'][path_escape id]
50
+ request = url_for(:policies_load_policy, credentials, account, id)
51
51
  PolicyLoadResult.new JSON.parse(request.send(method, policy))
52
52
  end
53
53
 
@@ -44,18 +44,10 @@ module Conjur
44
44
  # @param [String] username the *unqualified* Conjur username
45
45
  # @return [String] newline delimited public keys
46
46
  def public_keys username, account: Conjur.configuration.account
47
- public_keys_resource(username, account).get
47
+ url_for(:public_keys_for_user, account, username).get
48
48
  end
49
49
 
50
50
  #@!endgroup
51
-
52
- protected
53
-
54
- # @api private
55
- # Returns a RestClient::Resource with the pubkeys host and the given path.
56
- def public_keys_resource username, account
57
- RestClient::Resource.new(Conjur.configuration.core_url)['public_keys'][fully_escape account]['user'][path_escape username]
58
- end
59
51
  end
60
52
  end
61
53
  end
@@ -95,12 +95,7 @@ module Conjur
95
95
  options.delete(name.to_sym)
96
96
  end
97
97
 
98
- credentials ||= {}
99
-
100
- path = "/resources/#{path_escape account}"
101
- path += "/#{path_escape kind}" if kind
102
-
103
- result = JSON.parse(RestClient::Resource.new(Conjur.configuration.core_url, credentials)[path][options_querystring options].get)
98
+ result = JSON.parse(url_for(:resources, credentials, account, kind, options).get)
104
99
 
105
100
  result = result['count'] if result.is_a?(Hash)
106
101
 
@@ -0,0 +1,149 @@
1
+ module Conjur
2
+ class API
3
+ module Router
4
+ module V4
5
+ extend Conjur::Escape::ClassMethods
6
+ extend Conjur::QueryString
7
+ extend self
8
+
9
+ def authn_login account, username, password
10
+ verify_account(account)
11
+ RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)['users/login']
12
+ end
13
+
14
+ def authn_authenticate account, username
15
+ verify_account(account)
16
+ RestClient::Resource.new(Conjur.configuration.authn_url)['users'][fully_escape username]['authenticate']
17
+ end
18
+
19
+ # For v4, the authn-local message is the username.
20
+ def authn_authenticate_local username, account, expiration, cidr, &block
21
+ verify_account(account)
22
+
23
+ raise "'expiration' is not supported for authn-local v4" if expiration
24
+ raise "'cidr' is not supported for authn-local v4" if cidr
25
+
26
+ username
27
+ end
28
+
29
+ def authn_rotate_api_key credentials, account, id
30
+ verify_account(account)
31
+ username = if id.kind == "user"
32
+ id.identifier
33
+ else
34
+ [ id.kind, id.identifier ].join('/')
35
+ end
36
+ RestClient::Resource.new(Conjur.configuration.authn_url, credentials)['users']["api_key?id=#{username}"]
37
+ end
38
+
39
+ def authn_rotate_own_api_key account, username, password
40
+ verify_account(account)
41
+ RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)['users']["api_key"]
42
+ end
43
+
44
+ def host_factory_create_host token
45
+ http_options = {
46
+ headers: { authorization: %Q(Token token="#{token}") }
47
+ }
48
+ RestClient::Resource.new(Conjur.configuration.core_url, http_options)['host_factories']['hosts']
49
+ end
50
+
51
+ def host_factory_create_tokens credentials, id
52
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['host_factories'][id.identifier]['tokens']
53
+ end
54
+
55
+ def host_factory_revoke_token credentials, token
56
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['host_factories']['tokens'][token]
57
+ end
58
+
59
+ def resources_resource credentials, id
60
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['authz'][id.account]['resources'][id.kind][id.identifier]
61
+ end
62
+
63
+ def resources_check credentials, id, privilege, role
64
+ options = {}
65
+ options[:check] = true
66
+ options[:privilege] = privilege
67
+ if role
68
+ options[:resource_id] = id
69
+ roles_role(credentials, Id.new(role))[options_querystring options].get
70
+ else
71
+ resources_resource(credentials, id)[options_querystring options].get
72
+ end
73
+ end
74
+
75
+ def resources_permitted_roles credentials, id, privilege
76
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['authz'][id.account]['roles']['allowed_to'][privilege][id.kind][id.identifier]
77
+ end
78
+
79
+ def roles_role credentials, id
80
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['authz'][id.account]['roles'][id.kind][id.identifier]
81
+ end
82
+
83
+ def secrets_add credentials, id
84
+ verify_account(id.account)
85
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['variables'][fully_escape id.identifier]['values']
86
+ end
87
+
88
+ def variable credentials, id
89
+ verify_account(id.account)
90
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['variables'][fully_escape id.identifier]
91
+ end
92
+
93
+ def secrets_value credentials, id, options
94
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['variables'][fully_escape id.identifier]['value'][options_querystring options]
95
+ end
96
+
97
+ def secrets_values credentials, variable_ids
98
+ options = {
99
+ vars: Array(variable_ids).map { |v| fully_escape(v.identifier) }.join(',')
100
+ }
101
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['variables']['values'][options_querystring options]
102
+ end
103
+
104
+ def group_attributes credentials, resource, id
105
+ verify_account(id.account)
106
+ JSON.parse(RestClient::Resource.new(Conjur.configuration.core_url, credentials)['groups'][fully_escape id.identifier].get)
107
+ end
108
+
109
+ def variable_attributes credentials, resource, id
110
+ verify_account(id.account)
111
+ JSON.parse(RestClient::Resource.new(Conjur.configuration.core_url, credentials)['variables'][fully_escape id.identifier].get)
112
+ end
113
+
114
+ def user_attributes credentials, resource, id
115
+ verify_account(id.account)
116
+ JSON.parse(RestClient::Resource.new(Conjur.configuration.core_url, credentials)['users'][fully_escape id.identifier].get)
117
+ end
118
+
119
+ def parse_group_gidnumber attributes
120
+ attributes['gidnumber']
121
+ end
122
+
123
+ def parse_user_uidnumber attributes
124
+ attributes['uidnumber']
125
+ end
126
+
127
+ def parse_variable_kind attributes
128
+ attributes['kind']
129
+ end
130
+
131
+ def parse_variable_mime_type attributes
132
+ attributes['mime_type']
133
+ end
134
+
135
+ def parse_members credentials, result
136
+ result.collect do |json|
137
+ RoleGrant.parse_from_json(json, credentials)
138
+ end
139
+ end
140
+
141
+ protected
142
+
143
+ def verify_account account
144
+ raise "Expecting account to be #{Conjur.configuration.account.inspect}, got #{account.inspect}" unless Conjur.configuration.account == account
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,150 @@
1
+ module Conjur
2
+ class API
3
+ module Router
4
+ module V5
5
+ extend Conjur::Escape::ClassMethods
6
+ extend Conjur::QueryString
7
+ extend Conjur::Cast
8
+ extend self
9
+
10
+ def authn_login account, username, password
11
+ RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)[fully_escape account]['login']
12
+ end
13
+
14
+ def authn_authenticate account, username
15
+ RestClient::Resource.new(Conjur.configuration.authn_url)[fully_escape account][fully_escape username]['authenticate']
16
+ end
17
+
18
+ # For v5, the authn-local message is a JSON string with account, sub, and optional fields.
19
+ def authn_authenticate_local username, account, expiration, cidr, &block
20
+ { account: account, sub: username }.tap do |params|
21
+ params[:exp] = expiration if expiration
22
+ params[:cidr] = cidr if cidr
23
+ end.to_json
24
+ end
25
+
26
+ def authn_update_password account, username, password
27
+ RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)[fully_escape account]['password']
28
+ end
29
+
30
+ def authn_rotate_api_key credentials, account, id
31
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['authn'][path_escape account]["api_key?role=#{id}"]
32
+ end
33
+
34
+ def authn_rotate_own_api_key account, username, password
35
+ RestClient::Resource.new(Conjur.configuration.authn_url, user: username, password: password)[fully_escape account]['api_key']
36
+ end
37
+
38
+ def host_factory_create_host token
39
+ http_options = {
40
+ headers: { authorization: %Q(Token token="#{token}") }
41
+ }
42
+ RestClient::Resource.new(Conjur.configuration.core_url, http_options)["host_factories"]["hosts"]
43
+ end
44
+
45
+ def host_factory_create_tokens credentials, id
46
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['host_factory_tokens']
47
+ end
48
+
49
+ def host_factory_revoke_token credentials, token
50
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['host_factory_tokens'][token]
51
+ end
52
+
53
+ def policies_load_policy credentials, account, id
54
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['policies'][path_escape account]['policy'][path_escape id]
55
+ end
56
+
57
+ def public_keys_for_user account, username
58
+ RestClient::Resource.new(Conjur.configuration.core_url)['public_keys'][fully_escape account]['user'][path_escape username]
59
+ end
60
+
61
+ def resources credentials, account, kind, options
62
+ credentials ||= {}
63
+
64
+ path = "/resources/#{path_escape account}"
65
+ path += "/#{path_escape kind}" if kind
66
+
67
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)[path][options_querystring options]
68
+ end
69
+
70
+ def resources_resource credentials, id
71
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['resources'][id.to_url_path]
72
+ end
73
+
74
+ def resources_permitted_roles credentials, id, privilege
75
+ options = {}
76
+ options[:permitted_roles] = true
77
+ options[:privilege] = privilege
78
+ resources_resource(credentials, id)[options_querystring options]
79
+ end
80
+
81
+ def resources_check credentials, id, privilege, role
82
+ options = {}
83
+ options[:check] = true
84
+ options[:privilege] = privilege
85
+ options[:role] = cast_to_id(role) if role
86
+ resources_resource(credentials, id)[options_querystring options].get
87
+ end
88
+
89
+ def roles_role credentials, id
90
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['roles'][id.to_url_path]
91
+ end
92
+
93
+ def secrets_add credentials, id
94
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['secrets'][id.to_url_path]
95
+ end
96
+
97
+ def secrets_value credentials, id, options
98
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['secrets'][id.to_url_path][options_querystring options]
99
+ end
100
+
101
+ def secrets_values credentials, variable_ids
102
+ options = {
103
+ variable_ids: Array(variable_ids).join(',')
104
+ }
105
+ RestClient::Resource.new(Conjur.configuration.core_url, credentials)['secrets'][options_querystring(options).gsub("%2C", ',')]
106
+ end
107
+
108
+ def group_attributes credentials, resource, id
109
+ resource_annotations resource
110
+ end
111
+
112
+ def variable_attributes credentials, resource, id
113
+ resource_annotations resource
114
+ end
115
+
116
+ def user_attributes credentials, resource, id
117
+ resource_annotations resource
118
+ end
119
+
120
+ def parse_group_gidnumber attributes
121
+ HasAttributes.annotation_value attributes, 'conjur/gidnumber'
122
+ end
123
+
124
+ def parse_user_uidnumber attributes
125
+ HasAttributes.annotation_value attributes, 'conjur/uidnumber'
126
+ end
127
+
128
+ def parse_variable_kind attributes
129
+ HasAttributes.annotation_value attributes, 'conjur/kind'
130
+ end
131
+
132
+ def parse_variable_mime_type attributes
133
+ HasAttributes.annotation_value attributes, 'conjur/mime_type'
134
+ end
135
+
136
+ def parse_members credentials, result
137
+ result['members'].collect do |json|
138
+ RoleGrant.parse_from_json(json, credentials)
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def resource_annotations resource
145
+ resource.attributes['annotations'] || {}
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end