Module: Legate::Auth::ToolIntegration

Defined in:
lib/legate/auth/tool_integration.rb

Overview

Utility module for integrating authentication with tools. Provides methods for applying authentication to requests and detecting authentication errors in responses.

Class Method Summary collapse

Class Method Details

.apply_authentication(request, scheme, credential, token_store = nil, token_manager = nil) ⇒ Hash

Apply authentication to a request based on scheme and credential

Parameters:

Returns:

  • (Hash)

    The modified request with authentication applied

Raises:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
# File 'lib/legate/auth/tool_integration.rb', line 26

def apply_authentication(request, scheme, credential, token_store = nil, token_manager = nil)
  raise ArgumentError, 'Request must be a Hash' unless request.is_a?(Hash)
  raise ArgumentError, 'Scheme must be an Legate::Auth::Scheme' unless scheme.is_a?(Legate::Auth::Scheme)

  # If we have a token manager, use it for getting tokens
  if token_manager && token_manager.is_a?(Legate::Auth::TokenManager)
    # Get a token using the token manager
    token = token_manager.get_token(scheme, credential)

    # Use the token if available
    credential = token if token
  # Fall back to the old mechanism if token_manager not available
  elsif token_store && credential.is_a?(Legate::Auth::Credential)
    cache_key = generate_cache_key(scheme, credential)
    exchanged_credential = token_store.get(cache_key)

    if exchanged_credential
      # Check if token is expired and needs refresh
      if exchanged_credential.expired? && scheme.supports_refresh?
        begin
          # Try to refresh the token
          refreshed = scheme.refresh_token(exchanged_credential, credential)
          # Store refreshed token
          token_store.store(cache_key, refreshed)
          # Use the refreshed credential
          credential = refreshed
        rescue Legate::Auth::TokenRefreshError => e
          Legate.logger.warn("Failed to refresh token: #{e.message}. Using original credential.") if defined?(Legate.logger)
          # Fall back to original credential if refresh fails
        end
      else
        # Use cached credential
        credential = exchanged_credential
      end
    end
  end

  # Apply the credential to the request using the scheme
  begin
    scheme.apply_to_request(request, credential)
  rescue StandardError => e
    # Log the error but return the original request to allow the request to continue
    Legate.logger.error("Error applying authentication: #{e.message}") if defined?(Legate.logger)
    request
  end
end

.authentication_error?(response) ⇒ Boolean

Check if a response indicates an authentication error

Parameters:

  • response (Hash)

    The HTTP response to check

Returns:

  • (Boolean)

    True if the response indicates an authentication error



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/legate/auth/tool_integration.rb', line 76

def authentication_error?(response)
  return false unless response.is_a?(Hash)

  # Check for common authentication error status codes
  return true if [401, 403].include?(response[:status])

  # Check for common error messages in response body
  if response[:body] && response[:body].is_a?(String)
    body_lower = response[:body].downcase
    auth_error_indicators = [
      'unauthorized', 'not authorized', 'invalid token',
      'invalid api key', 'access denied', 'forbidden',
      'authentication failed'
    ]

    return true if auth_error_indicators.any? { |indicator| body_lower.include?(indicator) }
  end

  # Check for error responses with auth error messages in JSON
  if response[:body] && (response[:body].is_a?(Hash) ||
      (response[:body].is_a?(String) && response[:body].start_with?('{')))
    begin
      body = response[:body].is_a?(Hash) ? response[:body] : JSON.parse(response[:body])

      # Look for common error fields
      %w[error errors message].each do |field|
        next unless body[field]

        error_text = body[field].is_a?(String) ? body[field].downcase : body[field].to_s.downcase
        auth_error_indicators = [
          'unauthorized', 'not authorized', 'invalid token',
          'invalid api key', 'access denied', 'forbidden',
          'authentication failed'
        ]

        return true if auth_error_indicators.any? { |indicator| error_text.include?(indicator) }
      end
    rescue JSON::ParserError
      # Ignore parsing errors
    end
  end

  false
end

.generate_cache_key(scheme, credential) ⇒ String

Generate a cache key for storing/retrieving tokens

Parameters:

Returns:

  • (String)

    A unique cache key



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/legate/auth/tool_integration.rb', line 125

def generate_cache_key(scheme, credential)
  # Create a hash based on scheme type and relevant credential properties
  parts = [
    scheme.scheme_type.to_s,
    credential.auth_type.to_s
  ]

  # Add scheme-specific information
  case scheme.scheme_type
  when :api_key
    parts << credential[:api_key, resolve_env: false].to_s
  when :http_bearer
    parts << credential[:bearer_token, resolve_env: false].to_s
  when :oauth2, :oidc
    parts << credential[:client_id, resolve_env: false].to_s
    parts << credential[:scope, resolve_env: false].to_s
  when :service_account
    parts << credential[:client_email, resolve_env: false].to_s
  end

  # Create a unique key using a digest
  require 'digest/sha2'
  "auth_#{Digest::SHA256.hexdigest(parts.join(':'))}"
end

.requires_authentication?(request) ⇒ Boolean

Determine if a request requires authentication based on URL or headers

Parameters:

  • request (Hash)

    The request to check

Returns:

  • (Boolean)

    True if the request likely requires authentication



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/legate/auth/tool_integration.rb', line 153

def requires_authentication?(request)
  return false unless request.is_a?(Hash)

  # In test environments, always require authentication
  return true if request[:test_auth] == true

  # Check common indicators that a request requires authentication

  # 1. Check if path or URL contains auth-protected paths
  protected_paths = %w[
    /api/ /v1/ /v2/ /v3/ /private/ /user/ /admin/
    /account/ /secure/ /protected/ /internal/ /test/
  ]

  return true if request[:url] && protected_paths.any? { |path| request[:url].to_s.include?(path) }

  return true if request[:path] && protected_paths.any? { |path| request[:path].to_s.include?(path) }

  # 2. Check for Content-Type that often requires auth
  if request[:headers] && request[:headers]['Content-Type']
    auth_content_types = [
      'application/json', 'application/xml',
      'application/vnd.api+json'
    ]

    return true if auth_content_types.any? { |type| request[:headers]['Content-Type'].to_s.include?(type) }
  end

  # 3. Check for non-GET methods that typically require auth
  return true if request[:method] && !%w[GET HEAD OPTIONS].include?(request[:method].to_s.upcase)

  false
end