Class: Checkpoint::Authority

Inherits:
Object
  • Object
show all
Defined in:
lib/checkpoint/authority.rb

Overview

An Authority is the central point of contact for authorization questions in Checkpoint. It checks whether there are grants that would allow a given action to be taken.

Defined Under Namespace

Classes: RejectAll

Instance Method Summary collapse

Constructor Details

#initialize(agent_resolver: Agent::Resolver.new, credential_resolver: Credential::Resolver.new, resource_resolver: Resource::Resolver.new, grants: Grants.new) ⇒ Authority

Returns a new instance of Authority.



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/checkpoint/authority.rb', line 13

def initialize(
  agent_resolver: Agent::Resolver.new,
  credential_resolver: Credential::Resolver.new,
  resource_resolver: Resource::Resolver.new,
  grants: Grants.new)

  @agent_resolver      = agent_resolver
  @credential_resolver = credential_resolver
  @resource_resolver   = resource_resolver
  @grants              = grants
end

Instance Method Details

#grant!(actor, action, entity) ⇒ Boolean

Grant a single credential to a specific actor on an entity.

The parameters are converted to Agent, Credential, and Resource types, but not expanded. This allows very specific grants to be made. The default conversion of a symbol or string as the action is to a Credential::Permission of the same name.

If you want to use more general grants (for example, for an account type rather than for a given user), you should pass a more general Agent or an object that will be converted to one. Another example would be using a wildcard Resource as the entity to grant the credential for all objects of some given type.

Parameters:

  • actor (Object|Agent)

    The actor to whom the grant should be made.

  • action (Symbol|String|Credential)

    The action or Credential to grant.

  • entity (Object|Resource)

    The entity or Resource to which the grant will apply.

Returns:

  • (Boolean)

    True if the grant was made; false if it failed.



135
136
137
138
139
140
141
142
143
# File 'lib/checkpoint/authority.rb', line 135

def grant!(actor, action, entity)
  grant = grants.grant!(
    agent_resolver.convert(actor),
    credential_resolver.convert(action),
    resource_resolver.convert(entity)
  )

  !grant.nil?
end

#permits?(actor, action, entity) ⇒ Boolean

Check whether there are any matching grants that would allow this actor to take the action on the target entity.

The parameters are generally intended to be the most convenient forms for the application. For example, user and resource model objects would be typical in a Rails application, for the actor and entity, respectively. Using a symbol for a named action is typical.

Each of these will be converted and expanded by the corresponding resolver to sets of Checkpoint::Agents, Credentials, and Resources. In the case where you already have an Agent, Credential, or Resource, it can be passed; the expectation is that those types have an identity conversion.

Parameters:

  • actor (Object|Agent)

    The person/account taking the action.

  • action (Symbol|String|Credential)

    The action to authorize or Credential to check for.

  • entity (Object|Resource)

    The entity/resource to be acted upon.

Returns:

  • (Boolean)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/checkpoint/authority.rb', line 42

def permits?(actor, action, entity)
  # Conceptually equivalent to:
  #   can?(current_user, :edit, @listing)

  #  user   => agent tokens
  #  action => credential tokens
  #  target => resource tokens

  # Grant.where(agent: agents, credential: credentials, resource: resources)
  # SELECT * FROM grants
  # WHERE agent IN('user:gkostin', 'account-type:umich', 'affiliation:lib-staff')
  # AND credential IN('permission:edit', 'role:editor')
  # AND resource IN('listing:17', 'type:listing')

  #  agent_type, agent_id    | cred_type, cred_id | resource_type, resource_id
  #  ------------------------------------------------------------------------
  #  'user:gkostin'          | 'permission:edit'  | 'listing:17'
  #  'account-type:umich'    | 'role:editor'      | 'type:listing'
  #  'affiliation:lib-staff' |                    | 'listing:*'

  #        ^^^                       ^^^^              ^^^^
  #   if current_user has at least one row in each of of these columns,
  #   they have been "granted permission"
  grants.for(
    agent_resolver.expand(actor),
    credential_resolver.expand(action),
    resource_resolver.expand(entity)
  ).any?
end

#revoke!(actor, action, entity) ⇒ Boolean

Revoke a credential from a specific actor on an entity.

Like #permit!, the parameters are converted to Agent, Credential, and Resource types, but not expanded. This means that specific grants can be revoked without revoking more general ones. For example, if a user was granted read permission on an object, and then granted the same credential on all objects of that type, the more specific grant could be revoked individually.

Parameters:

  • actor (Object|Agent)

    The actor from whom the grant should be revoked.

  • action (Symbol|String|Credential)

    The action or Credential to revoke.

  • entity (Object|Resource)

    The entity or Resource upon which the

Returns:

  • (Boolean)

    True if any grants were revoked; false if none were revoked.



158
159
160
161
162
163
164
165
166
# File 'lib/checkpoint/authority.rb', line 158

def revoke!(actor, action, entity)
  revoked = grants.revoke!(
    agent_resolver.convert(actor),
    credential_resolver.convert(action),
    resource_resolver.convert(entity)
  )

  revoked.positive?
end

#what(actor, entity) ⇒ Array<Credential::Token>

Find credentials granted to an actor on an entity.

The actor and entity are expanded for matching more general grants.

Returns:

  • (Array<Credential::Token>)

    The distinct set of tokens for credentials that the actor is granted on the entity



93
94
95
96
97
98
99
100
# File 'lib/checkpoint/authority.rb', line 93

def what(actor, entity)
  agents = agent_resolver.expand(actor)
  resources = resource_resolver.expand(entity)

  grants.what(agents, resources).map do |grant|
    Credential::Token.new(grant.credential_type, grant.credential_id)
  end.uniq
end

#which(actor, action) ⇒ Array<Resource::Token>

Find resources on which the actor is permitted to take the given action.

The actor and action are expanded for matching more general grants.

Returns:

  • (Array<Resource::Token>)

    The distinct set of tokens for resources on which the actor is permitted to take the given action



108
109
110
111
112
113
114
115
# File 'lib/checkpoint/authority.rb', line 108

def which(actor, action)
  agents = agent_resolver.expand(actor)
  credentials = credential_resolver.expand(action)

  grants.which(agents, credentials).map do |grant|
    Resource::Token.new(grant.resource_type, grant.resource_id)
  end.uniq
end

#who(action, entity) ⇒ Array<Agent::Token>

Find agents who have grants to take an action on an entity.

The action and entity are expanded for matching more general grants.

Returns:

  • (Array<Agent::Token>)

    The distinct set of tokens for agents permitted to take the given action on the given entity



78
79
80
81
82
83
84
85
# File 'lib/checkpoint/authority.rb', line 78

def who(action, entity)
  credentials = credential_resolver.expand(action)
  resources = resource_resolver.expand(entity)

  grants.who(credentials, resources).map do |grant|
    Agent::Token.new(grant.agent_type, grant.agent_id)
  end.uniq
end