Class: Conjur::Role

Inherits:
RestClient::Resource
  • Object
show all
Includes:
Exists, PathBased
Defined in:
lib/conjur/role.rb

Overview

A Conjur Role represents an actor that can be granted or denied permissionto do various things to Conjur Resources. Roles are hierarchical: if role a is a member of role b, a is permitted to do everything b is permitted to do. This relationship is transitive, so if a is a member of b, b is a member of c, and c is a member of d, a has all of d's permissions.

This class represents a Role with a particular identifier. The actual Conjur role may or may not exist!

Instance Method Summary collapse

Methods included from PathBased

#account, #kind

Methods included from Exists

#exists?

Instance Method Details

#all(options = {}) ⇒ Array<Conjur::Role> Also known as: memberships

Find all roles of which this role is a member. This relationship is recursively expanded, so if a is a member of b, and b is a member of c, a.all will include c.

Permissions

You must be a member of the role to call this method.

You can restrict the roles returned to one or more role ids. This feature is mainly useful for checking whether this role is a member of any of a set of roles.

Examples:

Show all roles of which "conjur:group:pubkeys-1.0/key-managers" is a member

# Add alice to the group, so we see something interesting
key_managers = api.group('pubkeys-1.0/key-managers')
key_managers.add_member api.user('alice')

# Show the memberships, mapped to the member ids.
key_managers.role.all.map(&:roleid)
# => ["conjur:group:pubkeys-1.0/admin", "conjur:user:alice"]

See if role "conjur:user:alice" is a member of either "conjur:groups:developers" or "conjur:group:ops"

is_member = api.role('conjur:user:alice').all(filter: ['conjur:group:developers', 'conjur:group:ops']).any?

Parameters:

  • options (Hash) (defaults to: {})

    options for the request

Options Hash (options):

  • :filter (String, #roleid, Array<String, #roleid>)

    only return roles in this list

Returns:

  • (Array<Conjur::Role>)

    Roles of which this role is a member



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/conjur/role.rb', line 99

def all(options = {})
  query_string = "?all"
  
  if filter = options.delete(:filter)
    filter = [filter] unless filter.is_a?(Array)
    filter.map!{ |obj| cast(obj, :roleid) }
    (query_string << "&" << filter.to_query("filter")) unless filter.empty?
  end
  JSON.parse(self[query_string].get(options)).collect do |id|
    Role.new(Conjur::Authz::API.host, self.options)[Conjur::API.parse_role_id(id).join('/')]
  end
end

#grant_to(member, options = {})

This method returns an undefined value.

Grant this role to another one. The role given by the member argument will become a member of this role, and have all of its permissions.

Permissions

You must have admin permissions on this role.

Examples:

Allow 'alice' to do everything that 'bob' can do (perhaps better!).

bob = api.role 'cook:bob'
alice = api.role 'cook:alice'

# bob is allowed to 'fry' a resource called 'food:bacon'
bob.permitted? "food:bacon", "fry" # => true

# alice isn't
alice.permitted? "food:bacon", "fry" # => false

# grant the role 'cook:bob'  to alice, so that she can participate in our culture's
# bizarre bacon obsession!
bob.grant_to alice

# Now she can fry some bacon!
alice.permitted? 'food:bacon', 'fry' # => true

Make alice a member of job:cook, and let her grant that role to others

# Create an api logged in as 'alice'.  We assume that `api` is an admin.
alice_api = Conjur::API.new_from_key 'alice', 'alice-password'

# First do it without the `admin_option`
api.role('job:cook').grant_to alice_api.current_role

# Alice can't grant the role to bob
alice_api.role('job:cook').grant_to 'user:bob' # => raises RestClient::Forbidden

# Make alice an admin of the role
api.role('job:cook').grant_to alice_api.current_role, admin_option: true

# Now she can grant the role to bob
alice_api.role('job:cook').grant_to 'user:bob' # Works!

Take away a member's admin privilege

# alice_api is an api logged in as user "alice", who has admin rights on the role 'job:cooks'.
# Notice that she can grant the role to 'eve'
alice_api.role('job:cook').grant_to 'eve'

# We don't want her to do this any more
admin_api.role('job:cook').grant_to 'user:alice', admin_option: false

# She's still a member
alice_api.member_of?('job:cook') # => true

# But she can't grant the role to 'bob'
alice_api.role('job:cook').grant_to 'user:bob' # raises RestClient:Forbidden

Parameters:

  • member (String, #roleid)

    the role that will become a member of this role

  • options (Hash) (defaults to: {})

    options for the grant

Options Hash (options):

  • :admin_option (Boolean)

    when given, the admin flag on the role grant will be set to this value.

Raises:

  • (RestClient::Forbidden)

    if you don't have permission to perform the operation



202
203
204
205
206
207
208
209
210
211
# File 'lib/conjur/role.rb', line 202

def grant_to(member, options={})
  member = cast(member, :roleid)
  log do |logger|
    logger << "Granting role #{identifier} to #{member}"
    unless options.blank?
      logger << " with options #{options.to_json}"
    end
  end
  self["?members&member=#{query_escape member}"].put(options)
end

#identifierString Also known as: id

The unqualified identifier for this role.

Examples:

api.role('conjur:foo:bar').identifier # => "bar"

Returns:

  • (String)

    the unqualified identifier



43
44
45
# File 'lib/conjur/role.rb', line 43

def identifier
  match_path(3..-1)
end

#member_of?(other_role) ⇒ Boolean

Check to see if this role is a member of another role. Membership is transitive.

Permissions

You must be logged in as a member of this role in order to call this method. Note that if you pass a role of which you aren't a member to this method, it will return false rather than raising an exception.

Examples:

Permissions

alice_api = Conjur::API.new_from_key "alice", "alice-password"
admin_api = Conjur::API.new_from_key "admin", "admin-password"

# admin_view is the role as seen by the admin user
admin_view = admin_api.role('conjur:group:pubkeys-1.0/key-managers')
admin_view.member_of? alice_api.current_role # => false
alice_api.current_role.member_of? admin_view # => false

# alice_view is the role as seen by alice (who isn't a member of the key-managers group)
alice_view = alice_api.role('conjur:group:pubkeys-1.0/key-managers')
alice_view.member_of? alice_api.current_role # raises RestClient::Forbidden
alice_api.current_role.member_of? alice_view # false

Parameters:

  • other_role (String, #roleid)

    the role or role id of which we might be a member

Returns:

  • (Boolean)

    whether this role is a member of other_role

Raises:

  • (RestClient::Forbidden)

    if you don't have permission to perform this operation



138
139
140
141
# File 'lib/conjur/role.rb', line 138

def member_of?(other_role)
  other_role = cast(other_role, :roleid)
  not all(filter: other_role).empty?
end

#membersArray<Conjur::RoleGrant>

Fetch the members of this role. The results are not recursively expanded (in contrast to #memberships).

Permissions

You must be a member of the role to call this method.

Parameters:

  • options (Hash)

    unused and included only for backwards compatibility

Returns:

Raises:

  • (RestClient::Forbidden)

    if you don't have permission to perform this operation



311
312
313
314
315
# File 'lib/conjur/role.rb', line 311

def members
  JSON.parse(self["?members"].get(options)).collect do |json|
    RoleGrant.parse_from_json(json, self.options)
  end
end

#permitted?(resource, privilege, options = {}) ⇒ Boolean

Check to see if this role is allowed to perform privilege on resource.

Permissions

Any authenticated role may call this method. However, instead of raising a 404 if a resource or role doesn't exist, it will return false. This is to prevent bad guys from finding out which roles and resources exist.

web_layer.role.permitted? mysql_uri, 'execute' # => true

Examples:

bacon = api.create_resource 'food:bacon'
eggs  = api.create_resoure 'food:eggs'
bob = api.create_role 'cook:bob'

# Bob can't do anything initially
bob.permitted? bacon, 'fry' # => false
bob.permitted? eggs, 'poach' # => false

# Let him poach eggs
eggs.permit 'poach', bob

# Now it's permitted
bob.permitted? eggs, 'poach' # => true

Somethign a bit more realistic

# Say we have a service layer that needs access to a database connection string.
# The layer is called 'web', and the connection string is stored in a variable 'mysql-uri'
web_layer = api.layer 'web'
mysql_uri = api.variable 'mysql-uri'

# The web layer can't see the value of the variable right now:
web_layer.role.permitted? mysql_uri, 'execute' # => false

# Let's permit that
mysql_uri.permit 'execute', web_layer

# Now it's allowed to fetch the connection string

Parameters:

  • resource (#resourceid, String)

    the resource to check the permission against

  • privilege (String)

    the privilege to check

Returns:

  • (Boolean)

    true if this role has the privilege on the resource



293
294
295
296
297
298
299
300
# File 'lib/conjur/role.rb', line 293

def permitted?(resource, privilege, options = {})
  resource = cast(resource, :resourceid)
  # NOTE: in previous versions there was 'kind' passed separately. Now it is part of id
  self["?check&resource_id=#{query_escape resource}&privilege=#{query_escape privilege}"].get(options)
  true
rescue RestClient::ResourceNotFound
  false
end

#revoke_from(member, options = {})

This method returns an undefined value.

Remove (revoke) a member from this role. This operation is the inverse of #grant_to

Permissions

You must have admin permissions on this role

Examples:

Bob has been fired from his job as a cook.

# currently, he's a member, and therefore is allowed to 'fry' the 'bacon' resource
bob = api.role('user:bob')
bob.member_of? 'job:cook' # true
bob.permitted? 'food:bacon', 'fry' # true

# Revoke 'job:cook'
api.role('job:cook').revoke_from 'user:bob'

# Now he's not a member, and he can't fry bacon any more
bob.member_of? 'job:cook' # false
bob.permitted? 'food:bacon', 'fry' # false

# Note that if alice had her bacon frying permissions through her membership in the role 'user:bob',
# she'll lose them too:
api.role('user:alice').member_of? 'user:bob' # true
api.role('user:alice').permitted? 'food:bacon', 'fry' # => false

Parameters:

  • member (String, #roleid)

    the member to revoke this role from

  • options (Hash) (defaults to: {})

    included for backwards compatibility. Don't use it.

Raises:

  • (RestClient::Forbidden)

    If you don't have permission to perform this operation



242
243
244
245
246
247
248
249
250
251
# File 'lib/conjur/role.rb', line 242

def revoke_from(member, options = {})
  member = cast(member, :roleid)
  log do |logger|
    logger << "Revoking role #{identifier} from #{member}"
    unless options.empty?
      logger << " with options #{options.to_json}"
    end
  end
  self["?members&member=#{query_escape member}"].delete(options)
end

#roleidString Also known as: role_id

The qualified identifier for this role.

Examples:

api.user('bob').role_id # => "conjur:user:bob"

Returns:

  • (String)

    the qualified identifier



55
56
57
# File 'lib/conjur/role.rb', line 55

def roleid
  [ , kind, identifier ].join(':')
end