conjur-api 4.14.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +4 -0
  4. data/lib/conjur-api/version.rb +1 -1
  5. data/lib/conjur/acts_as_asset.rb +44 -3
  6. data/lib/conjur/acts_as_resource.rb +53 -4
  7. data/lib/conjur/acts_as_user.rb +17 -7
  8. data/lib/conjur/annotations.rb +49 -3
  9. data/lib/conjur/api.rb +30 -3
  10. data/lib/conjur/api/deputies.rb +25 -1
  11. data/lib/conjur/api/resources.rb +109 -5
  12. data/lib/conjur/api/roles.rb +103 -11
  13. data/lib/conjur/api/secrets.rb +16 -1
  14. data/lib/conjur/api/users.rb +65 -1
  15. data/lib/conjur/api/variables.rb +65 -1
  16. data/lib/conjur/audit-api.rb +3 -0
  17. data/lib/conjur/authn-api.rb +4 -0
  18. data/lib/conjur/authz-api.rb +4 -0
  19. data/lib/conjur/base.rb +31 -30
  20. data/lib/conjur/build_from_response.rb +11 -0
  21. data/lib/conjur/cast.rb +5 -1
  22. data/lib/conjur/core-api.rb +22 -2
  23. data/lib/conjur/deputy.rb +19 -2
  24. data/lib/conjur/env.rb +18 -3
  25. data/lib/conjur/escape.rb +65 -4
  26. data/lib/conjur/event_source.rb +15 -2
  27. data/lib/conjur/graph.rb +103 -12
  28. data/lib/conjur/has_id.rb +13 -1
  29. data/lib/conjur/has_identifier.rb +9 -6
  30. data/lib/conjur/has_owner.rb +21 -7
  31. data/lib/conjur/host.rb +8 -0
  32. data/lib/conjur/layer-api.rb +4 -0
  33. data/lib/conjur/layer.rb +50 -3
  34. data/lib/conjur/log.rb +22 -2
  35. data/lib/conjur/log_source.rb +27 -0
  36. data/lib/conjur/path_based.rb +47 -2
  37. data/lib/conjur/pubkeys-api.rb +12 -0
  38. data/lib/conjur/role.rb +220 -9
  39. data/lib/conjur/role_grant.rb +50 -2
  40. data/lib/conjur/secret.rb +9 -1
  41. data/lib/conjur/standard_methods.rb +31 -3
  42. data/lib/conjur/user.rb +55 -3
  43. data/spec/lib/role_spec.rb +1 -2
  44. metadata +2 -2
@@ -23,18 +23,20 @@ require 'conjur/graph'
23
23
 
24
24
  module Conjur
25
25
  class API
26
- ##
27
- # Fetch a digraph (or a forest of digraphs) representing of
28
- # role memberships related transitively to any of a list of roles.
29
- #
30
- # @param [Array<Conjur::Role, String>] roles the digraph (or forest thereof) of
31
- # the ancestors and descendants of these roles or role ids will be returned
32
- # @param [Hash] options options determining the graph returned
33
- # @option opts [Boolean] :ancestors Whether to return ancestors of the given roles (true by default)
34
- # @option opts [Boolean] :descendants Whether to return descendants of the given roles (true by default)
35
- # @option opts [Conjur::Role, String] :as_role Only roles visible to this role will be included in the graph
26
+ #@!group Authorization: Roles
27
+
28
+ # Fetch a {Conjur::Graph} representing the relationships of a given role or roles. Such graphs are transitive,
29
+ # and follow the normal permissions for role visibility.
30
+ #
31
+ # @param [Array<Conjur::Role, String>, String, Conjur::Role] roles role or or array of roles
32
+ # roles whose relationships we're interested in
33
+ # @param [Hash] options options for the request
34
+ # @option options [Boolean] :ancestors Whether to return ancestors of the given roles (true by default)
35
+ # @option options [Boolean] :descendants Whether to return descendants of the given roles (true by default)
36
+ # @option options [Conjur::Role, String] :as_role Only roles visible to this role will be included in the graph
36
37
  # @return [Conjur::Graph] An object representing the role memberships digraph
37
38
  def role_graph roles, options = {}
39
+ roles = [roles] unless roles.kind_of? Array
38
40
  roles.map!{|r| normalize_roleid(r) }
39
41
  options[:as_role] = normalize_roleid(options[:as_role]) if options.include?(:as_role)
40
42
  options.reverse_merge! as_role: normalize_roleid(current_role), descendants: true, ancestors: true
@@ -45,24 +47,108 @@ module Conjur
45
47
  Conjur::Graph.new RestClient::Resource.new(Conjur::Authz::API.host, credentials)["#{Conjur.account}/roles?#{query}"].get
46
48
  end
47
49
 
50
+ # Create a {Conjur::Role} with the given id.
51
+ #
52
+ # ### Permissions
53
+ # * All Conjur roles can create new roles.
54
+ # * The creator role (either the current role or the role given by the `:acting_as` option)
55
+ # is made a member of the new role. The new role is also made a member of itself.
56
+ # * If you give an `:acting_as` option, you must be a (transitive) member of the `:acting_as`
57
+ # role.
58
+ # * The new role is granted to the creator role with *admin option*: that is, the creator role
59
+ # is able to grant the created role to other roles.
60
+ #
61
+ # @example Basic role creation
62
+ # # Current role is 'user:jon', assume the organizational account is 'conjur'
63
+ # api.current_role # => 'conjur:user:jon'
64
+ #
65
+ # # Create a Conjur actor to control the permissions of a chron job (rebuild_indices)
66
+ # role = api.create_role 'robot:rebuild_indices'
67
+ # role.role_id # => "conjur:robot:rebuild_indices"
68
+ # role.members.map{ |grant| grant.member.role_id } # => ['conjur:user:jon', 'conjur:robot:rebuild_indices']
69
+ # api.role('user:jon').admin_of?(role) # => true
70
+ #
71
+ #
72
+ # @param [String] role a qualified role identifier for the new role
73
+ # @param [Hash] options options for the action
74
+ # @option options [String] :acting_as the resource will effectively be created by this role
75
+ # @return [Conjur::Role] the created role
76
+ # @raise [RestClient::MethodNotAllowed] if the role already exists. Note that this differs from
77
+ # the `RestClient::Conflict` exception raised when trying to create existing high level (user, group, etc.)
78
+ # Conjur assets.
48
79
  def create_role(role, options = {})
49
80
  role(role).tap do |r|
50
81
  r.create(options)
51
82
  end
52
83
  end
53
84
 
85
+ # Return a {Conjur::Role} representing a role with the given id. Note that the {Conjur::Role} may or
86
+ # may not exist (see {Conjur::Exists#exists?}).
87
+ #
88
+ # ### Permissions
89
+ # Because this method returns roles that may or may not exist, it doesn't require any permissions to call it:
90
+ # in fact, it does not perform an HTTP request (except for authentication if necessary).
91
+ #
92
+ # @example Create and show a role
93
+ # api.create_role 'cat:iggy'
94
+ # iggy = api.role 'cat:iggy'
95
+ # iggy.exists? # true
96
+ # iggy.members.map(&:member).map(&:roleid) # => ['conjur:user:admin']
97
+ # api.current_role.roleid # => 'conjur:user:admin' # creator role is a member of created role.
98
+ #
99
+ # @example No permissions are required to call this method
100
+ # api.current_role # => "user:no-access"
101
+ #
102
+ # # current role is only a member of itself, so it can't see other roles.
103
+ # api.current_role.memberships.count # => 1
104
+ # admin = api.role 'user:admin' # OK
105
+ # admin.exists? # => true
106
+ # admin.members # => RestClient::Forbidden: 403 Forbidden
107
+ #
108
+ # @param [String] role the id of the role, which must contain at least kind and id tokens (account is optional).
109
+ # @return [Conjur::Role] an object representing the role
54
110
  def role role
55
111
  Role.new(Conjur::Authz::API.host, credentials)[self.class.parse_role_id(role).join('/')]
56
112
  end
57
113
 
114
+ # Return a {Conjur::Role} object representing the role (typically a user or host) that this api is authenticated
115
+ # as. This is derived either from the `login` argument to {Conjur::API.new_from_key} or from the contents of the
116
+ # `token` given to {Conjur::API.new_from_token}.
117
+ #
118
+ # @example Current role for a user
119
+ # api = Conjur::API.new_from_key 'jon', 'somepassword'
120
+ # api.current_role.roleid # => 'conjur:user:jon'
121
+ #
122
+ # @example Current role for a host
123
+ # host = api.create_host id: 'exapmle-host'
124
+ #
125
+ # # Host and User have an `api` method that returns an api with their credentials. Note
126
+ # # that this only works with a newly created host or user, which has an `api_key` attribute.
127
+ # host.api.current_role.roleid # => 'conjur:host:example-host'
128
+ #
129
+ # @return [Conjur::Role] the authenticated role for this API instance
58
130
  def current_role
59
131
  role_from_username username
60
132
  end
61
133
 
134
+
135
+ #@!endgroup
136
+
137
+ # @api private
138
+ #
139
+ # Get a Role instance from a username or host id
140
+ # @param [String] username the username or host id
141
+ # @return [Conjur::Role]
62
142
  def role_from_username username
63
143
  role(role_name_from_username username)
64
144
  end
65
145
 
146
+ # @api private
147
+ #
148
+ # Convert a username or host id to a role identifier.
149
+ # This handles conversion of logins like 'host/foo' to 'host:foo'
150
+ # @param [String] username the user name or host id
151
+ # @return [String] A full role id for the user or host
66
152
  def role_name_from_username username = self.username
67
153
  tokens = username.split('/')
68
154
  if tokens.size == 1
@@ -71,8 +157,14 @@ module Conjur
71
157
  [ tokens[0], tokens[1..-1].join('/') ].join(':')
72
158
  end
73
159
  end
74
-
160
+
75
161
  private
162
+
163
+ # @api private
164
+ # Use of this method is deprecated in favor of Conjur::Cast#cast
165
+ # @deprecated
166
+ # @param [String, Conjur::Role] role object to extract a role id from
167
+ # @return [String] the role id
76
168
  def normalize_roleid role
77
169
  case role
78
170
  when String then role
@@ -22,10 +22,25 @@ require 'conjur/secret'
22
22
 
23
23
  module Conjur
24
24
  class API
25
+
26
+ # @api private
27
+ #
28
+ # Create a Conjur secret. Secrets are a low-level construcct upon which variables
29
+ # are built,
30
+ #
31
+ # @param [String] value the secret data
32
+ # @return [Conjur::Secret] the new secret
25
33
  def create_secret(value, options = {})
26
34
  standard_create Conjur::Core::API.host, :secret, nil, options.merge(value: value)
27
35
  end
28
-
36
+
37
+ # @api private
38
+ #
39
+ # Fetch a Conjur secret by id. Secrets are a low-level construct upon which variables
40
+ # are built, and should not generally be used directly.
41
+ #
42
+ # @param [String] id the *unqualified* identifier for the secret
43
+ # @return [Conjur::Secret] an object representing the secret
29
44
  def secret id
30
45
  standard_show Conjur::Core::API.host, :secret, id
31
46
  end
@@ -7,7 +7,7 @@
7
7
  # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
8
  # the Software, and to permit persons to whom the Software is furnished to do so,
9
9
  # subject to the following conditions:
10
- #
10
+ ##
11
11
  # The above copyright notice and this permission notice shall be included in all
12
12
  # copies or substantial portions of the Software.
13
13
  #
@@ -22,16 +22,80 @@ require 'conjur/user'
22
22
 
23
23
  module Conjur
24
24
  class API
25
+ #@!group Directory: Users
26
+
27
+ # Create a {http://developer.conjur.net/reference/services/directory/user Conjur User}. Conjur users
28
+ # are identities for humans.
29
+ #
30
+ # When you create a user for the first time, the returned object will have an `api_key` field. You can then
31
+ # use this to set a password for the user if you want to. Note that when the user is fetched later with the {#user}
32
+ # method, it **will not have an api_key**. Use it or lose it.
33
+ #
34
+ # ### Permissions
35
+ # Any authenticated role may call this method.
36
+ #
37
+ # @example Create a user 'alice' and set her password to 'frogger'
38
+ # alice = api.create_user 'alice', password: 'frogger'
39
+ #
40
+ # # Now we can login as 'alice'.
41
+ # alice_api = Conjur::API.new_from_key 'alice', 'frogger'
42
+ # alice_api.current_role # => 'conjur:user:alice'
43
+ #
44
+ # @example Create a user and save her `api_key` for later use
45
+ # alice = api.create_user 'alice' # note that we're not giving a password
46
+ # save_api_key 'alice', alice.api_key
47
+ #
48
+ # @param [String] login the login for the new user
49
+ # @param [Hash] options options for user creation
50
+ # @option options [String] :acting_as Qualified id of a role to perform the action as
51
+ # @option options [String, Integer] :uidnumber UID number to assign to the new user. If not given, one will be generated.
52
+ # @option options [String] :password when present, the user will be given a password in addition to a randomly
53
+ # generated api key.
54
+ # @return [Conjur::User] an object representing the new user
55
+ # @raise [RestClient::Conflict] If the user already exists, or a user with the given uidnumber exists.
25
56
  def create_user(login, options = {})
26
57
  standard_create Conjur::Core::API.host, :user, nil, options.merge(login: login)
27
58
  end
28
59
 
60
+ # Return an object representing a user with the given login. The {Conjur::User} object returned
61
+ # may or may not exist. You can check whether it exists with the {Conjur::Exists#exists?} method.
62
+ #
63
+ # The returned {Conjur::User} will *not* have an api_key.
64
+ #
65
+ # ### Permissions
66
+ # Any authenticated role may call this method.
67
+ #
68
+ # @param [String] login the user's login
69
+ # @return [Conjur::User] an object representing the user
29
70
  def user login
30
71
  standard_show Conjur::Core::API.host, :user, login
31
72
  end
32
73
 
74
+ # @api private
75
+ #
76
+ # @note In the future, further options for search may be added, but presently this only supports uid search.
77
+ #
78
+ # Find users by uidnumber.
79
+ #
80
+ #
81
+ # When a user is created it is assigned a uid number. When the uid number is not specified when creating the user,
82
+ # a sequential uid number will be generated, starting at 1000. uidnumbers are used when synchronizing with LDAP directories
83
+ # and to assign a UNIX user id number when using {http://developer.conjur.net/tutorials/ssh/conjur-ssh.html Conjur SSH login}.
84
+ #
85
+ # ### Note
86
+ # Although users are uniquely identified by their uidnumber, the result of this method is an array of user ids for compatibility
87
+ # reasons.
88
+ #
89
+ # ### Permissions
90
+ # Only roles of which you are a member will be returned
91
+ #
92
+ # @param [Hash] options query to send
93
+ # @option options [String, Integer] :uidnumber (required) the uidnumber to search for
94
+ # @return [Array<String>] a one element array containing the users login.
33
95
  def find_users options
34
96
  JSON.parse( RestClient::Resource.new(Conjur::Core::API.host, credentials)["users/search?#{options.to_query}"].get )
35
97
  end
98
+
99
+ #@!endgroup
36
100
  end
37
101
  end
@@ -22,14 +22,77 @@ require 'conjur/variable'
22
22
 
23
23
  module Conjur
24
24
  class API
25
+ #@!group Directory: Variables
26
+
27
+ # Create a {http://developer.conjur.net/reference/services/directory/variable Conjur Variable}.
28
+ # See {Conjur::Variable} for operations on Conjur variables.
29
+ #
30
+ # ### Permissions
31
+ # Any authenticated role may call this method
32
+ #
33
+ # @example Create a variable to store a database connection string
34
+ # db_uri = "mysql://username:password@mysql.somehost.com/mydb"
35
+ # var = api.create_variable 'text/plain', 'mysql-connection-string', id: 'production/mysql/uri'
36
+ # var.add_value db_uri
37
+ #
38
+ # # Alternatively, we could have done this:
39
+ # var = api.create_variable 'text/plain', 'mysql-connection-string',
40
+ # id: 'production/mysql/uri',
41
+ # value: db_uri
42
+ #
43
+ # @example Create a variable with a unique random id
44
+ # var = api.create_variable 'text/plain', 'secret'
45
+ # var.id # => 'kngeqg'
46
+ #
47
+ # @param [String] mime_type MIME type for the variable value, used to set the `"Content-Type"`header
48
+ # when serving the variable's value. Must be non-empty.
49
+ # @param [String] kind user defined `kind` for the variable. This is useful as a simple way to document
50
+ # the variable's purpose. Must be non-empty
51
+ # @param [Hash] options options for the new variable
52
+ # @option options [String] :id specify an id for the new variable. Must be non-empty.
53
+ # @option options [String] :value specify an initial value for the variable
54
+ # @return [Conjur::Variable] an object representing the new variable
55
+ # @raise [RestClient::Conflict] if you give an `:id` option and the variable already exists
56
+ # @raise [RestClient::UnprocessableEntity] if `mime_type`, `kind`, or `options[:id]` is the empty string.
25
57
  def create_variable(mime_type, kind, options = {})
26
58
  standard_create Conjur::Core::API.host, :variable, nil, options.merge(mime_type: mime_type, kind: kind)
27
59
  end
28
-
60
+
61
+ # Retrieve an object representing a {http://developer.conjur.net/reference/services/directory/variable Conjur Variable}.
62
+ # The {Conjur::Variable} returned may or may not exist, and
63
+ # your permissions on the corresponding resource determine the operations you can perform on it.
64
+ #
65
+ # ### Permissions
66
+ # Any authenticated role can call this method.
67
+ #
68
+ # @param [String] id the unqualified id of the variable
69
+ # @return [Conjur::Variable] and object representing the variable.
29
70
  def variable id
30
71
  standard_show Conjur::Core::API.host, :variable, id
31
72
  end
32
73
 
74
+ # Fetch the values of a list of variables. This operation is more efficient than fetching the
75
+ # values one by one.
76
+ #
77
+ # This method will fail unless:
78
+ # * All of the variables exist
79
+ # * You have permission to `'execute'` all of the variables
80
+ #
81
+ # @example Fetch multiple variable values
82
+ # values = variable_values ['postgres_uri', 'aws_secret_access_key', 'aws_access_key_id']
83
+ # values # =>
84
+ # {
85
+ # "postgres_uri" => "postgres://..."
86
+ # "aws_secret_access_key" => "..."
87
+ # "aws_access_key_id" => "..."
88
+ # }
89
+ #
90
+ # This method is used to implement the {http://developer.conjur.net/reference/tools/utilities/conjurenv `conjur env`}
91
+ # commands. You may consider using that instead to run your program in an environment with the necessary secrets.
92
+ #
93
+ # @param [Array<String>] varlist list of variable ids to fetch
94
+ # @return [Hash] a hash mapping variable ids to variable values
95
+ # @raise [RestClient::Forbidden, RestClient::ResourceNotFound] if any of the variables don't exist or aren't accessible.
33
96
  def variable_values(varlist)
34
97
  raise ArgumentError, "Variables list must be an array" unless varlist.kind_of? Array
35
98
  raise ArgumentError, "Variables list is empty" if varlist.empty?
@@ -42,5 +105,6 @@ module Conjur
42
105
  end
43
106
  end
44
107
 
108
+ #@!endgroup
45
109
  end
46
110
  end
@@ -23,6 +23,9 @@ module Conjur
23
23
  module Audit
24
24
  class API < Conjur::API
25
25
  class << self
26
+ # The URL for the audit service
27
+ #
28
+ # @return [String] the audit service url.
26
29
  def host
27
30
  Conjur.configuration.audit_url
28
31
  end
@@ -22,6 +22,10 @@ module Conjur
22
22
  module Authn
23
23
  class API < Conjur::API
24
24
  class << self
25
+
26
+ # The URL for the audit service
27
+ #
28
+ # @return [String] the audit service url.
25
29
  def host
26
30
  Conjur.configuration.authn_url
27
31
  end
@@ -22,6 +22,10 @@ module Conjur
22
22
  module Authz
23
23
  class API < Conjur::API
24
24
  class << self
25
+
26
+ # The URL for the audit service
27
+ #
28
+ # @return [String] the audit service url.
25
29
  def host
26
30
  Conjur.configuration.authz_url
27
31
  end
data/lib/conjur/base.rb CHANGED
@@ -35,31 +35,7 @@ require 'conjur/cast'
35
35
  module Conjur
36
36
  # NOTE: You have to put all 'class level' api docs here, because YARD is stoopid :-(
37
37
 
38
- # This class provides access to Conjur services
39
- # **TODO MOAR**
40
- #
41
- # # Conjur Services
42
- #
43
- # ### Public Keys Service
44
- # The {http://developer.conjur.net/reference/services/pubkeys Conjur Public Keys} service provides a
45
- # simple database of public keys with access controlled by Conjur. Reading a user's public keys requires
46
- # no authentication at all -- the user's public keys are public information, after all.
47
- #
48
- # Adding or deleting a public key may only be done if you have permission to update the *public keys
49
- # resource*, which is created when the appliance is launched, and has a resource id
50
- # `'<organizational account>:service:pubkeys-1.0/public-keys'`. The appliance also comes with a Group named
51
- # `'pubkeys-1.0/key-managers'` that has this permission. Rather than granting each user permission to
52
- # modify the public keys database, you should consider adding users to this group.
53
- #
54
- # A very common use case is {http://developer.conjur.net/tutorials/ssh public key management for SSH}
55
- #
56
- #
57
- # ### Audit Service
58
- #
59
- # The {http://developer.conjur.net/reference/services/audit Conjur Audit Service} allows you to
60
- # fetch audit records.
61
- #
62
- # Generally you will need to have *at least one* privilege on the subject of an event in order to see it.
38
+ # This class provides access to the Conjur services.
63
39
  class API
64
40
  include Escape
65
41
  include LogSource
@@ -184,19 +160,37 @@ module Conjur
184
160
 
185
161
  raise "Expecting ( username and api_key ) or token" unless ( username && api_key ) || token
186
162
  end
187
-
163
+
164
+ #@!attribute [r] api_key
165
+ # The api key used to create this instance. This is only present when you created the api with {Conjur::API.new_from_key}.#
166
+ #
167
+ # @return [String] the api key, or nil if this instance was created from a token.
188
168
  attr_reader :api_key
189
169
 
170
+ # The name of the user as which this api instance is authenticated. This is available whether the api
171
+ # instance was created from credentials or an authentication token.
190
172
  #
173
+ # @return [String] the login of the current user.
191
174
  def username
192
175
  @username || @token['data']
193
176
  end
194
177
 
195
-
178
+ # @api private
179
+ # used to delegate to host providing subclasses.
180
+ # @return [String] the host
196
181
  def host
197
182
  self.class.host
198
183
  end
199
184
 
185
+ # The token used to authenticate requests made with the api. The token will be fetched
186
+ # if it hasn't already, or if it has expired. Accordingly, this method may raise a RestClient::Unauthorized
187
+ # exception if the credentials are invalid.
188
+ #
189
+ # @note calling this method on an {Conjur::API} instance created with {Conjur::API.new_from_token} will have
190
+ # undefined behavior if the token is expired.
191
+ #
192
+ # @return [Hash] the authentication token as a Hash
193
+ # @raise [RestClient::Unauthorized] if the username and api key are invalid.
200
194
  def token
201
195
  @token = nil unless token_valid?
202
196
 
@@ -206,15 +200,22 @@ module Conjur
206
200
 
207
201
  return @token
208
202
  end
209
-
210
- # Authenticate the username and api_key to obtain a request token.
211
- # Tokens are cached by username for a short period of time.
203
+
204
+ # Credentials that can be merged with options to be passed to `RestClient::Resource` HTTP request methods.
205
+ # These include a username and an Authorization header containing the authentication token.
206
+ #
207
+ # @return [Hash] the options.
208
+ # @raise [RestClient::Unauthorized] if fetching the token fails.
209
+ # @see {#token}
212
210
  def credentials
213
211
  { headers: { authorization: "Token token=\"#{Base64.strict_encode64 token.to_json}\"" }, username: username }
214
212
  end
215
213
 
216
214
  private
217
215
 
216
+ # Check to see if @token is defined, and whether it's expired
217
+ #
218
+ # @return [Boolean] whether or not the token is valid.
218
219
  def token_valid?
219
220
  return false unless @token
220
221