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
data/lib/conjur/has_id.rb CHANGED
@@ -19,11 +19,23 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+
23
+ # Included in classes for assets that derive their id from their urls.
22
24
  module HasId
25
+ # @api private
26
+ # This method is provided to support basic JSON serialization for all objects with `id`s.
27
+ #
28
+ # @param [Hash] options provided for backwards compatibility, do not use.
29
+ # @return [Hash] the JSON hash.
23
30
  def to_json(options = {})
24
31
  { id: id }
25
32
  end
26
-
33
+
34
+
35
+ # Get this assets id. This is the *unqualified* Conjur id for the asset,
36
+ # and is derived from the asset's url.
37
+ #
38
+ # @return [String] the asset's id
27
39
  def id
28
40
  URI.unescape self.url.split('/')[-1]
29
41
  end
@@ -19,13 +19,16 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+ # Included in Conjur assets that have an identifier attribute.
22
23
  module HasIdentifier
23
- def self.included(base)
24
- base.instance_eval do
25
- include HasAttributes
26
- end
27
- end
28
-
24
+ include HasAttributes
25
+
26
+ # Get the identifier attribute. This is a *fully qualified* Conjur id.
27
+ #
28
+ # ### Permissions
29
+ # You must have the "`read`" permission on the underlying resource to call this method.
30
+ #
31
+ # @return [String] the asset's fully qualified id
29
32
  def identifier
30
33
  attributes['identifier']
31
34
  end
@@ -19,17 +19,31 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+ # Included in assets that have an *owner*.
22
23
  module HasOwner
23
- def self.included(base)
24
- base.instance_eval do
25
- include HasAttributes
26
- end
27
- end
28
-
24
+ include HasAttributes
25
+
26
+ # Return the `userid` attribute. This is the id of the Conjur role that
27
+ # created this asset.
28
+ #
29
+ # ### Permissions
30
+ # You must have the "`read`" permission on the underlying resource to call this method.
31
+ #
32
+ # @return [String] the userid
29
33
  def userid
30
34
  attributes['userid']
31
35
  end
32
-
36
+
37
+
38
+ # Return the owner of this resource or asset.
39
+ #
40
+ # The owner of a resource or an asset with an underlying resource is allowed to do anything to the resource,
41
+ # including granting permissions to other roles.
42
+ #
43
+ # ### Permissions
44
+ # You must have the "`read`" permission on the underlying resource to call this method.
45
+ #
46
+ # @return [String] the fully qualified role id of the asset's owner.
33
47
  def ownerid
34
48
  attributes['ownerid']
35
49
  end
data/lib/conjur/host.rb CHANGED
@@ -19,7 +19,15 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+ # This class represents a {http://developer.conjur.net/reference/services/directory/host
23
+ # Conjur Host} asset. You should not create {Conjur::Host} instances directly, but use {Conjur::API}
24
+ # methods such as {Conjur::API#create_host} and {Conjur::API#host}.
22
25
  class Host < Deputy
26
+
27
+ # @api private
28
+ # @deprecated
29
+ #
30
+ # This method was used before conjurize came along. It's no longer in use.
23
31
  def enrollment_url
24
32
  log do |logger|
25
33
  logger << "Fetching enrollment_url for #{id}"
@@ -1,5 +1,9 @@
1
1
  class Conjur::API
2
2
  class << self
3
+ # @api private
4
+ #
5
+ # Url to the layers service.
6
+ # @return [String] the url
3
7
  def layer_asset_host
4
8
  ENV["CONJUR_LAYER_ASSET_URL"] || Conjur::Core::API.host
5
9
  end
data/lib/conjur/layer.rb CHANGED
@@ -1,8 +1,37 @@
1
1
  module Conjur
2
+
3
+ # A {http://developer.conjur.net/reference/services/directory/layer Conjur Layer}
4
+ # represents a collection of
5
+ # {http://developer.conjur.net/reference/services/directory/host Conjur Hosts} with the
6
+ # ssame permissions on other Conjur resources.
7
+ #
8
+ # @example Allow hosts in the layer `dev/database` to access a `dev/database_uri` secret
9
+ # # Create the layer and add a couple of EC2 hosts
10
+ # layer = api.create_layer 'dev/database'
11
+ # hosts = ['ec2-iac5ed', 'ec2-iadc31'].map{ |hostid| api.create_host id: hostid }
12
+ # hosts.each{ |host| layer.add_host host }
13
+ #
14
+ # # A Variable representing the database uri secret
15
+ # database_uri = api.variable 'dev/database_uri'
16
+ #
17
+ # # Currently none of the hosts can access it:
18
+ # hosts.any?{ |host| host.role.permitted? database_uri, 'execute' } # => false
19
+ #
20
+ # # Grant permission on the layer
21
+ # database_uri.resource.permit 'execute', layer
22
+ #
23
+ # # Now all hosts in the layer have the execute permission on the secret through the layer
24
+ # hosts.all?{ |host| host.role.permitted? database_uri, 'execute' } # => true
25
+ #
2
26
  class Layer < RestClient::Resource
3
27
  include ActsAsAsset
4
28
  include ActsAsRole
5
-
29
+
30
+ # Add a host to this layer. The host's role will become a member of the layer's role, and have
31
+ # all privileges of the layer.
32
+ #
33
+ # @param [String, Conjur::Host] hostid A *qualified* Conjur id for the host, or a {Conjur::Host} instance.
34
+ # @return [void]
6
35
  def add_host(hostid)
7
36
  hostid = cast(hostid, :roleid)
8
37
  log do |logger|
@@ -12,7 +41,12 @@ module Conjur
12
41
  RestClient::Resource.new(self['hosts'].url, options).post(hostid: hostid)
13
42
  end
14
43
  end
15
-
44
+
45
+ # Remove a host from this layer. The host will lose all privileges it had through this
46
+ # layer.
47
+ #
48
+ # @param [String, Conjur::Host] hostid A *qualified* Conjur id for the host, or a {Conjur::Host} instance.
49
+ # @return [void]
16
50
  def remove_host(hostid)
17
51
  hostid = cast(hostid, :roleid)
18
52
  log do |logger|
@@ -23,11 +57,24 @@ module Conjur
23
57
  end
24
58
  end
25
59
 
26
- # Lists the roles that have been granted access to the hosts owned roles.
60
+ # Lists the roles that have been granted access to the host's owned roles.
61
+ #
62
+ # `role_name` can be either `admin_host` or `use_host`. This method corresponds
63
+ # to {Conjur::ActsAsAsset#add_member} in that members added with that method
64
+ # will be returned by this method.
65
+ #
66
+ # @param [String] role_name Either `use_host` or `admin_host`
67
+ # @return [Conjur::RoleGrant] the grants associated with this host (the return type
68
+ # is identical to that of {Conjur::Role#members}).
69
+ # @see Conjur::ActsAsAsset#add_member
27
70
  def hosts_members(role_name)
28
71
  owned_role(role_name).members
29
72
  end
30
73
 
74
+
75
+ # Return all hosts in the layer.
76
+ #
77
+ # @return [Array<Conjur::Host>] the hosts in the layer.
31
78
  def hosts
32
79
  self.attributes['hosts'].collect do |id|
33
80
  Conjur::Host.new(Conjur::API.core_asset_host, options)["hosts/#{fully_escape id}"]
data/lib/conjur/log.rb CHANGED
@@ -21,11 +21,26 @@
21
21
  require 'logger'
22
22
 
23
23
  module Conjur
24
- # You can also configure logging by the environment variable CONJURAPI_LOG.
24
+ # Assign a Logger for use by Conjur API methods. This method accepts
25
+ # several argument forms:
26
+ # * The strings 'stdout' and 'stderr' cause log messages to be sent to the corresponding stream.
27
+ # * Other stings are treated as paths and will cause log messages to be sent to those files.
28
+ # * A `Logger` instance will be used as is.
29
+ #
30
+ # Note that the logger specified by the `CONJURAPI_LOG` environment variable will override
31
+ # the value set here.
32
+ #
33
+ # @param [String, Logger,nil] log the new logger to use
34
+ # @return [void]
25
35
  def self.log= log
26
36
  @@log = create_log log
27
37
  end
28
38
 
39
+ # @api private
40
+ # Create a log from a String or Logger param
41
+ #
42
+ # @param [String, Logger, nil] param the value to create the logger from
43
+ # @return Logger
29
44
  def self.create_log param
30
45
  if param
31
46
  if param.is_a? String
@@ -46,7 +61,12 @@ module Conjur
46
61
 
47
62
  @@log = nil
48
63
 
49
- def self.log # :nodoc:
64
+ # @api private
65
+ # @note this method may return nil if no log has been set, so you **must** check the value
66
+ # before attempting to use the logger.
67
+ #
68
+ # You should consider using {Conjur::LogSource} instead.
69
+ def self.log
50
70
  @@env_log || @@log
51
71
  end
52
72
  end
@@ -19,7 +19,34 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+ # This module provides logging support for actions taken by the Conjur API.
23
+ #
24
+ # @example
25
+ # class Example
26
+ # include LogSource
27
+ #
28
+ # def something_interesting param
29
+ # log{|l| l << "doing something interesting with #{param}"}
30
+ #
31
+ # # Do something interesting...
32
+ # end
33
+ #
34
+ # end
35
+ # # ...
36
+ #
37
+ # Example.new.something_interesting 'foo'
38
+ # # will log:
39
+ # # [admin] doing something interesting with foo
40
+ #
22
41
  module LogSource
42
+ # Yield a logger to the block. You should use the `<<` method to write to the
43
+ # logger so that you don't send newlines or formatting. The block will only be called
44
+ # if {Conjur.log} is not nil.
45
+ #
46
+ # The log format is `"[<username>]<messages logged in block>\n"`.
47
+ #
48
+ # @yieldparam [#<<] logger a logger to write messages
49
+ # @return [void]
23
50
  def log(&block)
24
51
  if Conjur.log
25
52
  Conjur.log << "["
@@ -19,21 +19,66 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
+ # This module provides methods for determining Conjur id components from an asset's
23
+ # REST URL.
22
24
  module PathBased
25
+ # Return the Conjur {http://developer.conjur.net/reference/services/authorization#Organization.Account
26
+ # organizational account} for this role or resource. The `account`
27
+ # is the first token in a fully qualified Conjur id, like `"account:kind:identifier"`
28
+ #
29
+ # @example
30
+ # role = api.role 'foo:bar:baz'
31
+ # role.account # => 'foo'
32
+ #
33
+ # @return [String] the Conjur organizational account
23
34
  def account
24
35
  match_path(0..0)
25
36
  end
26
37
 
38
+ # Return the *kind* for this role or resource. The kind partitions the space of roles and resources, generally
39
+ # according to their purpose (for example, roles representing users have kind `'user'`). The `kind` of a role or
40
+ # resource is the second token of a fully qualified Conjur id, like `"account:kind:identifier"`.
41
+ #
42
+ # @example Get the kind of a role
43
+ # role = api.host('postgres-1').role
44
+ # role.kind # => 'host'
45
+ #
46
+ # @example Get the kind of a resource
47
+ # res = api.host('postgres-1').resource
48
+ # res.kind # => 'host'
49
+ #
50
+ # @return [String] the kind of the role or resource
27
51
  def kind
28
52
  match_path(2..2)
29
53
  end
30
54
 
31
55
  protected
32
-
56
+
57
+ # @api private
58
+ #
59
+ # Returns the path parts in the given range.
60
+ #
61
+ # @example
62
+ # self.url # => "https://10.0.3.100/api/authz/foo/roles/bar/baz"
63
+ # self.match_path 0..2 # => "foo/roles/bar"
64
+ # self.match_path 2..-1 # => "bar/baz"
65
+ #
66
+ # @param [Range] range the range of parts
67
+ # @return [String] the parts joined by `'/'`
33
68
  def match_path(range)
34
69
  tokens[range].map{|t| URI.unescape(t)}.join('/')
35
70
  end
36
-
71
+
72
+ # @api private
73
+ #
74
+ # Returns the components of this asset's path starting with the first component
75
+ # that isn't part of the authz service url.
76
+ #
77
+ # @example
78
+ # self.url # => "https://10.0.3.100/api/authz/foo/roles/bar/baz"
79
+ # self.tokens # => ["foo", "roles", "bar", "baz"]
80
+ #
81
+ # @return [Array<String>] the path components
37
82
  def tokens
38
83
  self.url[RestClient::Resource.new(Conjur::Authz::API.host)[''].url.length..-1].split('/')
39
84
  end
@@ -22,6 +22,14 @@ require 'conjur/api'
22
22
  require 'conjur/configuration'
23
23
 
24
24
  class Conjur::Configuration
25
+ # @!attribute pubkeys_url
26
+ # The url for the {http://developer.conjur.net/reference/services/pubkyes Conjur public keys service}.
27
+ #
28
+ # @note You should not generally set this value. Instead, Conjur will derive it from the
29
+ # {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url}
30
+ # properties.
31
+ #
32
+ # @return [String] the pubkeys service url
25
33
  add_option :pubkeys_url do
26
34
  account_service_url 'pubkeys', 400
27
35
  end
@@ -29,6 +37,10 @@ end
29
37
 
30
38
  class Conjur::API
31
39
  class << self
40
+ # @api private
41
+ #
42
+ # Url to the pubkeys service.
43
+ # @return [String] the url
32
44
  def pubkeys_asset_host
33
45
  Conjur.configuration.pubkeys_url
34
46
  end
data/lib/conjur/role.rb CHANGED
@@ -21,20 +21,47 @@
21
21
  require 'conjur/role_grant'
22
22
 
23
23
  module Conjur
24
+ # A {http://developer.conjur.net/reference/services/authorization/role Conjur Role} represents an actor that
25
+ # can be granted or denied permissionto do various things to
26
+ # {http://developer.conjur.net/reference/services/authorization/resource Conjur Resources}. Roles are hierarchical:
27
+ # if role a is a **member of** role b, a is permitted to do everything b is permitted
28
+ # to do. This relationship is transitive, so if a is a member of b, b is a member of c,
29
+ # and c is a member of d, a has all of d's permissions.
30
+ #
31
+ # This class represents a Role with a particular identifier. The actual Conjur role *may or may not
32
+ # exist!*
24
33
  class Role < RestClient::Resource
25
34
  include Exists
26
35
  include PathBased
27
36
 
37
+ # The *unqualified* identifier for this role.
38
+ #
39
+ # @example
40
+ # api.role('conjur:foo:bar').identifier # => "bar"
41
+ #
42
+ # @return [String] the unqualified identifier
28
43
  def identifier
29
44
  match_path(3..-1)
30
45
  end
31
46
 
32
47
  alias id identifier
33
-
48
+
49
+ # The *qualified* identifier for this role.
50
+ #
51
+ # @example
52
+ # api.user('bob').role_id # => "conjur:user:bob"
53
+ #
54
+ # @return [String] the *qualified* identifier
34
55
  def roleid
35
56
  [ account, kind, identifier ].join(':')
36
57
  end
37
-
58
+
59
+ alias role_id roleid
60
+
61
+ # @api private
62
+ # Create this role.
63
+ #
64
+ # You probably want to use {Conjur::API#create_role} instead.
38
65
  def create(options = {})
39
66
  log do |logger|
40
67
  logger << "Creating role #{kind}:#{identifier}"
@@ -44,7 +71,32 @@ module Conjur
44
71
  end
45
72
  self.put(options)
46
73
  end
47
-
74
+
75
+ # Find all roles of which this role is a member. This relationship is recursively expanded,
76
+ # so if `a` is a member of `b`, and `b` is a member of `c`, `a.all` will include `c`.
77
+ #
78
+ # ### Permissions
79
+ # You must be a member of the role to call this method (note that the `admin` user is
80
+ # a member of every role).
81
+ #
82
+ # You can restrict the roles returned to one or more role ids. This feature is mainly useful
83
+ # for checking whether this role is a member of any of a set of roles.
84
+ #
85
+ # @example Show all roles of which `"conjur:group:pubkeys-1.0/key-managers"` is a member
86
+ # # Add alice to the group, so we see something interesting
87
+ # key_managers = api.group('pubkeys-1.0/key-managers')
88
+ # key_managers.add_member api.user('alice')
89
+ #
90
+ # # Show the memberships, mapped to the member ids.
91
+ # key_managers.role.all.map(&:roleid)
92
+ # # => ["conjur:group:pubkeys-1.0/admin", "conjur:user:alice"]
93
+ #
94
+ # @example See if role `"conjur:user:alice"` is a member of either `"conjur:groups:developers"` or `"conjur:group:ops"`
95
+ # is_member = not api.role('conjur:user:alice').all(filter: ['conjur:group:developers', 'conjur:group:ops']).empty?
96
+ #
97
+ # @param [Hash] options options for the request
98
+ # @option options [String, #roleid, Array<String, #roleid>] :filter only return roles in this list
99
+ # @return [Array<Conjur::Role>] Roles of which this role is a member
48
100
  def all(options = {})
49
101
  query_string = "?all"
50
102
 
@@ -59,14 +111,95 @@ module Conjur
59
111
  end
60
112
 
61
113
  alias memberships all
62
-
114
+
115
+ # Check to see if this role is a member of another role. Membership is transitive.
116
+ #
117
+ # ### Permissions
118
+ # You must be logged in as a member of this role in order to call this method. Note that if you
119
+ # pass a role of which you aren't a member to this method, it will return false rather than raising an
120
+ # exception.
121
+ #
122
+ # @example Permissions
123
+ # alice_api = Conjur::API.new_from_key "alice", "alice-password"
124
+ # admin_api = Conjur::API.new_from_key "admin", "admin-password"
125
+ #
126
+ # # admin_view is the role as seen by the admin user
127
+ # admin_view = admin_api.role('conjur:group:pubkeys-1.0/key-managers')
128
+ # admin_view.member_of? alice_api.current_role # => false
129
+ # alice_api.current_role.member_of? admin_view # => false
130
+ #
131
+ # # alice_view is the role as seen by alice (who isn't a member of the key-managers group)
132
+ # alice_view = alice_api.role('conjur:group:pubkeys-1.0/key-managers')
133
+ # alice_view.member_of? alice_api.current_role # raises RestClient::Forbidden
134
+ # alice_api.current_role.member_of? alice_view # false
135
+ #
136
+ # @param [String, #roleid] other_role the role or role id of which we might be a member
137
+ # @return [Boolean] whether this role is a member of `other_role`
138
+ # @raise [RestClient::Forbidden] if you don't have permission to perform this operation
63
139
  def member_of?(other_role)
64
140
  other_role = cast(other_role, :roleid)
65
141
  not all(filter: other_role).empty?
66
142
  end
67
-
68
- # @param [Hash] options
69
- # * *admin_option* enables the +member+ to manage members of this role
143
+
144
+ # Grant this role to another one. The role given by the `member` argument will become
145
+ # a member of this role, and have all of its permissions.
146
+ #
147
+ # ### Permissions
148
+ # You must have admin permissions on this role.
149
+ #
150
+ # @example Allow `'alice'` to do everything that `'bob'` can do (perhaps better!).
151
+ # bob = api.role 'cook:bob'
152
+ # alice = api.role 'cook:alice'
153
+ #
154
+ # # bob is allowed to 'fry' a resource called 'food:bacon'
155
+ # bob.permitted? "food:bacon", "fry" # => true
156
+ #
157
+ # # alice isn't
158
+ # alice.permitted? "food:bacon", "fry" # => false
159
+ #
160
+ # # grant the role 'cook:bob' to alice, so that she can participate in our culture's
161
+ # # bizarre bacon obsession!
162
+ # bob.grant_to alice
163
+ #
164
+ # # Now she can fry some bacon!
165
+ # alice.permitted? 'food:bacon', 'fry' # => true
166
+ #
167
+ # @example Make `alice` a member of `job:cook`, and let her grant that role to others
168
+ # # Create an api logged in as 'alice'. We assume that `api` is an admin.
169
+ # alice_api = Conjur::API.new_from_key 'alice', 'alice-password'
170
+ #
171
+ # # First do it without the `admin_option`
172
+ # api.role('job:cook').grant_to alice_api.current_role
173
+ #
174
+ # # Alice can't grant the role to bob
175
+ # alice_api.role('job:cook').grant_to 'user:bob' # => raises RestClient::Forbidden
176
+ #
177
+ # # Make alice an admin of the role
178
+ # api.role('job:cook').grant_to alice_api.current_role, admin_option: true
179
+ #
180
+ # # Now she can grant the role to bob
181
+ # alice_api.role('job:cook').grant_to 'user:bob' # Works!
182
+ #
183
+ # @example Take away a member's admin privilege
184
+ # # alice_api is an api logged in as user "alice", who has admin rights on the role 'job:cooks'.
185
+ # # Notice that she can grant the role to 'eve'
186
+ # alice_api.role('job:cook').grant_to 'eve'
187
+ #
188
+ # # We don't want her to do this any more
189
+ # admin_api.role('job:cook').grant_to 'user:alice', admin_option: false
190
+ #
191
+ # # She's still a member
192
+ # alice_api.member_of?('job:cook') # => true
193
+ #
194
+ # # But she can't grant the role to 'bob'
195
+ # alice_api.role('job:cook').grant_to 'user:bob' # raises RestClient:Forbidden
196
+ #
197
+ # @param [String, #roleid] member the role that will become a member of this role
198
+ # @param [Hash] options options for the grant
199
+ # @option options [Boolean] :admin_option when given, the admin flag on the role grant will be set to
200
+ # this value.
201
+ # @return [void]
202
+ # @raise [RestClient::Forbidden] if you don't have permission to perform the operation
70
203
  def grant_to(member, options={})
71
204
  member = cast(member, :roleid)
72
205
  log do |logger|
@@ -78,6 +211,35 @@ module Conjur
78
211
  self["?members&member=#{query_escape member}"].put(options)
79
212
  end
80
213
 
214
+ # Remove (revoke) a member from this role. This operation is the inverse of {#grant_to}
215
+ #
216
+ # ### Permissions
217
+ # You must have admin permissions on this role
218
+ #
219
+ #
220
+ # @example Bob has been fired from his job as a cook.
221
+ # # currently, he's a member, and therefore is allowed to 'fry' the 'bacon' resource
222
+ # bob = api.role('user:bob')
223
+ # bob.member_of? 'job:cook' # true
224
+ # bob.permitted? 'food:bacon', 'fry' # true
225
+ #
226
+ # # Revoke 'job:cook'
227
+ # api.role('job:cook').revoke_from 'user:bob'
228
+ #
229
+ # # Now he's not a member, and he can't fry bacon any more
230
+ # bob.member_of? 'job:cook' # false
231
+ # bob.permitted? 'food:bacon', 'fry' # false
232
+ #
233
+ # # Note that if alice had her bacon frying permissions through her membership in the role 'user:bob',
234
+ # # she'll lose them too:
235
+ # api.role('user:alice').member_of? 'user:bob' # true
236
+ # api.role('user:alice').permitted? 'food:bacon', 'fry' # => false
237
+ #
238
+ #
239
+ # @param [String, #roleid] member the member to revoke this role from
240
+ # @param [Hash] options included for backwards compatibility. Don't use it.
241
+ # @return [void]
242
+ # @raise [RestClient::Forbidden] If you don't have permission to perform this operation
81
243
  def revoke_from(member, options = {})
82
244
  member = cast(member, :roleid)
83
245
  log do |logger|
@@ -89,6 +251,46 @@ module Conjur
89
251
  self["?members&member=#{query_escape member}"].delete(options)
90
252
  end
91
253
 
254
+ # Check to see if this role is allowed to perform `privilege` on `resource`.
255
+ #
256
+ # ### Permissions
257
+ # Any authenticated role may call this method. However, instead of raising a 404 if a resource
258
+ # or role doesn't exist, it will return false. This is to prevent bad guys from finding out which roles
259
+ # and resources exist.
260
+ #
261
+ # @example
262
+ # bacon = api.create_resource 'food:bacon'
263
+ # eggs = api.create_resoure 'food:eggs'
264
+ # bob = api.create_role 'cook:bob'
265
+ #
266
+ # # Bob can't do anything initially
267
+ # bob.permitted? bacon, 'fry' # => false
268
+ # bob.permitted? eggs, 'poach' # => false
269
+ #
270
+ # # Let him poach eggs
271
+ # eggs.permit 'poach', bob
272
+ #
273
+ # # Now it's permitted
274
+ # bob.permitted? eggs, 'poach' # => true
275
+ #
276
+ # @example Somethign a bit more realistic
277
+ # # Say we have a service layer that needs access to a database connection string.
278
+ # # The layer is called 'web', and the connection string is stored in a variable 'mysql-uri'
279
+ # web_layer = api.layer 'web'
280
+ # mysql_uri = api.variable 'mysql-uri'
281
+ #
282
+ # # The web layer can't see the value of the variable right now:
283
+ # web_layer.role.permitted? mysql_uri, 'execute' # => false
284
+ #
285
+ # # Let's permit that
286
+ # mysql_uri.permit 'execute', web_layer
287
+ #
288
+ # # Now it's allowed to fetch the connection string
289
+ # web_layer.role.permitted? mysql_uri, 'execute' # => true
290
+ #
291
+ # @param [#resourceid, String] resource the resource to check the permission against
292
+ # @param [String] privilege the privilege to check
293
+ # @return [Boolean] true if this role has the privilege on the resource
92
294
  def permitted?(resource, privilege, options = {})
93
295
  resource = cast(resource, :resourceid)
94
296
  # NOTE: in previous versions there was 'kind' passed separately. Now it is part of id
@@ -98,10 +300,19 @@ module Conjur
98
300
  false
99
301
  end
100
302
 
101
- def members options = {}
303
+
304
+ # Fetch the members of this role. The results are *not* recursively expanded (in contrast to {#memberships}).
305
+ #
306
+ # ### Permissions
307
+ # You must be a member of the role to call this method.
308
+ #
309
+ # @param [Hash] options unused and included only for backwards compatibility
310
+ # @return [Array<Conjur::RoleGrant>] the role memberships
311
+ # @raise [RestClient::Forbidden] if you don't have permission to perform this operation
312
+ def members
102
313
  JSON.parse(self["?members"].get(options)).collect do |json|
103
314
  RoleGrant.parse_from_json(json, self.options)
104
315
  end
105
316
  end
106
317
  end
107
- end
318
+ end