legate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +345 -0
- data/bin/legate +13 -0
- data/examples/00_quickstart.rb +51 -0
- data/examples/01_simple_agent.rb +105 -0
- data/examples/02_multi_tool_agent.rb +140 -0
- data/examples/03_custom_tool.rb +93 -0
- data/examples/04_agent_instructions.rb +84 -0
- data/examples/05_state_and_sessions.rb +91 -0
- data/examples/06_callbacks.rb +186 -0
- data/examples/07_async_jobs.rb +112 -0
- data/examples/08_loop_agent.rb +197 -0
- data/examples/09_sequential_workflow.rb +40 -0
- data/examples/10_parallel_workflow.rb +34 -0
- data/examples/11_agent_delegation.rb +24 -0
- data/examples/12_http_client_tool.rb +156 -0
- data/examples/13_authentication.rb +220 -0
- data/examples/14_mcp_client.rb +154 -0
- data/examples/15_mcp_server.rb +79 -0
- data/examples/16_webhooks.rb +91 -0
- data/examples/README_sequential_agents.md +164 -0
- data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
- data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
- data/examples/advanced/auth/excon_middleware.rb +317 -0
- data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
- data/examples/advanced/auth/fiber_auth_example.rb +281 -0
- data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
- data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
- data/examples/advanced/auth/oauth2_auth.rb +419 -0
- data/examples/advanced/auth/oidc_auth.rb +514 -0
- data/examples/advanced/auth/openweather_api.rb +251 -0
- data/examples/advanced/auth/openweather_tool.rb +153 -0
- data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
- data/examples/advanced/auth/service_account.rb +135 -0
- data/examples/advanced/auth/test_with_httpbin.rb +202 -0
- data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
- data/examples/advanced/callback_monitoring.rb +679 -0
- data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
- data/examples/advanced/mas/loop_workflow.rb +28 -0
- data/examples/advanced/mas/mock_planner.rb +77 -0
- data/examples/advanced/mas/proper_delegation_example.rb +276 -0
- data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
- data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
- data/examples/advanced/mcp/mcp_server_async.rb +76 -0
- data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
- data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
- data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
- data/examples/advanced/random_calculator.rb +104 -0
- data/examples/advanced/sleep_agent.rb +153 -0
- data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
- data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
- data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
- data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
- data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
- data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
- data/examples/tools/oauth2_example.rb +136 -0
- data/examples/tools/sleepy_tool.rb +42 -0
- data/lib/legate/activity_log.rb +71 -0
- data/lib/legate/agent.rb +959 -0
- data/lib/legate/agent_code_generator.rb +185 -0
- data/lib/legate/agent_definition.rb +812 -0
- data/lib/legate/agentic/decision.rb +49 -0
- data/lib/legate/agentic/loop.rb +134 -0
- data/lib/legate/agentic.rb +5 -0
- data/lib/legate/agents/loop_agent.rb +248 -0
- data/lib/legate/agents/parallel_agent.rb +163 -0
- data/lib/legate/agents/sequential_agent.rb +190 -0
- data/lib/legate/agents.rb +14 -0
- data/lib/legate/auth/config.rb +148 -0
- data/lib/legate/auth/coordinator.rb +218 -0
- data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
- data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
- data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
- data/lib/legate/auth/credential.rb +157 -0
- data/lib/legate/auth/encryption.rb +108 -0
- data/lib/legate/auth/error.rb +94 -0
- data/lib/legate/auth/exchanged_credential.rb +180 -0
- data/lib/legate/auth/excon_middleware.rb +285 -0
- data/lib/legate/auth/http_client_utils.rb +364 -0
- data/lib/legate/auth/manager.rb +531 -0
- data/lib/legate/auth/manager_store.rb +394 -0
- data/lib/legate/auth/middleware_factory.rb +290 -0
- data/lib/legate/auth/runner.rb +279 -0
- data/lib/legate/auth/scheme.rb +125 -0
- data/lib/legate/auth/schemes/api_key.rb +212 -0
- data/lib/legate/auth/schemes/google_service_account.rb +108 -0
- data/lib/legate/auth/schemes/http_bearer.rb +98 -0
- data/lib/legate/auth/schemes/oauth2.rb +396 -0
- data/lib/legate/auth/schemes/openid_connect.rb +346 -0
- data/lib/legate/auth/schemes/service_account.rb +388 -0
- data/lib/legate/auth/schemes.rb +40 -0
- data/lib/legate/auth/token_manager.rb +362 -0
- data/lib/legate/auth/token_store.rb +86 -0
- data/lib/legate/auth/tool_context_extension.rb +97 -0
- data/lib/legate/auth/tool_integration.rb +188 -0
- data/lib/legate/auth/url_guard.rb +81 -0
- data/lib/legate/auth.rb +453 -0
- data/lib/legate/callbacks/callback_context.rb +71 -0
- data/lib/legate/cli/agent_commands.rb +950 -0
- data/lib/legate/cli/auth_commands.rb +520 -0
- data/lib/legate/cli/base_command.rb +24 -0
- data/lib/legate/cli/deployment_commands.rb +934 -0
- data/lib/legate/cli/output_helper.rb +108 -0
- data/lib/legate/cli/session_commands.rb +138 -0
- data/lib/legate/cli/skaffold_commands.rb +223 -0
- data/lib/legate/cli/tool_commands.rb +261 -0
- data/lib/legate/cli/web_commands.rb +182 -0
- data/lib/legate/cli.rb +40 -0
- data/lib/legate/configuration/webhooks.rb +113 -0
- data/lib/legate/configuration.rb +39 -0
- data/lib/legate/definition_store.rb +23 -0
- data/lib/legate/errors.rb +118 -0
- data/lib/legate/event.rb +161 -0
- data/lib/legate/gemini_ai_beta_patch.rb +39 -0
- data/lib/legate/generators/agent_generator.rb +412 -0
- data/lib/legate/generators/code_validator.rb +48 -0
- data/lib/legate/generators/legate/install_generator.rb +35 -0
- data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
- data/lib/legate/generators/legate/templates/initializer.rb +18 -0
- data/lib/legate/generators/runtime_tool_loader.rb +76 -0
- data/lib/legate/generators/tool_generator.rb +408 -0
- data/lib/legate/generators.rb +11 -0
- data/lib/legate/global_definition_registry.rb +506 -0
- data/lib/legate/global_tool_manager.rb +135 -0
- data/lib/legate/llm/adapter.rb +69 -0
- data/lib/legate/llm/gemini.rb +172 -0
- data/lib/legate/llm/ollama.rb +80 -0
- data/lib/legate/llm.rb +34 -0
- data/lib/legate/mcp/client.rb +320 -0
- data/lib/legate/mcp/connection/sse.rb +292 -0
- data/lib/legate/mcp/connection/stdio.rb +273 -0
- data/lib/legate/mcp/connection_manager.rb +103 -0
- data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
- data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
- data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
- data/lib/legate/mcp/tool_wrapper.rb +138 -0
- data/lib/legate/mcp/util/schema_converter.rb +134 -0
- data/lib/legate/mcp.rb +23 -0
- data/lib/legate/plan_executor.rb +375 -0
- data/lib/legate/planner.rb +839 -0
- data/lib/legate/rails/railtie.rb +43 -0
- data/lib/legate/rails.rb +9 -0
- data/lib/legate/redaction.rb +32 -0
- data/lib/legate/session.rb +299 -0
- data/lib/legate/session_service/active_record.rb +300 -0
- data/lib/legate/session_service/base.rb +68 -0
- data/lib/legate/session_service/event_broadcast.rb +74 -0
- data/lib/legate/session_service/in_memory.rb +188 -0
- data/lib/legate/tool/metadata_dsl.rb +122 -0
- data/lib/legate/tool.rb +276 -0
- data/lib/legate/tool_code_generator.rb +103 -0
- data/lib/legate/tool_context.rb +350 -0
- data/lib/legate/tool_loader.rb +39 -0
- data/lib/legate/tool_registry.rb +73 -0
- data/lib/legate/tool_result.rb +61 -0
- data/lib/legate/tools/agent_tool.rb +187 -0
- data/lib/legate/tools/base/http_client.rb +319 -0
- data/lib/legate/tools/base/safe_url.rb +56 -0
- data/lib/legate/tools/base_async_job_tool.rb +91 -0
- data/lib/legate/tools/calculator.rb +89 -0
- data/lib/legate/tools/cat_facts.rb +81 -0
- data/lib/legate/tools/check_job_status_tool.rb +48 -0
- data/lib/legate/tools/current_time_tool.rb +64 -0
- data/lib/legate/tools/echo.rb +43 -0
- data/lib/legate/tools/http_request_tool.rb +105 -0
- data/lib/legate/tools/random_number_tool.rb +64 -0
- data/lib/legate/tools/read_webpage_tool.rb +92 -0
- data/lib/legate/tools/sleepy_tool.rb +74 -0
- data/lib/legate/tools/webhook_tool.rb +146 -0
- data/lib/legate/version.rb +5 -0
- data/lib/legate/web/app.rb +984 -0
- data/lib/legate/web/public/css/main.css +4980 -0
- data/lib/legate/web/public/images/favicon-256.png +0 -0
- data/lib/legate/web/public/images/favicon-32.png +0 -0
- data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
- data/lib/legate/web/public/images/legate-logo-light.png +0 -0
- data/lib/legate/web/public/js/legate.js +616 -0
- data/lib/legate/web/public/styles/main.scss +4402 -0
- data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
- data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
- data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
- data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
- data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
- data/lib/legate/web/routes/api_routes.rb +56 -0
- data/lib/legate/web/routes/authentication_routes.rb +1541 -0
- data/lib/legate/web/routes/core_routes.rb +111 -0
- data/lib/legate/web/routes/documentation_routes.rb +220 -0
- data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
- data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
- data/lib/legate/web/sass_compiler.rb +73 -0
- data/lib/legate/web/views/_active_session_info.slim +25 -0
- data/lib/legate/web/views/_activity_list.slim +55 -0
- data/lib/legate/web/views/_agent_card.slim +56 -0
- data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
- data/lib/legate/web/views/_agent_status_controls.slim +71 -0
- data/lib/legate/web/views/_agent_tool_table.slim +74 -0
- data/lib/legate/web/views/_chat_message.slim +95 -0
- data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
- data/lib/legate/web/views/_display_agent_description.slim +11 -0
- data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
- data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
- data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
- data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
- data/lib/legate/web/views/_display_agent_model.slim +17 -0
- data/lib/legate/web/views/_display_agent_name.slim +42 -0
- data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
- data/lib/legate/web/views/_display_agent_type.slim +65 -0
- data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
- data/lib/legate/web/views/_edit_agent_description.slim +16 -0
- data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
- data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
- data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
- data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
- data/lib/legate/web/views/_edit_agent_model.slim +23 -0
- data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
- data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
- data/lib/legate/web/views/_edit_agent_type.slim +67 -0
- data/lib/legate/web/views/_session_error.slim +4 -0
- data/lib/legate/web/views/_skeleton.slim +69 -0
- data/lib/legate/web/views/_tool_card.slim +9 -0
- data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
- data/lib/legate/web/views/agent.slim +436 -0
- data/lib/legate/web/views/agent_auth.slim +562 -0
- data/lib/legate/web/views/agents.slim +369 -0
- data/lib/legate/web/views/auth.slim +112 -0
- data/lib/legate/web/views/auth_credential_detail.slim +327 -0
- data/lib/legate/web/views/auth_credentials.slim +261 -0
- data/lib/legate/web/views/auth_debug.slim +94 -0
- data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
- data/lib/legate/web/views/auth_mapping_new.slim +123 -0
- data/lib/legate/web/views/auth_mappings.slim +120 -0
- data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
- data/lib/legate/web/views/auth_schemes.slim +259 -0
- data/lib/legate/web/views/auth_test.slim +418 -0
- data/lib/legate/web/views/chat.slim +192 -0
- data/lib/legate/web/views/docs_index.slim +105 -0
- data/lib/legate/web/views/docs_show.slim +105 -0
- data/lib/legate/web/views/error_404.slim +5 -0
- data/lib/legate/web/views/index.slim +148 -0
- data/lib/legate/web/views/layout.slim +144 -0
- data/lib/legate/web/views/tool_detail.slim +87 -0
- data/lib/legate/web/views/tools.slim +50 -0
- data/lib/legate/web/webhook_listener.rb +367 -0
- data/lib/legate/web.rb +9 -0
- data/lib/legate.rb +220 -0
- data/public/docs/advanced/callbacks.md +828 -0
- data/public/docs/advanced/mcp_schema_conversion.md +59 -0
- data/public/docs/authentication/api_reference/config.md +210 -0
- data/public/docs/authentication/api_reference/credential.md +246 -0
- data/public/docs/authentication/api_reference/encryption.md +218 -0
- data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
- data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
- data/public/docs/authentication/api_reference/index.md +30 -0
- data/public/docs/authentication/api_reference/scheme.md +250 -0
- data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
- data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
- data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
- data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
- data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
- data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
- data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
- data/public/docs/authentication/api_reference/token_manager.md +221 -0
- data/public/docs/authentication/api_reference/token_store.md +146 -0
- data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
- data/public/docs/authentication/guides/api_key.md +190 -0
- data/public/docs/authentication/guides/bearer.md +172 -0
- data/public/docs/authentication/guides/configuration.md +255 -0
- data/public/docs/authentication/guides/custom_flow.md +523 -0
- data/public/docs/authentication/guides/index.md +24 -0
- data/public/docs/authentication/guides/migration.md +435 -0
- data/public/docs/authentication/guides/oauth2.md +252 -0
- data/public/docs/authentication/guides/oidc.md +241 -0
- data/public/docs/authentication/guides/overview.md +155 -0
- data/public/docs/authentication/guides/secure_storage.md +301 -0
- data/public/docs/authentication/guides/service_account.md +228 -0
- data/public/docs/authentication/guides/token_lifecycle.md +295 -0
- data/public/docs/authentication/guides/web_ui_integration.md +504 -0
- data/public/docs/authentication/index.md +58 -0
- data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
- data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
- data/public/docs/authentication/troubleshooting/index.md +11 -0
- data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
- data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
- data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
- data/public/docs/cli/legate_cli_usage.md +363 -0
- data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
- data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
- data/public/docs/core_concepts/legate_configuration.md +116 -0
- data/public/docs/core_concepts/legate_definition_store.md +102 -0
- data/public/docs/core_concepts/legate_planner.md +94 -0
- data/public/docs/core_concepts/legate_session_service.md +104 -0
- data/public/docs/error_handling/legate_error_handling.md +122 -0
- data/public/docs/examples.md +199 -0
- data/public/docs/getting_started.md +111 -0
- data/public/docs/guides/agentic_agents.md +137 -0
- data/public/docs/guides/ai_code_generators.md +437 -0
- data/public/docs/guides/auto_loading.md +326 -0
- data/public/docs/guides/configuring_agent_webhooks.md +219 -0
- data/public/docs/guides/http_client_usage.md +264 -0
- data/public/docs/guides/llm_providers.md +137 -0
- data/public/docs/guides/mcp_client_integration.md +232 -0
- data/public/docs/guides/mcp_server_exposure.md +206 -0
- data/public/docs/guides/rails_integration.md +128 -0
- data/public/docs/guides/sending_outbound_webhooks.md +227 -0
- data/public/docs/guides/streaming.md +112 -0
- data/public/docs/guides/webhooks.md +288 -0
- data/public/docs/introduction.md +51 -0
- data/public/docs/multi_agent_systems/advanced_features.md +57 -0
- data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
- data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
- data/public/docs/multi_agent_systems/state_management.md +47 -0
- data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
- data/public/docs/tools/legate_built_in_tools.md +332 -0
- data/public/docs/tools/legate_tools_and_registry.md +263 -0
- data/public/docs/web_ui/legate_web_ui.md +137 -0
- metadata +823 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../coordinator'
|
|
4
|
+
require_relative '../schemes/openid_connect'
|
|
5
|
+
require_relative 'oauth2_coordinator'
|
|
6
|
+
|
|
7
|
+
module Legate
|
|
8
|
+
module Auth
|
|
9
|
+
module Coordinators
|
|
10
|
+
# OIDCCoordinator handles the interactive OpenID Connect authentication flow using fibers.
|
|
11
|
+
# It extends the OAuth2Coordinator with OIDC-specific functionality.
|
|
12
|
+
class OIDCCoordinator < OAuth2Coordinator
|
|
13
|
+
# Initialize a new OIDC coordinator
|
|
14
|
+
# @param scheme [Legate::Auth::Schemes::OpenIDConnect] The OIDC scheme
|
|
15
|
+
# @param credential [Legate::Auth::Credential] The credential with client information
|
|
16
|
+
# @param session_service [Legate::SessionService::Base] The session service
|
|
17
|
+
# @param token_store [Legate::Auth::TokenStore, nil] Optional token store
|
|
18
|
+
# @param timeout [Integer, nil] Optional timeout in seconds
|
|
19
|
+
# @param redirect_uri [String, nil] Optional redirect URI
|
|
20
|
+
def initialize(scheme:, credential:, session_service:, token_store: nil, timeout: DEFAULT_TIMEOUT, redirect_uri: nil)
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
raise ArgumentError, "Expected an OIDC scheme, got #{scheme.class}" unless scheme.is_a?(Legate::Auth::Schemes::OIDC)
|
|
24
|
+
|
|
25
|
+
return if credential.auth_type == :oidc
|
|
26
|
+
# Allow OAuth2 credentials as they are compatible
|
|
27
|
+
return if credential.auth_type == :oauth2
|
|
28
|
+
|
|
29
|
+
raise ArgumentError, "Credential must have auth_type :oidc or :oauth2, got #{credential.auth_type}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
# Implement the OIDC authentication flow, extending the OAuth2 flow
|
|
35
|
+
# @return [Legate::Auth::ExchangedCredential] The authenticated credential
|
|
36
|
+
# @raise [Legate::Auth::Error] If authentication fails
|
|
37
|
+
def authenticate
|
|
38
|
+
# Call the parent (OAuth2) authentication flow first
|
|
39
|
+
oauth2_result = super
|
|
40
|
+
|
|
41
|
+
# For OIDC, we may want to request the userinfo endpoint
|
|
42
|
+
# if the ID token doesn't have all needed claims
|
|
43
|
+
if @scheme.should_fetch_userinfo? && oauth2_result
|
|
44
|
+
fetch_userinfo(oauth2_result)
|
|
45
|
+
else
|
|
46
|
+
oauth2_result
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# Fetch additional user information using the userinfo endpoint
|
|
53
|
+
# @param token [Legate::Auth::ExchangedCredential] The token with access_token
|
|
54
|
+
# @return [Legate::Auth::ExchangedCredential] The token with added userinfo
|
|
55
|
+
# @raise [Legate::Auth::Error] If userinfo fetch fails
|
|
56
|
+
def fetch_userinfo(token)
|
|
57
|
+
# Fetch userinfo using the access token
|
|
58
|
+
userinfo = @scheme.fetch_userinfo(token)
|
|
59
|
+
|
|
60
|
+
# Add the userinfo to the token metadata
|
|
61
|
+
token.with(
|
|
62
|
+
metadata: (token.metadata || {}).merge(userinfo: userinfo)
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../coordinator'
|
|
4
|
+
require_relative '../schemes/service_account'
|
|
5
|
+
require_relative '../config'
|
|
6
|
+
|
|
7
|
+
module Legate
|
|
8
|
+
module Auth
|
|
9
|
+
module Coordinators
|
|
10
|
+
# ServiceAccountCoordinator handles non-interactive service account authentication
|
|
11
|
+
# with automatic token exchange and refresh. Unlike OAuth2 coordinators, service
|
|
12
|
+
# account authentication does not require user interaction.
|
|
13
|
+
class ServiceAccountCoordinator < Coordinator
|
|
14
|
+
# Authentication steps for Service Accounts
|
|
15
|
+
module Steps
|
|
16
|
+
TOKEN_EXCHANGE = :token_exchange
|
|
17
|
+
TOKEN_REFRESH = :token_refresh
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Initialize a new Service Account coordinator
|
|
21
|
+
# @param scheme [Legate::Auth::Schemes::ServiceAccount] The Service Account scheme
|
|
22
|
+
# @param credential [Legate::Auth::Credential] The credential with service account information
|
|
23
|
+
# @param session_service [Legate::SessionService::Base] The session service
|
|
24
|
+
# @param token_store [Legate::Auth::TokenStore, nil] Optional token store
|
|
25
|
+
# @param timeout [Integer, nil] Optional timeout in seconds
|
|
26
|
+
def initialize(scheme:, credential:, session_service:, token_store: nil, timeout: DEFAULT_TIMEOUT)
|
|
27
|
+
super(scheme: scheme, credential: credential, session_service: session_service, token_store: token_store, timeout: timeout)
|
|
28
|
+
|
|
29
|
+
raise ArgumentError, "Expected a ServiceAccount scheme, got #{scheme.class}" unless scheme.is_a?(Legate::Auth::Schemes::ServiceAccount)
|
|
30
|
+
|
|
31
|
+
raise ArgumentError, "Credential must have auth_type :service_account, got #{credential.auth_type}" unless credential.auth_type.to_sym == :service_account
|
|
32
|
+
|
|
33
|
+
@current_step = Steps::TOKEN_EXCHANGE
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
|
|
38
|
+
# Implement the Service Account authentication flow
|
|
39
|
+
# @return [Legate::Auth::ExchangedCredential] The authenticated credential
|
|
40
|
+
# @raise [Legate::Auth::Error] If authentication fails
|
|
41
|
+
def authenticate
|
|
42
|
+
# Service account authentication is non-interactive, so we just exchange tokens
|
|
43
|
+
@current_step = Steps::TOKEN_EXCHANGE
|
|
44
|
+
exchanged_token = exchange_token
|
|
45
|
+
|
|
46
|
+
# Store the token if we have a token store
|
|
47
|
+
store_token(exchanged_token) if @token_store
|
|
48
|
+
|
|
49
|
+
exchanged_token
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Refresh an existing token
|
|
53
|
+
# @param token [Legate::Auth::ExchangedCredential] The token to refresh
|
|
54
|
+
# @return [Legate::Auth::ExchangedCredential] The refreshed token
|
|
55
|
+
# @raise [Legate::Auth::TokenRefreshError] If token refresh fails
|
|
56
|
+
def refresh(token)
|
|
57
|
+
@current_step = Steps::TOKEN_REFRESH
|
|
58
|
+
|
|
59
|
+
# For service accounts, we get a new token rather than refreshing the existing one
|
|
60
|
+
refreshed_token = @scheme.refresh_token(token, @credential)
|
|
61
|
+
|
|
62
|
+
# Store the refreshed token if we have a token store
|
|
63
|
+
store_token(refreshed_token) if @token_store
|
|
64
|
+
|
|
65
|
+
refreshed_token
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Exchange for a token
|
|
71
|
+
# @return [Legate::Auth::ExchangedCredential] The exchanged token
|
|
72
|
+
# @raise [Legate::Auth::TokenExchangeError] If token exchange fails
|
|
73
|
+
def exchange_token
|
|
74
|
+
# Create a config for token exchange, if needed by the scheme
|
|
75
|
+
auth_config = Legate::Auth::Config.new(
|
|
76
|
+
scheme: @scheme,
|
|
77
|
+
credential: @credential,
|
|
78
|
+
options: { request_id: @request_id }
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Exchange for tokens using the scheme
|
|
82
|
+
# Some service account implementations might need the config, others might not
|
|
83
|
+
if @scheme.method(:exchange_token).arity == 2
|
|
84
|
+
@scheme.exchange_token(auth_config, @credential)
|
|
85
|
+
else
|
|
86
|
+
@scheme.exchange_token(@credential)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Store a token in the token store
|
|
91
|
+
# @param token [Legate::Auth::ExchangedCredential] The token to store
|
|
92
|
+
def store_token(token)
|
|
93
|
+
return unless @token_store && token
|
|
94
|
+
|
|
95
|
+
# Generate a key for the token
|
|
96
|
+
key = generate_token_key
|
|
97
|
+
|
|
98
|
+
# Store the token with the generated key
|
|
99
|
+
@token_store.store(key, token)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Generate a key for storing the token
|
|
103
|
+
# @return [String] The generated key
|
|
104
|
+
def generate_token_key
|
|
105
|
+
# Create a base key using the scheme type, client email, and scopes
|
|
106
|
+
base_key = "#{@scheme.scheme_type}"
|
|
107
|
+
|
|
108
|
+
# Add client email if available
|
|
109
|
+
base_key += ":#{@credential[:client_email]}" if @credential[:client_email]
|
|
110
|
+
|
|
111
|
+
# Add scopes if available
|
|
112
|
+
base_key += ":#{@scheme.scopes.join(',')}" if @scheme.scopes && !@scheme.scopes.empty?
|
|
113
|
+
|
|
114
|
+
# Add audience if available and scopes aren't set
|
|
115
|
+
base_key += ":#{@scheme.audience}" if @scheme.audience && (@scheme.scopes.nil? || @scheme.scopes.empty?)
|
|
116
|
+
|
|
117
|
+
base_key
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# File: lib/legate/auth/credential.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Legate
|
|
5
|
+
module Auth
|
|
6
|
+
# Represents authentication credentials required by different schemes.
|
|
7
|
+
# Handles different types of credentials such as API keys, OAuth2 client credentials,
|
|
8
|
+
# service account keys, and more.
|
|
9
|
+
#
|
|
10
|
+
# @example API Key credential
|
|
11
|
+
# credential = Legate::Auth::Credential.new(
|
|
12
|
+
# auth_type: :api_key,
|
|
13
|
+
# api_key: 'my-api-key'
|
|
14
|
+
# )
|
|
15
|
+
#
|
|
16
|
+
# @example OAuth2 credential with environment variable
|
|
17
|
+
# credential = Legate::Auth::Credential.new(
|
|
18
|
+
# auth_type: :oauth2,
|
|
19
|
+
# client_id: 'my-client-id',
|
|
20
|
+
# client_secret: 'ENV:MY_CLIENT_SECRET'
|
|
21
|
+
# )
|
|
22
|
+
class Credential
|
|
23
|
+
# Valid credential types
|
|
24
|
+
VALID_TYPES = %i[api_key oauth2 oidc service_account google_service_account http_bearer basic].freeze
|
|
25
|
+
|
|
26
|
+
# Prefix for environment variable references
|
|
27
|
+
ENV_PREFIX = 'ENV:'
|
|
28
|
+
|
|
29
|
+
# @return [Symbol] The type of authentication
|
|
30
|
+
attr_reader :auth_type
|
|
31
|
+
|
|
32
|
+
# Initialize a new credential
|
|
33
|
+
# @param auth_type [Symbol] The type of authentication (:api_key, :oauth2, :oidc, :service_account, :http_bearer)
|
|
34
|
+
# @param kwargs [Hash] Additional attributes for the specific auth type
|
|
35
|
+
# @raise [Legate::Auth::CredentialError] If the credential is invalid
|
|
36
|
+
def initialize(auth_type:, **kwargs)
|
|
37
|
+
@auth_type = auth_type.to_sym
|
|
38
|
+
@attributes = kwargs
|
|
39
|
+
|
|
40
|
+
validate_auth_type!
|
|
41
|
+
validate_required_attributes!
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get an attribute value
|
|
45
|
+
# @param name [Symbol, String] The attribute name
|
|
46
|
+
# @param resolve_env [Boolean] Whether to resolve environment variables
|
|
47
|
+
# @return [Object, nil] The attribute value, or nil if not present
|
|
48
|
+
# @raise [Legate::Auth::EnvironmentVariableNotFoundError] If an environment variable is not found
|
|
49
|
+
def [](name, resolve_env: true)
|
|
50
|
+
attr_name = name.to_sym
|
|
51
|
+
value = @attributes[attr_name]
|
|
52
|
+
|
|
53
|
+
if resolve_env && value.is_a?(String) && value.start_with?(ENV_PREFIX)
|
|
54
|
+
resolve_environment_variable(value)
|
|
55
|
+
else
|
|
56
|
+
value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Set an attribute value
|
|
61
|
+
# @param name [Symbol, String] The attribute name
|
|
62
|
+
# @param value [Object] The attribute value
|
|
63
|
+
def []=(name, value)
|
|
64
|
+
@attributes[name.to_sym] = value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Convert to a hash
|
|
68
|
+
# @param resolve_env [Boolean] Whether to resolve environment variables
|
|
69
|
+
# @return [Hash] A hash representation of the credential
|
|
70
|
+
def to_h(resolve_env: false)
|
|
71
|
+
result = { auth_type: @auth_type }
|
|
72
|
+
|
|
73
|
+
@attributes.each do |key, value|
|
|
74
|
+
result[key] = if resolve_env && value.is_a?(String) && value.start_with?(ENV_PREFIX)
|
|
75
|
+
resolve_environment_variable(value)
|
|
76
|
+
else
|
|
77
|
+
value
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Check if the credential has an attribute
|
|
85
|
+
# @param name [Symbol, String] The attribute name
|
|
86
|
+
# @return [Boolean] True if the attribute exists
|
|
87
|
+
def has_attribute?(name)
|
|
88
|
+
@attributes.key?(name.to_sym)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Validate the authentication type
|
|
94
|
+
# @raise [Legate::Auth::CredentialError] If the authentication type is invalid
|
|
95
|
+
def validate_auth_type!
|
|
96
|
+
return if VALID_TYPES.include?(@auth_type)
|
|
97
|
+
|
|
98
|
+
raise Legate::Auth::CredentialError,
|
|
99
|
+
"Invalid auth_type: #{@auth_type}. Must be one of: #{VALID_TYPES.join(', ')}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Validate required attributes based on the authentication type
|
|
103
|
+
# @raise [Legate::Auth::CredentialError] If required attributes are missing
|
|
104
|
+
def validate_required_attributes!
|
|
105
|
+
required_attrs = required_attributes_for_type
|
|
106
|
+
missing_attrs = required_attrs.reject { |attr| @attributes.key?(attr) }
|
|
107
|
+
|
|
108
|
+
return if missing_attrs.empty?
|
|
109
|
+
|
|
110
|
+
raise Legate::Auth::CredentialError,
|
|
111
|
+
"Missing required attributes for #{@auth_type}: #{missing_attrs.join(', ')}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Required attributes based on the authentication type
|
|
115
|
+
# @return [Array<Symbol>] The required attributes
|
|
116
|
+
def required_attributes_for_type
|
|
117
|
+
case @auth_type
|
|
118
|
+
when :api_key
|
|
119
|
+
[:api_key]
|
|
120
|
+
when :oauth2
|
|
121
|
+
[:client_id]
|
|
122
|
+
when :oidc
|
|
123
|
+
[:client_id]
|
|
124
|
+
when :service_account
|
|
125
|
+
# Allow either service_account_key or service_account_key_file
|
|
126
|
+
return [] if @attributes.key?(:service_account_key) || @attributes.key?(:service_account_key_file)
|
|
127
|
+
|
|
128
|
+
[:service_account_key]
|
|
129
|
+
when :google_service_account
|
|
130
|
+
# Allow either service_account_key or service_account_key_file
|
|
131
|
+
return [] if @attributes.key?(:service_account_key) || @attributes.key?(:service_account_key_file)
|
|
132
|
+
|
|
133
|
+
[:service_account_key]
|
|
134
|
+
when :http_bearer
|
|
135
|
+
[:bearer_token]
|
|
136
|
+
when :basic
|
|
137
|
+
%i[username password]
|
|
138
|
+
else
|
|
139
|
+
[]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Resolve an environment variable reference
|
|
144
|
+
# @param value [String] The environment variable reference (e.g., "ENV:VARIABLE_NAME")
|
|
145
|
+
# @return [String] The resolved value
|
|
146
|
+
# @raise [Legate::Auth::EnvironmentVariableNotFoundError] If the environment variable is not found
|
|
147
|
+
def resolve_environment_variable(value)
|
|
148
|
+
env_name = value[ENV_PREFIX.length..-1]
|
|
149
|
+
env_value = ENV[env_name]
|
|
150
|
+
|
|
151
|
+
raise Legate::Auth::EnvironmentVariableNotFoundError, env_name if env_value.nil? || env_value.empty?
|
|
152
|
+
|
|
153
|
+
env_value
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# File: lib/legate/auth/encryption.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Legate
|
|
5
|
+
module Auth
|
|
6
|
+
# Provides encryption and decryption utilities for sensitive authentication data.
|
|
7
|
+
# Uses the rbnacl gem for authenticated encryption.
|
|
8
|
+
module Encryption
|
|
9
|
+
# Environment variable name for the encryption key
|
|
10
|
+
ENV_KEY_NAME = 'LEGATE_AUTH_ENCRYPTION_KEY'
|
|
11
|
+
|
|
12
|
+
# Header added to encrypted data for identification
|
|
13
|
+
ENCRYPTION_HEADER = 'LGTAUTH'
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Encrypts sensitive data
|
|
17
|
+
# @param data [String] The data to encrypt
|
|
18
|
+
# @param key [String, nil] The encryption key (defaults to the key from environment)
|
|
19
|
+
# @return [String] The encrypted data in Base64 format with header
|
|
20
|
+
# @raise [LoadError] If the rbnacl gem is not available
|
|
21
|
+
# @raise [ArgumentError] If the encryption key is not available
|
|
22
|
+
def encrypt(data, key = nil)
|
|
23
|
+
require_rbnacl
|
|
24
|
+
encryption_key = key || get_encryption_key
|
|
25
|
+
|
|
26
|
+
require 'base64'
|
|
27
|
+
box = create_box(encryption_key)
|
|
28
|
+
encrypted = box.encrypt(data.to_s)
|
|
29
|
+
"#{ENCRYPTION_HEADER}#{Base64.strict_encode64(encrypted)}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Decrypts sensitive data
|
|
33
|
+
# @param encrypted_data [String] The encrypted data to decrypt
|
|
34
|
+
# @param key [String, nil] The encryption key (defaults to the key from environment)
|
|
35
|
+
# @return [String] The decrypted data
|
|
36
|
+
# @raise [LoadError] If the rbnacl gem is not available
|
|
37
|
+
# @raise [ArgumentError] If the data is not in the expected format or the key is invalid
|
|
38
|
+
def decrypt(encrypted_data, key = nil)
|
|
39
|
+
require_rbnacl
|
|
40
|
+
encryption_key = key || get_encryption_key
|
|
41
|
+
|
|
42
|
+
# Check format and remove header
|
|
43
|
+
raise ArgumentError, 'Invalid encrypted data format' unless encrypted_data.to_s.start_with?(ENCRYPTION_HEADER)
|
|
44
|
+
|
|
45
|
+
encoded = encrypted_data.to_s[ENCRYPTION_HEADER.length..-1]
|
|
46
|
+
require 'base64'
|
|
47
|
+
encrypted = Base64.strict_decode64(encoded)
|
|
48
|
+
|
|
49
|
+
box = create_box(encryption_key)
|
|
50
|
+
box.decrypt(encrypted)
|
|
51
|
+
rescue RbNaCl::CryptoError => e
|
|
52
|
+
raise ArgumentError, "Decryption failed: #{e.message}"
|
|
53
|
+
rescue ArgumentError => e
|
|
54
|
+
raise ArgumentError, "Invalid Base64 encoding: #{e.message}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Generates a new random encryption key
|
|
58
|
+
# @return [String] A new encryption key in Base64 format
|
|
59
|
+
# @raise [LoadError] If the rbnacl gem is not available
|
|
60
|
+
def generate_key
|
|
61
|
+
require_rbnacl
|
|
62
|
+
require 'base64'
|
|
63
|
+
raw_key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)
|
|
64
|
+
Base64.strict_encode64(raw_key)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Checks if the encrypted data is in the expected format
|
|
68
|
+
# @param data [String] The data to check
|
|
69
|
+
# @return [Boolean] True if the data appears to be encrypted
|
|
70
|
+
def encrypted?(data)
|
|
71
|
+
data.to_s.start_with?(ENCRYPTION_HEADER)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Gets the encryption key from the environment or configuration
|
|
77
|
+
# @return [String] The encryption key in raw binary format
|
|
78
|
+
# @raise [ArgumentError] If the encryption key is not available
|
|
79
|
+
def get_encryption_key
|
|
80
|
+
env_key = ENV[ENV_KEY_NAME]
|
|
81
|
+
raise ArgumentError, "Encryption key not found. Set #{ENV_KEY_NAME} environment variable." unless env_key
|
|
82
|
+
|
|
83
|
+
require 'base64'
|
|
84
|
+
begin
|
|
85
|
+
Base64.strict_decode64(env_key)
|
|
86
|
+
rescue ArgumentError
|
|
87
|
+
raise ArgumentError, 'Invalid encryption key format. Must be Base64-encoded.'
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Creates a SimpleBox from the encryption key
|
|
92
|
+
# @param key [String] The encryption key in raw binary format
|
|
93
|
+
# @return [RbNaCl::SimpleBox] A box for encryption/decryption
|
|
94
|
+
def create_box(key)
|
|
95
|
+
RbNaCl::SimpleBox.from_secret_key(key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Ensures that the rbnacl gem is available
|
|
99
|
+
# @raise [LoadError] If the rbnacl gem is not available
|
|
100
|
+
def require_rbnacl
|
|
101
|
+
require 'rbnacl'
|
|
102
|
+
rescue LoadError
|
|
103
|
+
raise LoadError, "rbnacl gem is required for encryption. Add it to your Gemfile: gem 'rbnacl'"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# File: lib/legate/auth/error.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Legate
|
|
5
|
+
module Auth
|
|
6
|
+
# Base class for all authentication-related errors
|
|
7
|
+
class Error < Legate::Error
|
|
8
|
+
# @param message [String] The error message
|
|
9
|
+
# @param cause [Exception, nil] The underlying exception that caused this error
|
|
10
|
+
def initialize(message = nil, cause = nil)
|
|
11
|
+
message = "Authentication error#{": #{message}" if message}"
|
|
12
|
+
super(message)
|
|
13
|
+
set_backtrace(cause.backtrace) if cause && cause.backtrace
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Raised when authentication configuration is invalid
|
|
18
|
+
class ConfigurationError < Error
|
|
19
|
+
def initialize(message = nil, cause = nil)
|
|
20
|
+
super(message || 'Invalid authentication configuration', cause)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Raised when a token exchange operation fails
|
|
25
|
+
class TokenExchangeError < Error
|
|
26
|
+
# @param message [String] The error message
|
|
27
|
+
# @param provider_error [String, nil] Error information from the provider
|
|
28
|
+
# @param cause [Exception, nil] The underlying exception that caused this error
|
|
29
|
+
def initialize(message = nil, provider_error = nil, cause = nil)
|
|
30
|
+
error_message = message || 'Token exchange failed'
|
|
31
|
+
error_message = "#{error_message}: #{provider_error}" if provider_error
|
|
32
|
+
super(error_message, cause)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Raised when a token refresh operation fails
|
|
37
|
+
class TokenRefreshError < Error
|
|
38
|
+
# @param message [String] The error message
|
|
39
|
+
# @param provider_error [String, nil] Error information from the provider
|
|
40
|
+
# @param cause [Exception, nil] The underlying exception that caused this error
|
|
41
|
+
def initialize(message = nil, provider_error = nil, cause = nil)
|
|
42
|
+
error_message = message || 'Token refresh failed'
|
|
43
|
+
error_message = "#{error_message}: #{provider_error}" if provider_error
|
|
44
|
+
super(error_message, cause)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Raised when a token revocation operation fails
|
|
49
|
+
class TokenRevocationError < Error
|
|
50
|
+
# @param message [String] The error message
|
|
51
|
+
# @param provider_error [String, nil] Error information from the provider
|
|
52
|
+
# @param cause [Exception, nil] The underlying exception that caused this error
|
|
53
|
+
def initialize(message = nil, provider_error = nil, cause = nil)
|
|
54
|
+
error_message = message || 'Token revocation failed'
|
|
55
|
+
error_message = "#{error_message}: #{provider_error}" if provider_error
|
|
56
|
+
super(error_message, cause)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Raised when an authentication provider returns an error
|
|
61
|
+
class ProviderError < Error
|
|
62
|
+
# @param message [String] The error message
|
|
63
|
+
# @param provider_error [String, nil] Error information from the provider
|
|
64
|
+
# @param cause [Exception, nil] The underlying exception that caused this error
|
|
65
|
+
def initialize(message = nil, provider_error = nil, cause = nil)
|
|
66
|
+
error_message = message || 'Authentication provider error'
|
|
67
|
+
error_message = "#{error_message}: #{provider_error}" if provider_error
|
|
68
|
+
super(error_message, cause)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Raised when credentials are missing or invalid
|
|
73
|
+
class CredentialError < Error
|
|
74
|
+
def initialize(message = nil, cause = nil)
|
|
75
|
+
super(message || 'Invalid or missing credentials', cause)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Raised when an environment variable used for credentials is not found
|
|
80
|
+
class EnvironmentVariableNotFoundError < CredentialError
|
|
81
|
+
# @param var_name [String] The name of the environment variable
|
|
82
|
+
def initialize(var_name, cause = nil)
|
|
83
|
+
super("Environment variable not found: #{var_name}", cause)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Raised when a scheme validation fails
|
|
88
|
+
class SchemeValidationError < ConfigurationError
|
|
89
|
+
def initialize(message = nil, cause = nil)
|
|
90
|
+
super(message || 'Invalid authentication scheme configuration', cause)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|