Class: Legate::Auth::Manager

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/legate/auth/manager.rb

Overview

The AuthManager is a singleton that manages authentication schemes and credentials. It provides a centralized registry for authentication schemes and credentials, as well as methods for finding the appropriate scheme and credential for a given URL.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeManager

Returns a new instance of Manager.



36
37
38
39
40
41
42
43
44
45
# File 'lib/legate/auth/manager.rb', line 36

def initialize
  @schemes = {}
  @credentials = {}
  @url_mappings = []
  @store = nil
  @loaded_from_store = false

  # Register built-in schemes
  register_default_schemes
end

Instance Attribute Details

#credentialsHash{Symbol => Legate::Auth::Credential} (readonly)

Returns:



32
33
34
# File 'lib/legate/auth/manager.rb', line 32

def credentials
  @credentials
end

#schemesHash{Symbol => Legate::Auth::Scheme} (readonly)

Read access to the registered schemes/credentials/url-mappings. Exposed so callers (e.g. the web routes) don’t reach in via instance_variable_get.

Returns:



30
31
32
# File 'lib/legate/auth/manager.rb', line 30

def schemes
  @schemes
end

#storeLegate::Auth::ManagerStore::RedisStore, ... (readonly)



25
26
27
# File 'lib/legate/auth/manager.rb', line 25

def store
  @store
end

#url_mappingsArray<Hash> (readonly)

Returns:

  • (Array<Hash>)


34
35
36
# File 'lib/legate/auth/manager.rb', line 34

def url_mappings
  @url_mappings
end

Instance Method Details

#find_scheme(scheme_type) ⇒ Legate::Auth::Scheme?

Find a scheme by type without requiring credentials

Parameters:

  • scheme_type (Symbol, String)

    The scheme type to find

Returns:



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/legate/auth/manager.rb', line 217

def find_scheme(scheme_type)
  scheme_sym = scheme_type.to_sym

  # Try to find by exact scheme_type match first
  scheme = @schemes.values.find { |s| s.scheme_type == scheme_sym }

  # If not found by scheme_type, try to find by registration name
  # This handles cases like :oidc -> OpenIDConnect where scheme_type is :openid_connect
  scheme ||= @schemes[scheme_sym]

  # Special mappings for backward compatibility and aliases
  if scheme.nil?
    scheme_mappings = {
      oidc: :openid_connect,
      openid_connect: :oidc
    }

    # Try the mapped scheme type
    mapped_type = scheme_mappings[scheme_sym]
    if mapped_type
      scheme = @schemes.values.find { |s| s.scheme_type == mapped_type } ||
               @schemes[mapped_type]
    end
  end

  scheme
end

#find_scheme_and_credential(url: nil, scheme_type: nil, credential_name: nil) ⇒ Array<Legate::Auth::Scheme, Legate::Auth::Credential>?

Find the appropriate scheme and credential for a URL

Parameters:

  • url (String) (defaults to: nil)

    The URL to find a scheme and credential for

  • scheme_type (Symbol, nil) (defaults to: nil)

    Optional scheme type to filter by

  • credential_name (Symbol, String, nil) (defaults to: nil)

    Optional credential name to use

Returns:



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/legate/auth/manager.rb', line 250

def find_scheme_and_credential(url: nil, scheme_type: nil, credential_name: nil)
  # Case 1: Direct credential and matching scheme specified
  if credential_name
    credential = get_credential(credential_name)
    return nil unless credential

    if scheme_type
      # Find scheme of the specified type
      scheme = @schemes.values.find { |s| s.scheme_type == scheme_type.to_sym }
      return [scheme, credential] if scheme
    else
      # Try to find a compatible scheme for this credential
      scheme = find_compatible_scheme(credential)
      return [scheme, credential] if scheme
    end
  end

  # Case 2: URL matching
  if url
    found_mapping = @url_mappings.find do |mapping|
      pattern = mapping[:pattern]

      # Check if URL matches the pattern
      (pattern.is_a?(Regexp) && url =~ pattern) ||
        (pattern.is_a?(String) && url.include?(pattern))
    end

    if found_mapping
      scheme = get_scheme(found_mapping[:scheme_name])
      credential = get_credential(found_mapping[:credential_name])

      # Skip if we're filtering by scheme_type and it doesn't match
      return nil if scheme_type && scheme.scheme_type != scheme_type.to_sym

      return [scheme, credential] if scheme && credential
    end

    # No matching URL mapping found, if a URL was provided and no mapping matched
    # but scheme_type was specified, we shouldn't continue to Case 3
    return nil if scheme_type && url
  end

  # Case 3: Just trying to find by scheme_type with any credential
  if scheme_type
    scheme_sym = scheme_type.to_sym

    # Try to find by exact scheme_type match first
    scheme = @schemes.values.find { |s| s.scheme_type == scheme_sym }

    # If not found by scheme_type, try to find by registration name
    # This handles cases like :oidc -> OpenIDConnect where scheme_type is :openid_connect
    scheme ||= @schemes[scheme_sym]

    # Special mappings for backward compatibility and aliases
    if scheme.nil?
      scheme_mappings = {
        oidc: :openid_connect,
        openid_connect: :oidc
      }

      # Try the mapped scheme type
      mapped_type = scheme_mappings[scheme_sym]
      if mapped_type
        scheme = @schemes.values.find { |s| s.scheme_type == mapped_type } ||
                 @schemes[mapped_type]
      end
    end

    return nil unless scheme

    # Find any compatible credential
    @credentials.each_value do |cred|
      return [scheme, cred] if credential_compatible_with_scheme?(cred, scheme)
    end
  end

  nil
end

#get_credential(name) ⇒ Legate::Auth::Credential?

Get a registered credential

Parameters:

  • name (Symbol, String)

    The name of the credential

Returns:



210
211
212
# File 'lib/legate/auth/manager.rb', line 210

def get_credential(name)
  @credentials[name.to_sym]
end

#get_scheme(name) ⇒ Legate::Auth::Scheme?

Get a registered scheme

Parameters:

  • name (Symbol, String)

    The name of the scheme

Returns:



203
204
205
# File 'lib/legate/auth/manager.rb', line 203

def get_scheme(name)
  @schemes[name.to_sym]
end

#load_from_storeBoolean

Load all schemes, credentials, and URL mappings from the store

Returns:

  • (Boolean)

    true if successful



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/legate/auth/manager.rb', line 57

def load_from_store
  return false unless @store&.available?
  return true if @loaded_from_store # Don't reload if already loaded

  Legate.logger&.info('Loading authentication configuration from store...')

  # Load credentials first (schemes might depend on them for URL mappings)
  load_credentials_from_store

  # Load schemes (will overwrite defaults with stored config)
  load_schemes_from_store

  # Load URL mappings
  load_url_mappings_from_store

  @loaded_from_store = true
  Legate.logger&.info('Authentication configuration loaded from store.')
  true
rescue StandardError => e
  Legate.logger&.error("Failed to load auth config from store: #{e.message}")
  false
end

#register_credential(credential, name, persist: true) ⇒ Symbol

Register a credential

Parameters:

  • credential (Legate::Auth::Credential)

    The credential to register

  • name (Symbol, String)

    The name to register the credential under

  • persist (Boolean) (defaults to: true)

    Whether to persist to store (default: true)

Returns:

  • (Symbol)

    The name the credential was registered under

Raises:

  • (ArgumentError)


124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/legate/auth/manager.rb', line 124

def register_credential(credential, name, persist: true)
  raise ArgumentError, 'Credential must be an Legate::Auth::Credential' unless credential.is_a?(Legate::Auth::Credential)
  raise ArgumentError, 'Name must be provided' if name.nil?

  name = name.to_sym
  @credentials[name] = credential

  # Persist to store if available and persistence is enabled
  @store&.save_credential(name, credential) if persist && @store&.available?

  name
end

#register_scheme(scheme, name = nil, persist: true) ⇒ Symbol

Register an authentication scheme

Parameters:

  • scheme (Legate::Auth::Scheme)

    The scheme to register

  • name (Symbol, String) (defaults to: nil)

    Optional name for the scheme (defaults to scheme type)

  • persist (Boolean) (defaults to: true)

    Whether to persist to store (default: true)

Returns:

  • (Symbol)

    The name the scheme was registered under

Raises:

  • (ArgumentError)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/legate/auth/manager.rb', line 91

def register_scheme(scheme, name = nil, persist: true)
  raise ArgumentError, 'Scheme must be an Legate::Auth::Scheme' unless scheme.is_a?(Legate::Auth::Scheme)

  # Use scheme type as name if not provided
  name ||= scheme.scheme_type
  name = name.to_sym

  @schemes[name] = scheme

  # Persist to store if available and persistence is enabled
  @store&.save_scheme(name, scheme) if persist && @store&.available?

  name
end

#register_url_mapping(url_pattern, scheme_name, credential_name, persist: true) ⇒ Object

Register a URL mapping to a scheme and credential

Parameters:

  • url_pattern (String, Regexp)

    The URL pattern to match

  • scheme_name (Symbol, String)

    The name of the scheme to use

  • credential_name (Symbol, String)

    The name of the credential to use

  • persist (Boolean) (defaults to: true)

    Whether to persist to store (default: true)

Raises:

  • (ArgumentError)


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/legate/auth/manager.rb', line 155

def register_url_mapping(url_pattern, scheme_name, credential_name, persist: true)
  scheme_name = scheme_name.to_sym
  credential_name = credential_name.to_sym

  raise ArgumentError, "Unknown scheme: #{scheme_name}" unless @schemes.key?(scheme_name)

  raise ArgumentError, "Unknown credential: #{credential_name}" unless @credentials.key?(credential_name)

  @url_mappings << {
    pattern: url_pattern,
    scheme_name: scheme_name,
    credential_name: credential_name
  }

  # Persist all URL mappings to store
  @store&.save_url_mappings(@url_mappings) if persist && @store&.available?
end

#reload_from_storeObject

Force reload from store (useful after external changes)



81
82
83
84
# File 'lib/legate/auth/manager.rb', line 81

def reload_from_store
  @loaded_from_store = false
  load_from_store
end

#remove_url_mapping(index, persist: true) ⇒ Boolean

Remove a URL mapping by index

Parameters:

  • index (Integer)

    The index of the mapping to remove

  • persist (Boolean) (defaults to: true)

    Whether to persist the deletion (default: true)

Returns:

  • (Boolean)

    true if removed



177
178
179
180
181
182
183
184
185
186
# File 'lib/legate/auth/manager.rb', line 177

def remove_url_mapping(index, persist: true)
  return false if index < 0 || index >= @url_mappings.size

  @url_mappings.delete_at(index)

  # Persist all URL mappings to store
  @store&.save_url_mappings(@url_mappings) if persist && @store&.available?

  true
end

#replace_url_mappings(mappings, persist: true) ⇒ Array<Hash>

Replaces the full set of URL mappings and persists them. Used by the web UI, which manages a richer mapping shape (pattern/priority/active) than register_url_mapping and edits the array in place before saving.

Parameters:

  • mappings (Array<Hash>)

    the new mapping set

  • persist (Boolean) (defaults to: true)

    whether to persist (default: true)

Returns:

  • (Array<Hash>)

    the stored mappings



194
195
196
197
198
# File 'lib/legate/auth/manager.rb', line 194

def replace_url_mappings(mappings, persist: true)
  @url_mappings = mappings
  @store&.save_url_mappings(@url_mappings) if persist && @store&.available?
  @url_mappings
end

#set_store(store, load_immediately: true) ⇒ Object

Set the persistence store and load existing data

Parameters:



50
51
52
53
# File 'lib/legate/auth/manager.rb', line 50

def set_store(store, load_immediately: true)
  @store = store
  load_from_store if load_immediately && store&.available?
end

#unregister_credential(name, persist: true) ⇒ Boolean

Unregister/delete a credential

Parameters:

  • name (Symbol, String)

    The credential name

  • persist (Boolean) (defaults to: true)

    Whether to persist the deletion (default: true)

Returns:

  • (Boolean)

    true if deleted



141
142
143
144
145
146
147
148
# File 'lib/legate/auth/manager.rb', line 141

def unregister_credential(name, persist: true)
  name = name.to_sym
  deleted = @credentials.delete(name)

  @store&.delete_credential(name) if persist && deleted && @store&.available?

  !deleted.nil?
end

#unregister_scheme(name, persist: true) ⇒ Boolean

Unregister/delete a scheme

Parameters:

  • name (Symbol, String)

    The scheme name

  • persist (Boolean) (defaults to: true)

    Whether to persist the deletion (default: true)

Returns:

  • (Boolean)

    true if deleted



110
111
112
113
114
115
116
117
# File 'lib/legate/auth/manager.rb', line 110

def unregister_scheme(name, persist: true)
  name = name.to_sym
  deleted = @schemes.delete(name)

  @store&.delete_scheme(name) if persist && deleted && @store&.available?

  !deleted.nil?
end