Class: Legion::Identity::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/identity/request.rb

Constant Summary collapse

SOURCE_NORMALIZATION =

Maps middleware-emitted source values to the canonical credential enum. :local is emitted by Middleware#system_principal for unauthenticated loopback requests and must normalize to :system to maintain audit trail consistency. :jwt is intentionally kept distinct — JWT is the transport, not the provider. Entra-specific identification requires issuer inspection (Phase 7 concern).

{
  api_key:  :api,
  jwt:      :jwt,
  kerberos: :kerberos,
  local:    :system,
  system:   :system
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(principal_id:, canonical_name:, kind:, groups: [], roles: [], source: nil, metadata: {}) ⇒ Request

rubocop:disable Metrics/ParameterLists



23
24
25
26
27
28
29
30
31
32
# File 'lib/legion/identity/request.rb', line 23

def initialize(principal_id:, canonical_name:, kind:, groups: [], roles: [], source: nil, metadata: {}) # rubocop:disable Metrics/ParameterLists
  @principal_id   = principal_id
  @canonical_name = canonical_name
  @kind           = kind
  @groups         = groups.freeze
  @roles          = roles.freeze
  @source         = SOURCE_NORMALIZATION.fetch(source&.to_sym, source)
  @metadata       = .freeze
  freeze
end

Instance Attribute Details

#canonical_nameObject (readonly)

Returns the value of attribute canonical_name.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def canonical_name
  @canonical_name
end

#groupsObject (readonly)

Returns the value of attribute groups.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def groups
  @groups
end

#kindObject (readonly)

Returns the value of attribute kind.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def kind
  @kind
end

#metadataObject (readonly)

Returns the value of attribute metadata.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def 
  @metadata
end

#principal_idObject (readonly) Also known as: id

Returns the value of attribute principal_id.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def principal_id
  @principal_id
end

#rolesObject (readonly)

Returns the value of attribute roles.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def roles
  @roles
end

#sourceObject (readonly)

Returns the value of attribute source.



19
20
21
# File 'lib/legion/identity/request.rb', line 19

def source
  @source
end

Class Method Details

.from_auth_context(claims_hash) ⇒ Object

Builds a Request from a parsed auth claims hash with symbol keys:

{ sub:, name:, preferred_username:, kind:, groups:, resolved_roles:, source: }

resolved_roles is the final merged set of Entra app roles + group-derived RBAC roles (populated by Identity::Middleware before calling this method). The source value is normalized via SOURCE_NORMALIZATION at construction time.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/legion/identity/request.rb', line 45

def self.from_auth_context(claims_hash)
  raw_name = claims_hash[:name] || claims_hash[:preferred_username] || ''
  canonical = raw_name.to_s.strip.downcase.gsub('.', '-')
  raw_source = claims_hash[:source]&.to_sym
  normalized_source = SOURCE_NORMALIZATION.fetch(raw_source, raw_source)

  new(
    principal_id:   claims_hash[:sub],
    canonical_name: canonical,
    kind:           claims_hash[:kind] || :human,
    groups:         claims_hash[:groups] || [],
    roles:          Array(claims_hash[:resolved_roles]),
    source:         normalized_source
  )
end

.from_env(env) ⇒ Object

Reads the already-resolved identity from the Rack env (set by middleware). Returns nil when the key is absent.



36
37
38
# File 'lib/legion/identity/request.rb', line 36

def self.from_env(env)
  env['legion.principal']
end

Instance Method Details

#identity_hashObject



61
62
63
64
65
66
67
68
69
70
# File 'lib/legion/identity/request.rb', line 61

def identity_hash
  {
    principal_id:   principal_id,
    canonical_name: canonical_name,
    kind:           kind,
    groups:         groups,
    roles:          roles,
    source:         source
  }
end

#to_caller_hashObject

Pipeline-compatible caller hash (matches legion-llm pipeline format).



82
83
84
85
86
87
88
89
90
91
# File 'lib/legion/identity/request.rb', line 82

def to_caller_hash
  {
    requested_by: {
      id:         principal_id,
      identity:   canonical_name,
      type:       kind,
      credential: source
    }
  }
end

#to_rbac_principalObject

Maps to RBAC principal format. :service workers are represented as :worker in RBAC.



74
75
76
77
78
79
# File 'lib/legion/identity/request.rb', line 74

def to_rbac_principal
  {
    identity: canonical_name,
    type:     kind == :service ? :worker : kind
  }
end