conjur-api 5.0.0 → 5.1.0

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