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,394 @@
|
|
|
1
|
+
# File: lib/legate/auth/manager_store.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Legate
|
|
7
|
+
module Auth
|
|
8
|
+
# Module for persisting authentication configuration (schemes, credentials, URL mappings)
|
|
9
|
+
module ManagerStore
|
|
10
|
+
# Redis-backed implementation for storing authentication configuration
|
|
11
|
+
class RedisStore
|
|
12
|
+
# Redis key prefixes
|
|
13
|
+
SCHEMES_HASH_KEY = 'legate:auth:schemes'
|
|
14
|
+
CREDENTIALS_HASH_KEY = 'legate:auth:credentials'
|
|
15
|
+
URL_MAPPINGS_KEY = 'legate:auth:url_mappings'
|
|
16
|
+
|
|
17
|
+
# @param redis_client [Redis] An instance of the Redis client
|
|
18
|
+
def initialize(redis_client:)
|
|
19
|
+
@redis = redis_client
|
|
20
|
+
@logger = Legate.logger
|
|
21
|
+
@logger&.info('Legate::Auth::ManagerStore::RedisStore initialized.')
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
Legate.logger&.error("Failed to initialize Auth ManagerStore: #{e.message}")
|
|
24
|
+
@redis = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Check if Redis is available
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
def available?
|
|
30
|
+
!@redis.nil?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# ========== Scheme Operations ==========
|
|
34
|
+
|
|
35
|
+
# Save a scheme configuration
|
|
36
|
+
# @param name [String, Symbol] The scheme name
|
|
37
|
+
# @param scheme [Legate::Auth::Scheme] The scheme to save
|
|
38
|
+
# @return [Boolean] true if successful
|
|
39
|
+
def save_scheme(name, scheme)
|
|
40
|
+
return false unless available?
|
|
41
|
+
|
|
42
|
+
name = name.to_s
|
|
43
|
+
|
|
44
|
+
scheme_data = serialize_scheme(scheme)
|
|
45
|
+
@redis.hset(SCHEMES_HASH_KEY, name, scheme_data.to_json)
|
|
46
|
+
@logger&.debug("Saved auth scheme '#{name}' to Redis")
|
|
47
|
+
true
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
@logger&.error("Failed to save scheme '#{name}': #{e.message}")
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Load a single scheme
|
|
54
|
+
# @param name [String, Symbol] The scheme name
|
|
55
|
+
# @return [Hash, nil] The scheme data or nil if not found
|
|
56
|
+
def load_scheme(name)
|
|
57
|
+
return nil unless available?
|
|
58
|
+
|
|
59
|
+
name = name.to_s
|
|
60
|
+
data = @redis.hget(SCHEMES_HASH_KEY, name)
|
|
61
|
+
return nil unless data
|
|
62
|
+
|
|
63
|
+
JSON.parse(data, symbolize_names: true)
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
@logger&.error("Failed to load scheme '#{name}': #{e.message}")
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Load all schemes
|
|
70
|
+
# @return [Hash] Hash of scheme_name => scheme_data
|
|
71
|
+
def load_all_schemes
|
|
72
|
+
return {} unless available?
|
|
73
|
+
|
|
74
|
+
result = {}
|
|
75
|
+
@redis.hgetall(SCHEMES_HASH_KEY).each do |name, data|
|
|
76
|
+
result[name.to_sym] = JSON.parse(data, symbolize_names: true)
|
|
77
|
+
rescue JSON::ParserError => e
|
|
78
|
+
@logger&.warn("Failed to parse scheme '#{name}': #{e.message}")
|
|
79
|
+
end
|
|
80
|
+
result
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
@logger&.error("Failed to load schemes: #{e.message}")
|
|
83
|
+
{}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Delete a scheme
|
|
87
|
+
# @param name [String, Symbol] The scheme name
|
|
88
|
+
# @return [Boolean] true if successful
|
|
89
|
+
def delete_scheme(name)
|
|
90
|
+
return false unless available?
|
|
91
|
+
|
|
92
|
+
@redis.hdel(SCHEMES_HASH_KEY, name.to_s)
|
|
93
|
+
@logger&.debug("Deleted auth scheme '#{name}' from Redis")
|
|
94
|
+
true
|
|
95
|
+
rescue StandardError => e
|
|
96
|
+
@logger&.error("Failed to delete scheme '#{name}': #{e.message}")
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# ========== Credential Operations ==========
|
|
101
|
+
|
|
102
|
+
# Save a credential
|
|
103
|
+
# @param name [String, Symbol] The credential name
|
|
104
|
+
# @param credential [Legate::Auth::Credential] The credential to save
|
|
105
|
+
# @return [Boolean] true if successful
|
|
106
|
+
def save_credential(name, credential)
|
|
107
|
+
return false unless available?
|
|
108
|
+
|
|
109
|
+
name = name.to_s
|
|
110
|
+
|
|
111
|
+
credential_data = serialize_credential(credential)
|
|
112
|
+
@redis.hset(CREDENTIALS_HASH_KEY, name, credential_data.to_json)
|
|
113
|
+
@logger&.debug("Saved auth credential '#{name}' to Redis")
|
|
114
|
+
true
|
|
115
|
+
rescue StandardError => e
|
|
116
|
+
@logger&.error("Failed to save credential '#{name}': #{e.message}")
|
|
117
|
+
false
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Load a single credential
|
|
121
|
+
# @param name [String, Symbol] The credential name
|
|
122
|
+
# @return [Hash, nil] The credential data or nil if not found
|
|
123
|
+
def load_credential(name)
|
|
124
|
+
return nil unless available?
|
|
125
|
+
|
|
126
|
+
name = name.to_s
|
|
127
|
+
data = @redis.hget(CREDENTIALS_HASH_KEY, name)
|
|
128
|
+
return nil unless data
|
|
129
|
+
|
|
130
|
+
JSON.parse(data, symbolize_names: true)
|
|
131
|
+
rescue StandardError => e
|
|
132
|
+
@logger&.error("Failed to load credential '#{name}': #{e.message}")
|
|
133
|
+
nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Load all credentials
|
|
137
|
+
# @return [Hash] Hash of credential_name => credential_data
|
|
138
|
+
def load_all_credentials
|
|
139
|
+
return {} unless available?
|
|
140
|
+
|
|
141
|
+
result = {}
|
|
142
|
+
@redis.hgetall(CREDENTIALS_HASH_KEY).each do |name, data|
|
|
143
|
+
result[name.to_sym] = JSON.parse(data, symbolize_names: true)
|
|
144
|
+
rescue JSON::ParserError => e
|
|
145
|
+
@logger&.warn("Failed to parse credential '#{name}': #{e.message}")
|
|
146
|
+
end
|
|
147
|
+
result
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
@logger&.error("Failed to load credentials: #{e.message}")
|
|
150
|
+
{}
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Delete a credential
|
|
154
|
+
# @param name [String, Symbol] The credential name
|
|
155
|
+
# @return [Boolean] true if successful
|
|
156
|
+
def delete_credential(name)
|
|
157
|
+
return false unless available?
|
|
158
|
+
|
|
159
|
+
@redis.hdel(CREDENTIALS_HASH_KEY, name.to_s)
|
|
160
|
+
@logger&.debug("Deleted auth credential '#{name}' from Redis")
|
|
161
|
+
true
|
|
162
|
+
rescue StandardError => e
|
|
163
|
+
@logger&.error("Failed to delete credential '#{name}': #{e.message}")
|
|
164
|
+
false
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# ========== URL Mapping Operations ==========
|
|
168
|
+
|
|
169
|
+
# Save URL mappings (replaces all)
|
|
170
|
+
# @param mappings [Array<Hash>] Array of URL mapping hashes
|
|
171
|
+
# @return [Boolean] true if successful
|
|
172
|
+
def save_url_mappings(mappings)
|
|
173
|
+
return false unless available?
|
|
174
|
+
|
|
175
|
+
serialized = mappings.map do |mapping|
|
|
176
|
+
{
|
|
177
|
+
pattern: mapping[:pattern].is_a?(Regexp) ? { regexp: mapping[:pattern].source } : mapping[:pattern],
|
|
178
|
+
scheme_name: mapping[:scheme_name].to_s,
|
|
179
|
+
credential_name: mapping[:credential_name].to_s
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
@redis.set(URL_MAPPINGS_KEY, serialized.to_json)
|
|
184
|
+
@logger&.debug("Saved #{mappings.size} URL mappings to Redis")
|
|
185
|
+
true
|
|
186
|
+
rescue StandardError => e
|
|
187
|
+
@logger&.error("Failed to save URL mappings: #{e.message}")
|
|
188
|
+
false
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Load URL mappings
|
|
192
|
+
# @return [Array<Hash>] Array of URL mapping hashes
|
|
193
|
+
def load_url_mappings
|
|
194
|
+
return [] unless available?
|
|
195
|
+
|
|
196
|
+
data = @redis.get(URL_MAPPINGS_KEY)
|
|
197
|
+
return [] unless data
|
|
198
|
+
|
|
199
|
+
mappings = JSON.parse(data, symbolize_names: true)
|
|
200
|
+
|
|
201
|
+
# Reconstruct Regexp patterns
|
|
202
|
+
mappings.map do |mapping|
|
|
203
|
+
pattern = mapping[:pattern]
|
|
204
|
+
pattern = Regexp.new(pattern[:regexp]) if pattern.is_a?(Hash) && pattern[:regexp]
|
|
205
|
+
|
|
206
|
+
{
|
|
207
|
+
pattern: pattern,
|
|
208
|
+
scheme_name: mapping[:scheme_name].to_sym,
|
|
209
|
+
credential_name: mapping[:credential_name].to_sym
|
|
210
|
+
}
|
|
211
|
+
end
|
|
212
|
+
rescue StandardError => e
|
|
213
|
+
@logger&.error("Failed to load URL mappings: #{e.message}")
|
|
214
|
+
[]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Add a single URL mapping
|
|
218
|
+
# @param mapping [Hash] The URL mapping to add
|
|
219
|
+
# @return [Boolean] true if successful
|
|
220
|
+
def add_url_mapping(mapping)
|
|
221
|
+
return false unless available?
|
|
222
|
+
|
|
223
|
+
current = load_url_mappings
|
|
224
|
+
current << mapping
|
|
225
|
+
save_url_mappings(current)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Remove a URL mapping
|
|
229
|
+
# @param index [Integer] The index of the mapping to remove
|
|
230
|
+
# @return [Boolean] true if successful
|
|
231
|
+
def remove_url_mapping(index)
|
|
232
|
+
return false unless available?
|
|
233
|
+
|
|
234
|
+
current = load_url_mappings
|
|
235
|
+
return false if index < 0 || index >= current.size
|
|
236
|
+
|
|
237
|
+
current.delete_at(index)
|
|
238
|
+
save_url_mappings(current)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Clear all URL mappings
|
|
242
|
+
# @return [Boolean] true if successful
|
|
243
|
+
def clear_url_mappings
|
|
244
|
+
return false unless available?
|
|
245
|
+
|
|
246
|
+
@redis.del(URL_MAPPINGS_KEY)
|
|
247
|
+
true
|
|
248
|
+
rescue StandardError => e
|
|
249
|
+
@logger&.error("Failed to clear URL mappings: #{e.message}")
|
|
250
|
+
false
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
# Serialize a scheme to a storable hash
|
|
256
|
+
# @param scheme [Legate::Auth::Scheme] The scheme
|
|
257
|
+
# @return [Hash] Serialized scheme data
|
|
258
|
+
def serialize_scheme(scheme)
|
|
259
|
+
data = {
|
|
260
|
+
scheme_type: scheme.scheme_type.to_s,
|
|
261
|
+
class_name: scheme.class.name
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# Add scheme-specific configuration
|
|
265
|
+
case scheme
|
|
266
|
+
when Legate::Auth::Schemes::OAuth2, Legate::Auth::Schemes::OpenIDConnect
|
|
267
|
+
data[:authorization_url] = scheme.authorization_url if scheme.respond_to?(:authorization_url)
|
|
268
|
+
data[:token_url] = scheme.token_url if scheme.respond_to?(:token_url)
|
|
269
|
+
data[:scopes] = scheme.scopes if scheme.respond_to?(:scopes)
|
|
270
|
+
data[:use_pkce] = scheme.use_pkce if scheme.respond_to?(:use_pkce)
|
|
271
|
+
data[:revocation_url] = scheme.revocation_url if scheme.respond_to?(:revocation_url)
|
|
272
|
+
when Legate::Auth::Schemes::ApiKey
|
|
273
|
+
data[:header_name] = scheme.header_name if scheme.respond_to?(:header_name)
|
|
274
|
+
data[:query_param_name] = scheme.query_param_name if scheme.respond_to?(:query_param_name)
|
|
275
|
+
data[:location] = scheme.location if scheme.respond_to?(:location)
|
|
276
|
+
when Legate::Auth::Schemes::ServiceAccount
|
|
277
|
+
data[:token_url] = scheme.token_url if scheme.respond_to?(:token_url)
|
|
278
|
+
when Legate::Auth::Schemes::GoogleServiceAccount
|
|
279
|
+
data[:scopes] = scheme.scopes if scheme.respond_to?(:scopes)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
data
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Serialize a credential to a storable hash
|
|
286
|
+
# @param credential [Legate::Auth::Credential] The credential
|
|
287
|
+
# @return [Hash] Serialized credential data
|
|
288
|
+
def serialize_credential(credential)
|
|
289
|
+
data = {
|
|
290
|
+
auth_type: credential.auth_type.to_s
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# Copy all attributes (they may contain ENV: references which should be preserved as-is)
|
|
294
|
+
attributes = credential.instance_variable_get(:@attributes) || {}
|
|
295
|
+
attributes.each do |key, value|
|
|
296
|
+
data[key] = value
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
data
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# In-memory fallback store (for when Redis is unavailable)
|
|
304
|
+
class InMemoryStore
|
|
305
|
+
def initialize
|
|
306
|
+
@schemes = {}
|
|
307
|
+
@credentials = {}
|
|
308
|
+
@url_mappings = []
|
|
309
|
+
@logger = Legate.logger
|
|
310
|
+
@logger&.info('Legate::Auth::ManagerStore::InMemoryStore initialized (no persistence).')
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def available?
|
|
314
|
+
true
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def save_scheme(name, scheme)
|
|
318
|
+
@schemes[name.to_sym] = serialize_scheme(scheme)
|
|
319
|
+
true
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def load_scheme(name)
|
|
323
|
+
@schemes[name.to_sym]
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def load_all_schemes
|
|
327
|
+
@schemes.dup
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def delete_scheme(name)
|
|
331
|
+
@schemes.delete(name.to_sym)
|
|
332
|
+
true
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def save_credential(name, credential)
|
|
336
|
+
@credentials[name.to_sym] = serialize_credential(credential)
|
|
337
|
+
true
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def load_credential(name)
|
|
341
|
+
@credentials[name.to_sym]
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def load_all_credentials
|
|
345
|
+
@credentials.dup
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def delete_credential(name)
|
|
349
|
+
@credentials.delete(name.to_sym)
|
|
350
|
+
true
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def save_url_mappings(mappings)
|
|
354
|
+
@url_mappings = mappings.dup
|
|
355
|
+
true
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def load_url_mappings
|
|
359
|
+
@url_mappings.dup
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def add_url_mapping(mapping)
|
|
363
|
+
@url_mappings << mapping
|
|
364
|
+
true
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def remove_url_mapping(index)
|
|
368
|
+
return false if index < 0 || index >= @url_mappings.size
|
|
369
|
+
|
|
370
|
+
@url_mappings.delete_at(index)
|
|
371
|
+
true
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def clear_url_mappings
|
|
375
|
+
@url_mappings = []
|
|
376
|
+
true
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
private
|
|
380
|
+
|
|
381
|
+
def serialize_scheme(scheme)
|
|
382
|
+
{ scheme_type: scheme.scheme_type.to_s, class_name: scheme.class.name }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def serialize_credential(credential)
|
|
386
|
+
data = { auth_type: credential.auth_type.to_s }
|
|
387
|
+
attributes = credential.instance_variable_get(:@attributes) || {}
|
|
388
|
+
attributes.each { |k, v| data[k] = v }
|
|
389
|
+
data
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# File: lib/legate/auth/middleware_factory.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'excon_middleware'
|
|
5
|
+
require_relative 'token_store'
|
|
6
|
+
require_relative 'token_manager'
|
|
7
|
+
require_relative 'schemes/api_key'
|
|
8
|
+
require_relative 'schemes/http_bearer'
|
|
9
|
+
require_relative 'schemes/oauth2'
|
|
10
|
+
require_relative 'schemes/openid_connect'
|
|
11
|
+
require_relative 'schemes/service_account'
|
|
12
|
+
|
|
13
|
+
module Legate
|
|
14
|
+
module Auth
|
|
15
|
+
# Factory for creating authentication middleware instances
|
|
16
|
+
# based on different scheme types and configurations.
|
|
17
|
+
class MiddlewareFactory
|
|
18
|
+
class << self
|
|
19
|
+
# Create middleware for any authentication scheme
|
|
20
|
+
# @param scheme [Legate::Auth::Scheme] The authentication scheme to use
|
|
21
|
+
# @param credential [Legate::Auth::Credential] The credential to use
|
|
22
|
+
# @param options [Hash] Additional options for the middleware
|
|
23
|
+
# @option options [Legate::Auth::TokenStore] :token_store Optional token store for caching tokens
|
|
24
|
+
# @option options [Legate::Auth::TokenManager] :token_manager Optional token manager for token lifecycle
|
|
25
|
+
# @option options [Boolean] :auto_retry Whether to automatically retry on auth errors (default: true)
|
|
26
|
+
# @option options [Integer] :max_retries Maximum number of retries (default: 3)
|
|
27
|
+
# @option options [Symbol] :backoff_strategy Strategy for retries (:linear, :exponential, :fibonacci, :jitter, :none)
|
|
28
|
+
# @option options [Float] :backoff_factor Factor to use for backoff calculation (default: 1.0)
|
|
29
|
+
# @option options [Boolean] :retry_non_idempotent Whether to retry non-idempotent requests (default: false)
|
|
30
|
+
# @option options [Array<Integer>] :retry_on Additional HTTP status codes to retry on
|
|
31
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
32
|
+
def create(scheme:, credential:, **options)
|
|
33
|
+
# Create a token store if not provided
|
|
34
|
+
token_store = options[:token_store]
|
|
35
|
+
unless token_store
|
|
36
|
+
session_service = options[:session_service]
|
|
37
|
+
token_store = Legate::Auth::TokenStore.new(session_service) if session_service
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Create a token manager if not provided
|
|
41
|
+
token_manager = options[:token_manager]
|
|
42
|
+
|
|
43
|
+
# Configure retry options
|
|
44
|
+
auto_retry = options.key?(:auto_retry) ? options[:auto_retry] : true
|
|
45
|
+
max_retries = options[:max_retries] || 3
|
|
46
|
+
backoff_strategy = options[:backoff_strategy] || :exponential
|
|
47
|
+
backoff_factor = options[:backoff_factor] || 1.0
|
|
48
|
+
retry_non_idempotent = options[:retry_non_idempotent] || false
|
|
49
|
+
retry_on = options[:retry_on] || []
|
|
50
|
+
|
|
51
|
+
# Create the middleware instance with nil stack (will be set by Excon later)
|
|
52
|
+
Legate::Auth::ExconMiddleware.new(nil, {
|
|
53
|
+
scheme: scheme,
|
|
54
|
+
credential: credential,
|
|
55
|
+
token_store: token_store,
|
|
56
|
+
token_manager: token_manager,
|
|
57
|
+
auto_retry: auto_retry,
|
|
58
|
+
max_retries: max_retries,
|
|
59
|
+
backoff_strategy: backoff_strategy,
|
|
60
|
+
backoff_factor: backoff_factor,
|
|
61
|
+
retry_non_idempotent: retry_non_idempotent,
|
|
62
|
+
retry_on: retry_on
|
|
63
|
+
})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Create middleware specifically for API key authentication
|
|
67
|
+
# @param api_key [String] The API key to use
|
|
68
|
+
# @param location [String] Where to place the API key ('header', 'query', 'cookie')
|
|
69
|
+
# @param name [String] The name of the parameter/header
|
|
70
|
+
# @param options [Hash] Additional options for the middleware
|
|
71
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
72
|
+
def create_api_key(api_key:, location: 'header', name: 'X-API-Key', **options)
|
|
73
|
+
# Create the scheme
|
|
74
|
+
scheme = Legate::Auth::Schemes::ApiKey.new
|
|
75
|
+
|
|
76
|
+
# Create the credential
|
|
77
|
+
credential = Legate::Auth::Credential.new(
|
|
78
|
+
auth_type: :api_key,
|
|
79
|
+
api_key: api_key,
|
|
80
|
+
location: location,
|
|
81
|
+
name: name
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Create and return the middleware
|
|
85
|
+
create(scheme: scheme, credential: credential, **options)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Create middleware specifically for Bearer token authentication
|
|
89
|
+
# @param token [String] The bearer token to use
|
|
90
|
+
# @param options [Hash] Additional options for the middleware
|
|
91
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
92
|
+
def create_bearer(token:, **options)
|
|
93
|
+
# Create the scheme
|
|
94
|
+
scheme = Legate::Auth::Schemes::HTTPBearer.new
|
|
95
|
+
|
|
96
|
+
# Create the credential
|
|
97
|
+
credential = Legate::Auth::Credential.new(
|
|
98
|
+
auth_type: :http_bearer,
|
|
99
|
+
bearer_token: token
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Create and return the middleware
|
|
103
|
+
create(scheme: scheme, credential: credential, **options)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Create middleware specifically for OAuth2 authentication
|
|
107
|
+
# @param client_id [String] The OAuth client ID
|
|
108
|
+
# @param client_secret [String] The OAuth client secret
|
|
109
|
+
# @param authorization_url [String] The authorization URL for the OAuth provider
|
|
110
|
+
# @param token_url [String] The token URL for the OAuth provider
|
|
111
|
+
# @param scopes [Array<String>, String] The OAuth scopes to request
|
|
112
|
+
# @param options [Hash] Additional options for the middleware
|
|
113
|
+
# @option options [String] :redirect_uri The redirect URI for the OAuth flow
|
|
114
|
+
# @option options [Hash] :additional_params Additional parameters to include in the authorization request
|
|
115
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
116
|
+
def create_oauth2(client_id:, client_secret:, authorization_url:, token_url:, scopes: nil, **options)
|
|
117
|
+
# Extract OAuth2-specific options
|
|
118
|
+
redirect_uri = options.delete(:redirect_uri)
|
|
119
|
+
additional_params = options.delete(:additional_params) || {}
|
|
120
|
+
|
|
121
|
+
# Create the scheme with additional params
|
|
122
|
+
scheme_options = {
|
|
123
|
+
authorization_url: authorization_url,
|
|
124
|
+
token_url: token_url,
|
|
125
|
+
scopes: scopes
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Add redirect_uri if provided
|
|
129
|
+
scheme_options[:redirect_uri] = redirect_uri if redirect_uri
|
|
130
|
+
|
|
131
|
+
# Add any additional parameters
|
|
132
|
+
scheme_options[:additional_params] = additional_params unless additional_params.empty?
|
|
133
|
+
|
|
134
|
+
# Create the scheme
|
|
135
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(**scheme_options)
|
|
136
|
+
|
|
137
|
+
# Create the credential
|
|
138
|
+
credential = Legate::Auth::Credential.new(
|
|
139
|
+
auth_type: :oauth2,
|
|
140
|
+
client_id: client_id,
|
|
141
|
+
client_secret: client_secret
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Create and return the middleware
|
|
145
|
+
create(scheme: scheme, credential: credential, **options)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Create middleware specifically for OpenID Connect authentication
|
|
149
|
+
# @param client_id [String] The OAuth client ID
|
|
150
|
+
# @param client_secret [String] The OAuth client secret
|
|
151
|
+
# @param discovery_url [String, nil] The OIDC discovery URL
|
|
152
|
+
# @param authorization_url [String, nil] The authorization URL (if not using discovery)
|
|
153
|
+
# @param token_url [String, nil] The token URL (if not using discovery)
|
|
154
|
+
# @param userinfo_url [String, nil] The userinfo URL (if not using discovery)
|
|
155
|
+
# @param jwks_url [String, nil] The JWKS URL (if not using discovery)
|
|
156
|
+
# @param scopes [Array<String>, String] The OAuth scopes to request
|
|
157
|
+
# @param options [Hash] Additional options for the middleware
|
|
158
|
+
# @option options [String] :redirect_uri The redirect URI for the OIDC flow
|
|
159
|
+
# @option options [Hash] :additional_params Additional parameters to include in the authorization request
|
|
160
|
+
# @option options [Boolean] :verify_id_token Whether to verify the ID token
|
|
161
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
162
|
+
def create_oidc(client_id:, client_secret:, discovery_url: nil, authorization_url: nil,
|
|
163
|
+
token_url: nil, userinfo_url: nil, jwks_url: nil, scopes: nil, **options)
|
|
164
|
+
# Extract OIDC-specific options
|
|
165
|
+
redirect_uri = options.delete(:redirect_uri)
|
|
166
|
+
additional_params = options.delete(:additional_params) || {}
|
|
167
|
+
verify_id_token = options.key?(:verify_id_token) ? options.delete(:verify_id_token) : true
|
|
168
|
+
|
|
169
|
+
# Determine how to initialize the scheme
|
|
170
|
+
scheme_options = if discovery_url
|
|
171
|
+
{
|
|
172
|
+
discovery_url: discovery_url,
|
|
173
|
+
scopes: scopes,
|
|
174
|
+
verify_id_token: verify_id_token
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
{
|
|
178
|
+
authorization_url: authorization_url,
|
|
179
|
+
token_url: token_url,
|
|
180
|
+
userinfo_url: userinfo_url,
|
|
181
|
+
jwks_url: jwks_url,
|
|
182
|
+
scopes: scopes,
|
|
183
|
+
verify_id_token: verify_id_token
|
|
184
|
+
}
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Add redirect_uri if provided
|
|
188
|
+
scheme_options[:redirect_uri] = redirect_uri if redirect_uri
|
|
189
|
+
|
|
190
|
+
# Add any additional parameters
|
|
191
|
+
scheme_options[:additional_params] = additional_params unless additional_params.empty?
|
|
192
|
+
|
|
193
|
+
# Create the scheme
|
|
194
|
+
scheme = Legate::Auth::Schemes::OIDC.new(**scheme_options)
|
|
195
|
+
|
|
196
|
+
# Create the credential
|
|
197
|
+
credential = Legate::Auth::Credential.new(
|
|
198
|
+
auth_type: :oidc,
|
|
199
|
+
client_id: client_id,
|
|
200
|
+
client_secret: client_secret
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Create and return the middleware
|
|
204
|
+
create(scheme: scheme, credential: credential, **options)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Create middleware specifically for Service Account authentication
|
|
208
|
+
# @param service_account_key [String, Hash] The service account key as JSON string or Hash
|
|
209
|
+
# @param token_url [String, nil] The token URL for the service account
|
|
210
|
+
# @param scopes [Array<String>, String, nil] The scopes to request
|
|
211
|
+
# @param audience [String, nil] The audience for the token
|
|
212
|
+
# @param options [Hash] Additional options for the middleware
|
|
213
|
+
# @option options [Integer] :token_lifetime Time in seconds for token expiration (default: 3600)
|
|
214
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
215
|
+
def create_service_account(service_account_key:, token_url: nil, scopes: nil, audience: nil, **options)
|
|
216
|
+
# Extract service account specific options
|
|
217
|
+
token_lifetime = options.delete(:token_lifetime) || 3600
|
|
218
|
+
|
|
219
|
+
# Parse the key if it's a string
|
|
220
|
+
key_data = if service_account_key.is_a?(String)
|
|
221
|
+
begin
|
|
222
|
+
JSON.parse(service_account_key)
|
|
223
|
+
rescue JSON::ParserError
|
|
224
|
+
raise ArgumentError, 'Invalid service account key: not valid JSON'
|
|
225
|
+
end
|
|
226
|
+
else
|
|
227
|
+
service_account_key
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Use token_url from the key if not provided
|
|
231
|
+
token_url ||= key_data['token_uri']
|
|
232
|
+
|
|
233
|
+
# Create the scheme
|
|
234
|
+
scheme = Legate::Auth::Schemes::ServiceAccount.new(
|
|
235
|
+
token_url: token_url,
|
|
236
|
+
audience: audience,
|
|
237
|
+
scopes: scopes,
|
|
238
|
+
token_lifetime: token_lifetime
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Create the credential
|
|
242
|
+
credential = Legate::Auth::Credential.new(
|
|
243
|
+
auth_type: :service_account,
|
|
244
|
+
service_account_key: service_account_key.is_a?(String) ? service_account_key : service_account_key.to_json
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Create and return the middleware
|
|
248
|
+
create(scheme: scheme, credential: credential, **options)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Create middleware specifically for Basic authentication
|
|
252
|
+
# @param username [String] The username for Basic Auth
|
|
253
|
+
# @param password [String] The password for Basic Auth
|
|
254
|
+
# @param options [Hash] Additional options for the middleware
|
|
255
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
256
|
+
def create_basic_auth(username:, password:, **options)
|
|
257
|
+
# Basic auth is handled by the HTTPBearer scheme with a different type
|
|
258
|
+
scheme = Legate::Auth::Schemes::HTTPBearer.new(auth_type: :basic)
|
|
259
|
+
|
|
260
|
+
# Create the credential
|
|
261
|
+
credential = Legate::Auth::Credential.new(
|
|
262
|
+
auth_type: :basic,
|
|
263
|
+
username: username,
|
|
264
|
+
password: password
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Create and return the middleware
|
|
268
|
+
create(scheme: scheme, credential: credential, **options)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Create middleware for a pre-configured authentication provider
|
|
272
|
+
# @param provider_id [String] The ID of the pre-configured provider
|
|
273
|
+
# @param options [Hash] Additional options for the middleware
|
|
274
|
+
# @return [Legate::Auth::ExconMiddleware] The configured middleware
|
|
275
|
+
def create_from_provider(provider_id, **options)
|
|
276
|
+
# Retrieve the stored credential from Legate::Auth
|
|
277
|
+
exchanged_credential = Legate::Auth.get_exchanged_credential(provider_id)
|
|
278
|
+
raise ArgumentError, "No credential found for provider ID: #{provider_id}" unless exchanged_credential
|
|
279
|
+
|
|
280
|
+
# Get the scheme
|
|
281
|
+
scheme = Legate::Auth.get_scheme_for_provider(provider_id)
|
|
282
|
+
raise ArgumentError, "No scheme found for provider ID: #{provider_id}" unless scheme
|
|
283
|
+
|
|
284
|
+
# Create and return the middleware
|
|
285
|
+
create(scheme: scheme, credential: exchanged_credential, **options)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|