conjur-api 4.14.0 → 4.15.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 (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