Class: ApiEngineBase::Authorization::Role

Inherits:
Object
  • Object
show all
Defined in:
lib/api_engine_base/authorization/role.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, description:, entities:, allow_everything: false) ⇒ Role

Returns a new instance of Role.



39
40
41
42
43
44
# File 'lib/api_engine_base/authorization/role.rb', line 39

def initialize(name:, description:, entities:, allow_everything: false)
  @name = name
  @entities = Array(entities)
  @description = description
  @allow_everything = allow_everything
end

Instance Attribute Details

#allow_everythingObject (readonly)

Returns the value of attribute allow_everything.



38
39
40
# File 'lib/api_engine_base/authorization/role.rb', line 38

def allow_everything
  @allow_everything
end

#descriptionObject (readonly)

Returns the value of attribute description.



38
39
40
# File 'lib/api_engine_base/authorization/role.rb', line 38

def description
  @description
end

#entitiesObject (readonly)

Returns the value of attribute entities.



38
39
40
# File 'lib/api_engine_base/authorization/role.rb', line 38

def entities
  @entities
end

#nameObject (readonly)

Returns the value of attribute name.



38
39
40
# File 'lib/api_engine_base/authorization/role.rb', line 38

def name
  @name
end

Class Method Details

.create_role(name:, description:, entities: nil, allow_everything: false) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/api_engine_base/authorization/role.rb', line 7

def create_role(name:, description:, entities: nil, allow_everything: false)
  if roles[name]
    raise Error, "Role [#{name}] already exists. Must use different name"
  end

  if allow_everything
    Rails.logger.info { "Authorization Role: #{name} is granted authorization to all roles" }
  else
    unless Array(entities).all? { Entity === _1 }
      raise Error, "Parameter :entities must include objects of or inherited by ApiEngineBase::Authorization::Entity"
    end
  end

  roles[name] = new(name:, description:, entities:, allow_everything:)
  # A role is `intended` to be immutable (attr_reader)
  # Once the role is defined it will not get changed
  # After it is created, add the mapping to the source of truth list of mapped method names to their controllers
  ApiEngineBase::Authorization.add_mapping!(role: roles[name])

  roles[name]
end

.rolesObject



29
30
31
# File 'lib/api_engine_base/authorization/role.rb', line 29

def roles
  @roles ||= ActiveSupport::HashWithIndifferentAccess.new
end

.roles_reset!Object



33
34
35
# File 'lib/api_engine_base/authorization/role.rb', line 33

def roles_reset!
  @roles = ActiveSupport::HashWithIndifferentAccess.new
end

Instance Method Details

#authorized?(controller:, method:, user:) ⇒ Boolean

Returns:

  • (Boolean)


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
71
72
73
# File 'lib/api_engine_base/authorization/role.rb', line 46

def authorized?(controller:, method:, user:)
  return_value = { role: name, description: }
  return return_value.merge(authorized: true, reason: "#{name} allows all authorizations") if allow_everything

  matched_controllers = controller_entity_mapping[controller]
  # if Role does not match any of the controllers
  # explicitly return nil here to ensure upstream knows this role does not care about the route
  return return_value.merge(authorized: nil, reason: "#{name} does not match") if matched_controllers.nil?

  rejected_entities = matched_controllers.map do |entity|
    case entity.matches?(controller:, method:)
    when false, nil
      { authorized: false, entity: entity.name, controller:, readable: entity.humanize, status: "Rejected by inclusion" }
    when true
      # Entity matches all inclusions
      if entity.authorized?(user:)
        # Do nothing! Entity has authorized the user
      else
        { authorized: false, entity: entity.name, controller:, readable: entity.humanize, status: "Rejected via custom Entity Authorization" }
      end
    end
  end.compact

  # If there were no entities that rejected authorization, return authorized
  return return_value.merge(authorized: true, reason: "All entities approve authorization") if rejected_entities.empty?

  return_value.merge(authorized: false, reason: "Subset of Entities Rejected authorization", rejected_entities:)
end

#controller_entity_mappingObject



96
97
98
# File 'lib/api_engine_base/authorization/role.rb', line 96

def controller_entity_mapping
  @controller_entity_mapping ||= @entities.group_by(&:controller)
end

#guardsObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/api_engine_base/authorization/role.rb', line 75

def guards
  mapping = {}
  controller_entity_mapping.each do |controller, entities|
    mapping[controller] ||= []
    entities.map do |entity|
      if entity.only
        # We only care about these methods on the controller
        mapping[controller] += entity.only
      elsif entity.except
        # We care about all methods on the controller except these
        mapping[controller] += controller.instance_methods(false) - entity.except
      else
        # We care about all methods on the controller
        mapping[controller] += controller.instance_methods(false)
      end
    end
  end

  mapping
end