Module: Legion::Rbac

Extended by:
Logging::Helper
Defined in:
lib/legion/rbac.rb,
lib/legion/rbac/role.rb,
lib/legion/rbac/store.rb,
lib/legion/rbac/routes.rb,
lib/legion/rbac/version.rb,
lib/legion/rbac/settings.rb,
lib/legion/rbac/principal.rb,
lib/legion/rbac/middleware.rb,
lib/legion/rbac/permission.rb,
lib/legion/rbac/team_scope.rb,
lib/legion/rbac/config_loader.rb,
lib/legion/rbac/policy_engine.rb,
lib/legion/rbac/capability_audit.rb,
lib/legion/rbac/group_role_mapper.rb,
lib/legion/rbac/capability_registry.rb,
lib/legion/rbac/entra_claims_mapper.rb,
lib/legion/rbac/kerberos_claims_mapper.rb

Defined Under Namespace

Modules: CapabilityAudit, CapabilityRegistry, ConfigLoader, EntraClaimsMapper, GroupRoleMapper, KerberosClaimsMapper, PolicyEngine, Routes, Settings, Store, TeamScope Classes: AccessDenied, DenyRule, Middleware, Permission, Principal, Role

Constant Summary collapse

EMPTY_ROLE_INDEX =
{}.freeze
VERSION =
'0.3.4'

Class Method Summary collapse

Class Method Details

.audit_extension(extension_name:, source_path:, declared_capabilities: []) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/legion/rbac.rb', line 135

def audit_extension(extension_name:, source_path:, declared_capabilities: [])
  log.info("RBAC audit_extension extension=#{extension_name} source_path=#{source_path}")
  result = CapabilityAudit.audit(
    extension_name:        extension_name,
    source_path:           source_path,
    declared_capabilities: declared_capabilities
  )
  CapabilityRegistry.register(
    extension_name,
    capabilities: result.detected_capabilities,
    audit_result: result
  )
  log.info(
    "RBAC audit_extension result extension=#{extension_name} allowed=#{result.allowed} " \
    "detected=#{result.detected_capabilities.size} undeclared=#{result.undeclared.size}"
  )
  result
end

.authorize!(principal:, action:, resource:) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/legion/rbac.rb', line 97

def authorize!(principal:, action:, resource:, **)
  return { allowed: true, reason: 'rbac disabled' } unless enabled?

  result = PolicyEngine.evaluate(principal: principal, action: action, resource: resource, **)
  log.info("RBAC authorize principal=#{principal.id} action=#{action} resource=#{resource} allowed=#{result[:allowed]}")

  unless result[:allowed]
    log.warn("RBAC authorize denied principal=#{principal.id} reason=#{result[:reason]}")
    raise AccessDenied, result if enforcing?
  end

  result
end

.authorize_capability!(principal:, capability:, extension_name: nil) ⇒ Object

Raises:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/legion/rbac.rb', line 154

def authorize_capability!(principal:, capability:, extension_name: nil)
  result = PolicyEngine.evaluate_capability(
    principal:      principal,
    capability:     capability,
    extension_name: extension_name
  )
  log.info(
    "RBAC authorize_capability principal=#{principal.id} capability=#{capability} " \
    "extension=#{extension_name} allowed=#{result[:allowed]}"
  )
  log.warn("RBAC authorize_capability denied principal=#{principal.id} reason=#{result[:reason]}") unless result[:allowed]
  raise AccessDenied, result unless result[:allowed]

  result
end

.authorize_execution!(principal:, runner_class:, function:, target_team: nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/legion/rbac.rb', line 111

def authorize_execution!(principal:, runner_class:, function:, target_team: nil, **)
  return { allowed: true, reason: 'rbac disabled' } unless enabled?

  runner_path = build_runner_path(runner_class, function)
  log.info(
    "RBAC authorize_execution principal=#{principal.id} runner=#{runner_path} " \
    "target_team=#{target_team || principal.team || 'none'}"
  )
  result = PolicyEngine.evaluate_execution(
    principal:   principal,
    action:      :execute,
    resource:    runner_path,
    target_team: target_team,
    **
  )

  unless result[:allowed]
    log.warn("RBAC authorize_execution denied principal=#{principal.id} reason=#{result[:reason]}")
    raise AccessDenied, result if enforcing?
  end

  result
end

.enabled?Boolean

Returns:

  • (Boolean)


76
77
78
79
80
# File 'lib/legion/rbac.rb', line 76

def enabled?
  return true unless defined?(Legion::Settings)

  Legion::Settings[:rbac]&.fetch(:enabled, true) != false
end

.enforcing?Boolean

Returns:

  • (Boolean)


82
83
84
85
86
# File 'lib/legion/rbac.rb', line 82

def enforcing?
  return true unless defined?(Legion::Settings)

  Legion::Settings[:rbac]&.fetch(:enforce, true) != false
end

.events_enabled?Boolean

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
# File 'lib/legion/rbac.rb', line 88

def events_enabled?
  return false unless defined?(Legion::Events)
  return false unless defined?(Legion::Settings)

  Legion::Settings[:rbac]&.fetch(:emit_events, true) != false
rescue StandardError
  false
end

.register_routesObject



47
48
49
50
51
52
53
54
# File 'lib/legion/rbac.rb', line 47

def register_routes
  return unless defined?(Legion::API) && Legion::API.respond_to?(:register_library_routes)

  Legion::API.register_library_routes('rbac', Legion::Rbac::Routes)
  log.debug 'Legion::Rbac routes registered with API'
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'rbac.register_routes')
end

.role_indexObject



43
44
45
# File 'lib/legion/rbac.rb', line 43

def role_index
  role_index_lock.synchronize { @role_index || EMPTY_ROLE_INDEX }
end

.setupObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/legion/rbac.rb', line 56

def setup
  log.info 'Legion::Rbac setup started'
  Legion::Settings.merge_settings(:rbac, Legion::Rbac::Settings.default)
  unless enabled?
    update_role_index(EMPTY_ROLE_INDEX, connected: false)
    log.info 'Legion::Rbac disabled via settings'
    return
  end

  loaded_roles = ConfigLoader.load_roles.freeze
  update_role_index(loaded_roles, connected: true)
  register_routes
  log.info "Legion::Rbac connected roles=#{loaded_roles.size}"
end

.shutdownObject



71
72
73
74
# File 'lib/legion/rbac.rb', line 71

def shutdown
  update_role_index(EMPTY_ROLE_INDEX, connected: false)
  log.info 'Legion::Rbac shutdown complete'
end