Module: Legion::Identity::Broker

Defined in:
lib/legion/identity/broker.rb

Constant Summary collapse

GROUPS_CACHE_TTL =
60
AUDIT_QUEUE_MAX =
1000
AUDIT_DROP_LOG_INTERVAL =
100

Class Method Summary collapse

Class Method Details

.authenticated?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/legion/identity/broker.rb', line 96

def authenticated?
  Identity::Process.resolved?
end

.credentials_available(provider_name) ⇒ Object



141
142
143
144
145
# File 'lib/legion/identity/broker.rb', line 141

def credentials_available(provider_name)
  name = provider_name.to_sym
  all_keys = (renewers.keys + static_leases.keys)
  all_keys.select { |k| k.first == name }.map(&:last).uniq
end

.credentials_for(provider_name, qualifier: nil, service: nil) ⇒ Object



40
41
42
43
44
45
46
47
# File 'lib/legion/identity/broker.rb', line 40

def credentials_for(provider_name, qualifier: nil, service: nil)
  name = provider_name.to_sym
  resolved = qualifier || default_qualifier_for(name)
  lease = lease_for(name, qualifier: resolved)
  return nil unless lease&.valid?

  { token: lease.token, provider: name, service: service, lease: lease }
end

.emailsObject



130
131
132
133
134
# File 'lib/legion/identity/broker.rb', line 130

def emails
  process_state = Identity::Process.identity_hash
   = process_state[:metadata] || {}
  Array([:emails])
end

.groupsObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/legion/identity/broker.rb', line 100

def groups
  cached = @groups_cache&.get
  return cached[:groups] if cached && (Time.now - cached[:fetched_at]) < GROUPS_CACHE_TTL

  if @groups_fetch_in_progress.make_true
    begin
      fetched = fetch_groups
      @groups_cache.set({ groups: fetched, fetched_at: Time.now })
      fetched
    ensure
      @groups_fetch_in_progress.make_false
    end
  else
    loop do
      current = @groups_cache&.get
      return current[:groups] if current

      break unless @groups_fetch_in_progress.true?

      sleep(0.01)
    end

    cached ? cached[:groups] : []
  end
end

.invalidate_groups_cache!Object



126
127
128
# File 'lib/legion/identity/broker.rb', line 126

def invalidate_groups_cache!
  @groups_cache.set(nil)
end

.lease_for(provider_name, qualifier: nil) ⇒ Object



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

def lease_for(provider_name, qualifier: nil)
  name = provider_name.to_sym
  resolved = qualifier || default_qualifier_for(name)
  key = [name, resolved].freeze

  renewer = renewers[key]
  return renewer.current_lease if renewer

  static_ref = static_leases[key]
  static_ref&.get
end

.leasesObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/legion/identity/broker.rb', line 147

def leases
  result = {}
  renewers.each do |key, renewer|
    provider_name, qualifier = key
    result[provider_name] ||= {}
    result[provider_name][qualifier] = renewer.current_lease&.to_h
  end
  static_leases.each do |key, ref|
    provider_name, qualifier = key
    result[provider_name] ||= {}
    result[provider_name][qualifier] = ref.get&.to_h unless result[provider_name].key?(qualifier)
  end
  result
end

.providersObject



136
137
138
139
# File 'lib/legion/identity/broker.rb', line 136

def providers
  all_keys = (renewers.keys + static_leases.keys)
  all_keys.map(&:first).uniq
end

.refresh_credential(provider_name, qualifier: nil) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/legion/identity/broker.rb', line 78

def refresh_credential(provider_name, qualifier: nil)
  name = provider_name.to_sym
  resolved = qualifier || default_qualifier_for(name)
  key = [name, resolved].freeze

  ref = static_leases[key]
  return false unless ref

  provider = provider_instances[name]
  return false unless provider.respond_to?(:provide_token)

  new_lease = provider.provide_token
  return false unless new_lease&.valid?

  ref.set(new_lease)
  true
end

.register_provider(provider_name, provider:, lease:, qualifier: :default, default: false) ⇒ Object



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
74
75
76
# File 'lib/legion/identity/broker.rb', line 49

def register_provider(provider_name, provider:, lease:, qualifier: :default, default: false)
  name = provider_name.to_sym
  qual = qualifier
  key = [name, qual].freeze

  # Set default qualifier: first registration or explicit default: true
  default_qualifiers[name] = qual if default || !default_qualifiers.key?(name)

  # Store provider instance (first-write-wins per provider name)
  provider_instances[name] ||= provider

  # Stop existing renewer for this specific tuple key
  renewers[key]&.stop!

  if lease&.expires_at.nil? && !lease&.renewable
    # Static credential — store without a background renewal thread
    renewers.delete(key)
    static_leases[key] = Concurrent::AtomicReference.new(lease)
  else
    # Dynamic credential — create LeaseRenewer
    static_leases.delete(key)
    renewers[key] = LeaseRenewer.new(
      provider_name: name,
      provider:      provider,
      lease:         lease
    )
  end
end

.renewer_for(provider_name, qualifier: nil) ⇒ Object



34
35
36
37
38
# File 'lib/legion/identity/broker.rb', line 34

def renewer_for(provider_name, qualifier: nil)
  name = provider_name.to_sym
  resolved = qualifier || default_qualifier_for(name)
  renewers[[name, resolved].freeze]
end

.reset!Object



175
176
177
178
179
180
181
182
183
# File 'lib/legion/identity/broker.rb', line 175

def reset!
  shutdown
  @groups_cache = Concurrent::AtomicReference.new(nil)
  @groups_fetch_in_progress = Concurrent::AtomicBoolean.new(false)
  @audit_queue = Concurrent::Array.new
  @audit_drops = Concurrent::AtomicFixnum.new(0)
  @audit_drainer = nil
  @audit_drainer_started = Concurrent::AtomicBoolean.new(false)
end

.shutdownObject



162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/legion/identity/broker.rb', line 162

def shutdown
  renewers.each_value do |r|
    r.stop!
  rescue Exception # rubocop:disable Lint/RescueException
    nil
  end
  renewers.clear
  static_leases.clear
  provider_instances.clear
  default_qualifiers.clear
  stop_audit_drainer
end

.token_for(provider_name, qualifier: nil, for_context: nil, purpose: nil, context: nil) ⇒ Object



13
14
15
16
17
18
19
20
# File 'lib/legion/identity/broker.rb', line 13

def token_for(provider_name, qualifier: nil, for_context: nil, purpose: nil, context: nil)
  name = provider_name.to_sym
  resolved = resolve_qualifier(name, qualifier: qualifier, for_context: for_context)
  lease = lease_for(name, qualifier: resolved)
  token = lease&.valid? ? lease.token : nil
  emit_audit(provider: name, qualifier: resolved, purpose: purpose, context: context, granted: !token.nil?)
  token
end