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,317 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using the Excon middleware for authentication
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use the Legate::Auth::ExconMiddleware to automatically
|
|
7
|
+
# handle authentication for HTTP requests, including automatic token refresh and retry.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/advanced/auth/excon_middleware.rb
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'legate'
|
|
14
|
+
require 'json'
|
|
15
|
+
require 'excon'
|
|
16
|
+
|
|
17
|
+
puts 'Legate::Auth Excon Middleware Example'
|
|
18
|
+
puts '--------------------------------'
|
|
19
|
+
|
|
20
|
+
# Create a basic token store
|
|
21
|
+
token_store = Legate::Auth::TokenStore.new
|
|
22
|
+
|
|
23
|
+
# Enable mock mode for Excon to simulate HTTP requests
|
|
24
|
+
Excon.defaults[:mock] = true
|
|
25
|
+
|
|
26
|
+
# 1. Example with API Key Authentication
|
|
27
|
+
puts "\n1. API Key Authentication Example:"
|
|
28
|
+
|
|
29
|
+
# Set up our mock response for API Key authentication
|
|
30
|
+
Excon.stub({}) do |params|
|
|
31
|
+
if params[:headers] && params[:headers]['X-API-Key'] == 'test-api-key'
|
|
32
|
+
{
|
|
33
|
+
status: 200,
|
|
34
|
+
body: JSON.generate({
|
|
35
|
+
headers: params[:headers]
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
{ status: 401, body: '{"error": "Unauthorized"}' }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create a middleware using the factory
|
|
44
|
+
api_key_middleware = Legate::Auth::MiddlewareFactory.create_api_key(
|
|
45
|
+
api_key: 'test-api-key',
|
|
46
|
+
location: :header,
|
|
47
|
+
name: 'X-API-Key',
|
|
48
|
+
token_store: token_store
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Create a connection with the middleware properly configured
|
|
52
|
+
connection = Excon.new('https://httpbin.org')
|
|
53
|
+
|
|
54
|
+
# Add our middleware to the stack if not already present
|
|
55
|
+
connection.data[:middlewares] = connection.data[:middlewares].dup
|
|
56
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
57
|
+
|
|
58
|
+
# Set our configured middleware instance
|
|
59
|
+
connection.data[:auth_middleware] = api_key_middleware
|
|
60
|
+
|
|
61
|
+
# Make a request - the middleware will automatically add the API key header
|
|
62
|
+
begin
|
|
63
|
+
response = connection.request(method: :get, path: '/headers')
|
|
64
|
+
|
|
65
|
+
puts 'Request Headers:'
|
|
66
|
+
headers = JSON.parse(response.body)['headers']
|
|
67
|
+
puts " X-API-Key: #{headers['X-Api-Key'] || headers['X-API-Key']}"
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
puts "Error: #{e.message}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 2. Example with Bearer Token Authentication
|
|
73
|
+
puts "\n2. Bearer Token Authentication Example:"
|
|
74
|
+
|
|
75
|
+
# Set up our mock response for Bearer authentication
|
|
76
|
+
Excon.stub({}) do |params|
|
|
77
|
+
if params[:headers] && params[:headers]['Authorization'] == 'Bearer test-bearer-token'
|
|
78
|
+
{
|
|
79
|
+
status: 200,
|
|
80
|
+
body: JSON.generate({
|
|
81
|
+
headers: params[:headers]
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
{ status: 401, body: '{"error": "Unauthorized"}' }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Create a connection with bearer token middleware
|
|
90
|
+
connection = Excon.new('https://httpbin.org')
|
|
91
|
+
bearer_middleware = Legate::Auth::MiddlewareFactory.create_bearer(
|
|
92
|
+
token: 'test-bearer-token',
|
|
93
|
+
token_store: token_store
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Configure the connection to use our middleware
|
|
97
|
+
connection.data[:middlewares] = connection.data[:middlewares].dup
|
|
98
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
99
|
+
connection.data[:auth_middleware] = bearer_middleware
|
|
100
|
+
|
|
101
|
+
# Make a request with bearer authentication
|
|
102
|
+
begin
|
|
103
|
+
response = connection.request(method: :get, path: '/headers')
|
|
104
|
+
|
|
105
|
+
puts 'Request Headers:'
|
|
106
|
+
headers = JSON.parse(response.body)['headers']
|
|
107
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
puts "Error: #{e.message}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# 3. Example with OAuth2 Client Credentials (simulation)
|
|
113
|
+
puts "\n3. OAuth2 Client Credentials Example (Simulated):"
|
|
114
|
+
|
|
115
|
+
# In a real scenario, this would communicate with an actual OAuth2 server
|
|
116
|
+
# For this example, we'll use a mock
|
|
117
|
+
class MockOAuth2Scheme < Legate::Auth::Scheme
|
|
118
|
+
def scheme_type
|
|
119
|
+
:oauth2
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def supports_exchange?
|
|
123
|
+
true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def supports_refresh?
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def exchange_token(_credential)
|
|
131
|
+
# Simulate token exchange
|
|
132
|
+
Legate::Auth::ExchangedCredential.new(
|
|
133
|
+
auth_type: :oauth2,
|
|
134
|
+
access_token: "mock-access-token-#{rand(1000)}",
|
|
135
|
+
refresh_token: "mock-refresh-token-#{rand(1000)}",
|
|
136
|
+
expires_at: Time.now + 3600,
|
|
137
|
+
scope: 'read write'
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def refresh_token(token, _credential)
|
|
142
|
+
# Simulate token refresh
|
|
143
|
+
Legate::Auth::ExchangedCredential.new(
|
|
144
|
+
auth_type: :oauth2,
|
|
145
|
+
access_token: "mock-refreshed-token-#{rand(1000)}",
|
|
146
|
+
refresh_token: token.refresh_token,
|
|
147
|
+
expires_at: Time.now + 3600,
|
|
148
|
+
scope: token.scope
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def apply_to_request(request, credential)
|
|
153
|
+
# Apply the token to the request
|
|
154
|
+
request[:headers] ||= {}
|
|
155
|
+
|
|
156
|
+
request[:headers]['Authorization'] = "Bearer #{credential.access_token}" if credential.is_a?(Legate::Auth::ExchangedCredential) && credential.access_token
|
|
157
|
+
|
|
158
|
+
request
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Set up stub for OAuth2 authentication to capture any Bearer token
|
|
163
|
+
Excon.stub({}) do |params|
|
|
164
|
+
if params[:headers] && params[:headers]['Authorization'] && params[:headers]['Authorization'].start_with?('Bearer ')
|
|
165
|
+
{
|
|
166
|
+
status: 200,
|
|
167
|
+
body: JSON.generate({
|
|
168
|
+
headers: params[:headers]
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
else
|
|
172
|
+
{ status: 401, body: '{"error": "Unauthorized"}' }
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
begin
|
|
177
|
+
# Create the scheme and credential
|
|
178
|
+
oauth2_scheme = MockOAuth2Scheme.new
|
|
179
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
180
|
+
auth_type: :oauth2,
|
|
181
|
+
client_id: 'test-client-id',
|
|
182
|
+
client_secret: 'test-client-secret'
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Create a connection
|
|
186
|
+
connection = Excon.new('https://httpbin.org')
|
|
187
|
+
|
|
188
|
+
# Create the middleware instance
|
|
189
|
+
oauth2_middleware = Legate::Auth::ExconMiddleware.new(
|
|
190
|
+
nil,
|
|
191
|
+
scheme: oauth2_scheme,
|
|
192
|
+
credential: oauth2_credential,
|
|
193
|
+
token_store: token_store,
|
|
194
|
+
auto_retry: true,
|
|
195
|
+
max_retries: 2,
|
|
196
|
+
backoff_strategy: :exponential,
|
|
197
|
+
backoff_factor: 0.5
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Properly configure the connection
|
|
201
|
+
connection.data[:middlewares] = connection.data[:middlewares].dup
|
|
202
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
203
|
+
connection.data[:auth_middleware] = oauth2_middleware
|
|
204
|
+
|
|
205
|
+
# Make a request - middleware will handle token exchange and apply the token
|
|
206
|
+
response = connection.request(method: :get, path: '/headers')
|
|
207
|
+
|
|
208
|
+
puts 'Request Headers:'
|
|
209
|
+
headers = JSON.parse(response.body)['headers']
|
|
210
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
211
|
+
|
|
212
|
+
# Demonstrate token refresh by forcing another request
|
|
213
|
+
# In a real scenario, this would happen automatically when a token expires
|
|
214
|
+
puts "\nForcing token refresh and making another request:"
|
|
215
|
+
token = token_store.get(Legate::Auth::ToolIntegration.generate_cache_key(oauth2_scheme, oauth2_credential))
|
|
216
|
+
token_store.clear(Legate::Auth::ToolIntegration.generate_cache_key(oauth2_scheme, oauth2_credential))
|
|
217
|
+
|
|
218
|
+
# Make another request - middleware should get a new token
|
|
219
|
+
response = connection.request(method: :get, path: '/headers')
|
|
220
|
+
|
|
221
|
+
puts 'Request Headers (after refresh):'
|
|
222
|
+
headers = JSON.parse(response.body)['headers']
|
|
223
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
224
|
+
rescue StandardError => e
|
|
225
|
+
puts "Error: #{e.message}"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# 4. Example with automatic retry on auth failure (simulation)
|
|
229
|
+
puts "\n4. Authentication Failure and Retry Example (Simulated):"
|
|
230
|
+
|
|
231
|
+
class MockRetryAuthScheme < Legate::Auth::Scheme
|
|
232
|
+
def initialize
|
|
233
|
+
@fail_count = 1 # Fail the first request, then succeed
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def scheme_type
|
|
237
|
+
:custom
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def apply_to_request(request, _credential)
|
|
241
|
+
request[:headers] ||= {}
|
|
242
|
+
|
|
243
|
+
if @fail_count > 0
|
|
244
|
+
@fail_count -= 1
|
|
245
|
+
# This will cause a 401 response
|
|
246
|
+
request[:headers]['X-Should-Fail'] = 'true'
|
|
247
|
+
else
|
|
248
|
+
# This will succeed
|
|
249
|
+
request[:headers]['X-Auth-Success'] = 'true'
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
request
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
begin
|
|
257
|
+
# Set up stubs for retry test
|
|
258
|
+
Excon.stub({}) do |params|
|
|
259
|
+
if params[:headers] && params[:headers]['X-Should-Fail'] == 'true'
|
|
260
|
+
# Simulate an authentication failure
|
|
261
|
+
{
|
|
262
|
+
status: 401,
|
|
263
|
+
body: '{"error": "Unauthorized"}'
|
|
264
|
+
}
|
|
265
|
+
elsif params[:headers] && params[:headers]['X-Auth-Success'] == 'true'
|
|
266
|
+
# Simulate success after retry
|
|
267
|
+
{
|
|
268
|
+
status: 200,
|
|
269
|
+
body: JSON.generate({
|
|
270
|
+
status: 'success',
|
|
271
|
+
headers: params[:headers]
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
else
|
|
275
|
+
# Default response
|
|
276
|
+
{
|
|
277
|
+
status: 400,
|
|
278
|
+
body: '{"error": "Bad Request"}'
|
|
279
|
+
}
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Create a simple middleware for the retry test
|
|
284
|
+
retry_scheme = MockRetryAuthScheme.new
|
|
285
|
+
retry_credential = Legate::Auth::Credential.new(auth_type: :custom)
|
|
286
|
+
|
|
287
|
+
# Create a middleware that will automatically retry on auth failure
|
|
288
|
+
retry_middleware = Legate::Auth::ExconMiddleware.new(
|
|
289
|
+
nil,
|
|
290
|
+
scheme: retry_scheme,
|
|
291
|
+
credential: retry_credential,
|
|
292
|
+
auto_retry: true,
|
|
293
|
+
max_retries: 1
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Set up a connection with our middleware
|
|
297
|
+
connection = Excon.new('https://httpbin.org')
|
|
298
|
+
connection.data[:middlewares] = connection.data[:middlewares].dup
|
|
299
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
300
|
+
connection.data[:auth_middleware] = retry_middleware
|
|
301
|
+
|
|
302
|
+
# Make a request that will fail and then retry automatically
|
|
303
|
+
puts 'Making request that will fail authentication and automatically retry:'
|
|
304
|
+
response = connection.request(method: :get, path: '/anything')
|
|
305
|
+
|
|
306
|
+
response_data = JSON.parse(response.body)
|
|
307
|
+
puts "Final response status: #{response.status}"
|
|
308
|
+
puts "Response contains X-Auth-Success header: #{!response_data['headers']['X-Auth-Success'].nil?}"
|
|
309
|
+
|
|
310
|
+
# Clean up stubs
|
|
311
|
+
Excon.stubs.clear
|
|
312
|
+
rescue StandardError => e
|
|
313
|
+
puts "Error: #{e.message}"
|
|
314
|
+
Excon.stubs.clear
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
puts "\nExample complete."
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Enhanced Example of using the Excon middleware for authentication
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use the Legate::Auth::ExconMiddleware with various
|
|
7
|
+
# authentication schemes, including automatic token refresh, retry functionality,
|
|
8
|
+
# and configurable backoff strategies.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ruby examples/advanced/auth/excon_middleware_auth.rb
|
|
12
|
+
|
|
13
|
+
require 'bundler/setup'
|
|
14
|
+
require 'legate'
|
|
15
|
+
require 'json'
|
|
16
|
+
require 'excon'
|
|
17
|
+
|
|
18
|
+
puts 'Legate::Auth Enhanced Excon Middleware Example'
|
|
19
|
+
puts '----------------------------------------'
|
|
20
|
+
|
|
21
|
+
# Initialize a session service and token store
|
|
22
|
+
session_service = Legate::SessionService::Memory.new
|
|
23
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
24
|
+
|
|
25
|
+
# 1. Example with API Key Authentication
|
|
26
|
+
puts "\n1. API Key Authentication Example:"
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
# Create a connection using the convenience method
|
|
30
|
+
connection = Legate::Auth.create_api_key_connection(
|
|
31
|
+
'https://httpbin.org',
|
|
32
|
+
api_key: 'test-api-key-1234',
|
|
33
|
+
location: 'header',
|
|
34
|
+
name: 'X-API-Key',
|
|
35
|
+
token_store: token_store,
|
|
36
|
+
auto_retry: true,
|
|
37
|
+
max_retries: 2,
|
|
38
|
+
backoff_strategy: :exponential,
|
|
39
|
+
backoff_factor: 0.5
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Make sure the middleware is added to the connection's middlewares
|
|
43
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
44
|
+
|
|
45
|
+
# Make a request - the middleware will automatically add the API key header
|
|
46
|
+
response = connection.request(method: :get, path: '/headers')
|
|
47
|
+
|
|
48
|
+
puts 'Request Headers:'
|
|
49
|
+
headers = JSON.parse(response.body)['headers']
|
|
50
|
+
puts " X-API-Key: #{headers['X-Api-Key']}"
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
puts "Error: #{e.message}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# 2. Example with Bearer Token Authentication
|
|
56
|
+
puts "\n2. Bearer Token Authentication Example:"
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
# Create a connection using the convenience method
|
|
60
|
+
connection = Legate::Auth.create_bearer_connection(
|
|
61
|
+
'https://httpbin.org',
|
|
62
|
+
token: 'test-bearer-token-5678',
|
|
63
|
+
token_store: token_store,
|
|
64
|
+
auto_retry: true,
|
|
65
|
+
max_retries: 2,
|
|
66
|
+
backoff_strategy: :jitter, # Use jitter backoff to prevent thundering herd
|
|
67
|
+
backoff_factor: 0.5
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Make sure the middleware is added to the connection's middlewares
|
|
71
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
72
|
+
|
|
73
|
+
# Make a request - the middleware will automatically add the Bearer token
|
|
74
|
+
response = connection.request(method: :get, path: '/headers')
|
|
75
|
+
|
|
76
|
+
puts 'Request Headers:'
|
|
77
|
+
headers = JSON.parse(response.body)['headers']
|
|
78
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
puts "Error: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# 3. Example with Basic Authentication
|
|
84
|
+
puts "\n3. Basic Authentication Example:"
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
# Create a connection using the convenience method
|
|
88
|
+
connection = Legate::Auth.create_basic_auth_connection(
|
|
89
|
+
'https://httpbin.org',
|
|
90
|
+
username: 'testuser',
|
|
91
|
+
password: 'testpassword',
|
|
92
|
+
token_store: token_store,
|
|
93
|
+
auto_retry: true,
|
|
94
|
+
max_retries: 3,
|
|
95
|
+
backoff_strategy: :linear,
|
|
96
|
+
backoff_factor: 1.0
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Make sure the middleware is added to the connection's middlewares
|
|
100
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
101
|
+
|
|
102
|
+
# Make a request - the middleware will automatically add the Basic Auth header
|
|
103
|
+
response = connection.request(method: :get, path: '/headers')
|
|
104
|
+
|
|
105
|
+
puts 'Request Headers:'
|
|
106
|
+
headers = JSON.parse(response.body)['headers']
|
|
107
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
puts "Error: #{e.message}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# 4. Example with OAuth2 Client Credentials (simulation)
|
|
113
|
+
puts "\n4. OAuth2 Client Credentials Example (Simulated):"
|
|
114
|
+
|
|
115
|
+
# In a real scenario, this would communicate with an actual OAuth2 server
|
|
116
|
+
# For this example, we'll use a mock
|
|
117
|
+
class MockOAuth2Scheme < Legate::Auth::Scheme
|
|
118
|
+
def scheme_type
|
|
119
|
+
:oauth2
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def supports_exchange?
|
|
123
|
+
true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def supports_refresh?
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def exchange_token(_credential)
|
|
131
|
+
# Simulate token exchange
|
|
132
|
+
Legate::Auth::ExchangedCredential.new(
|
|
133
|
+
auth_type: :oauth2,
|
|
134
|
+
access_token: "mock-access-token-#{rand(1000)}",
|
|
135
|
+
refresh_token: "mock-refresh-token-#{rand(1000)}",
|
|
136
|
+
expires_at: Time.now + 10, # Short expiry for testing refresh
|
|
137
|
+
scope: 'read write'
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def refresh_token(token, _credential)
|
|
142
|
+
# Simulate token refresh
|
|
143
|
+
Legate::Auth::ExchangedCredential.new(
|
|
144
|
+
auth_type: :oauth2,
|
|
145
|
+
access_token: "mock-refreshed-token-#{rand(1000)}",
|
|
146
|
+
refresh_token: token.refresh_token,
|
|
147
|
+
expires_at: Time.now + 3600,
|
|
148
|
+
scope: token.scope
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def apply_to_request(request, credential)
|
|
153
|
+
# Apply the token to the request
|
|
154
|
+
request[:headers] ||= {}
|
|
155
|
+
|
|
156
|
+
if credential.is_a?(Legate::Auth::ExchangedCredential) && credential.access_token
|
|
157
|
+
request[:headers]['Authorization'] = "Bearer #{credential.access_token}"
|
|
158
|
+
elsif credential[:client_id] && credential[:client_secret]
|
|
159
|
+
# If we don't have a token yet but have client credentials, we could
|
|
160
|
+
# apply client auth here (in a real implementation)
|
|
161
|
+
basic_auth = Base64.strict_encode64("#{credential[:client_id]}:#{credential[:client_secret]}")
|
|
162
|
+
request[:headers]['Authorization'] = "Basic #{basic_auth}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
request
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
begin
|
|
170
|
+
# Create the scheme and credential
|
|
171
|
+
oauth2_scheme = MockOAuth2Scheme.new
|
|
172
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
173
|
+
auth_type: :oauth2,
|
|
174
|
+
client_id: 'test-client-id',
|
|
175
|
+
client_secret: 'test-client-secret'
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Create a connection using the middleware factory
|
|
179
|
+
connection = Legate::Auth.create_connection(
|
|
180
|
+
'https://httpbin.org',
|
|
181
|
+
scheme: oauth2_scheme,
|
|
182
|
+
credential: oauth2_credential,
|
|
183
|
+
token_store: token_store,
|
|
184
|
+
auto_retry: true,
|
|
185
|
+
max_retries: 2,
|
|
186
|
+
backoff_strategy: :exponential,
|
|
187
|
+
backoff_factor: 0.5
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Make sure the middleware is added to the connection's middlewares
|
|
191
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
192
|
+
|
|
193
|
+
# Make a request - middleware will handle token exchange and apply the token
|
|
194
|
+
response = connection.request(method: :get, path: '/headers')
|
|
195
|
+
|
|
196
|
+
puts 'Request Headers:'
|
|
197
|
+
headers = JSON.parse(response.body)['headers']
|
|
198
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
199
|
+
|
|
200
|
+
# Wait for token to expire
|
|
201
|
+
puts "\nWaiting for token to expire (10 seconds)..."
|
|
202
|
+
sleep 11
|
|
203
|
+
|
|
204
|
+
# Make another request - middleware should refresh the token
|
|
205
|
+
response = connection.request(method: :get, path: '/headers')
|
|
206
|
+
|
|
207
|
+
puts 'Request Headers (after refresh):'
|
|
208
|
+
headers = JSON.parse(response.body)['headers']
|
|
209
|
+
puts " Authorization: #{headers['Authorization']}"
|
|
210
|
+
rescue StandardError => e
|
|
211
|
+
puts "Error: #{e.message}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# 5. Example with automatic retry on auth failure (simulation)
|
|
215
|
+
puts "\n5. Authentication Failure and Retry Example (Simulated):"
|
|
216
|
+
|
|
217
|
+
class RetryCounterScheme < Legate::Auth::Scheme
|
|
218
|
+
def initialize
|
|
219
|
+
@attempts = 0
|
|
220
|
+
@max_failures = 2
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def scheme_type
|
|
224
|
+
:custom
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def apply_to_request(request, _credential)
|
|
228
|
+
request[:headers] ||= {}
|
|
229
|
+
|
|
230
|
+
@attempts += 1
|
|
231
|
+
|
|
232
|
+
if @attempts <= @max_failures
|
|
233
|
+
# This will cause a 401 response for the first N attempts
|
|
234
|
+
request[:headers]['X-Should-Fail'] = 'true'
|
|
235
|
+
request[:headers]['X-Attempt'] = @attempts.to_s
|
|
236
|
+
else
|
|
237
|
+
# This will succeed
|
|
238
|
+
request[:headers]['X-Auth-Success'] = 'true'
|
|
239
|
+
request[:headers]['X-Attempt'] = @attempts.to_s
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
request
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
begin
|
|
247
|
+
# Create a scheme that will fail a certain number of times
|
|
248
|
+
retry_scheme = RetryCounterScheme.new
|
|
249
|
+
retry_credential = Legate::Auth::Credential.new(auth_type: :custom)
|
|
250
|
+
|
|
251
|
+
# Create a middleware that will automatically retry on auth failure
|
|
252
|
+
retry_middleware = Legate::Auth::MiddlewareFactory.create(
|
|
253
|
+
scheme: retry_scheme,
|
|
254
|
+
credential: retry_credential,
|
|
255
|
+
auto_retry: true,
|
|
256
|
+
max_retries: 3,
|
|
257
|
+
backoff_strategy: :exponential,
|
|
258
|
+
backoff_factor: 0.1 # Small factor to make test run quickly
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Set up a connection with our middleware
|
|
262
|
+
connection = Excon.new('https://httpbin.org')
|
|
263
|
+
connection.data[:middlewares] ||= connection.data[:middlewares].dup
|
|
264
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
265
|
+
connection.data[:auth_middleware] = retry_middleware
|
|
266
|
+
|
|
267
|
+
# Use a stub to simulate a 401 response followed by success
|
|
268
|
+
Excon.stub({}) do |params|
|
|
269
|
+
if params[:headers] && params[:headers]['X-Should-Fail'] == 'true'
|
|
270
|
+
# Log the attempt
|
|
271
|
+
attempt = params[:headers]['X-Attempt']
|
|
272
|
+
puts "Request attempt #{attempt} - Authentication failed"
|
|
273
|
+
|
|
274
|
+
# Simulate an authentication failure
|
|
275
|
+
{
|
|
276
|
+
status: 401,
|
|
277
|
+
body: "{\"error\": \"Unauthorized\", \"attempt\": #{attempt}}"
|
|
278
|
+
}
|
|
279
|
+
else
|
|
280
|
+
# Log the successful attempt
|
|
281
|
+
attempt = params[:headers]['X-Attempt']
|
|
282
|
+
puts "Request attempt #{attempt} - Authentication succeeded"
|
|
283
|
+
|
|
284
|
+
# Simulate success
|
|
285
|
+
{
|
|
286
|
+
status: 200,
|
|
287
|
+
body: "{\"status\": \"success\", \"attempt\": #{attempt}}"
|
|
288
|
+
}
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Make a request that will fail and then retry automatically
|
|
293
|
+
puts 'Making request that will fail authentication and retry automatically:'
|
|
294
|
+
response = connection.request(method: :get, path: '/anything')
|
|
295
|
+
|
|
296
|
+
puts "Final response status: #{response[:status]}"
|
|
297
|
+
puts "Response body: #{response[:body]}"
|
|
298
|
+
|
|
299
|
+
# Clean up stubs
|
|
300
|
+
Excon.stubs.clear
|
|
301
|
+
rescue StandardError => e
|
|
302
|
+
puts "Error: #{e.message}"
|
|
303
|
+
Excon.stubs.clear
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# 6. Example with retry on rate limiting (simulation)
|
|
307
|
+
puts "\n6. Rate Limiting and Retry Example (Simulated):"
|
|
308
|
+
|
|
309
|
+
class RateLimitScheme < Legate::Auth::Scheme
|
|
310
|
+
def initialize
|
|
311
|
+
@attempts = 0
|
|
312
|
+
@rate_limit_count = 2 # Will hit rate limit twice before succeeding
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def scheme_type
|
|
316
|
+
:custom
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def apply_to_request(request, _credential)
|
|
320
|
+
request[:headers] ||= {}
|
|
321
|
+
|
|
322
|
+
@attempts += 1
|
|
323
|
+
request[:headers]['X-Attempt'] = @attempts.to_s
|
|
324
|
+
|
|
325
|
+
request[:headers]['X-Rate-Limited'] = if @attempts <= @rate_limit_count
|
|
326
|
+
# This will trigger rate limiting
|
|
327
|
+
'true'
|
|
328
|
+
else
|
|
329
|
+
# This will succeed
|
|
330
|
+
'false'
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
request
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
begin
|
|
338
|
+
# Create a scheme that will trigger rate limiting
|
|
339
|
+
rate_limit_scheme = RateLimitScheme.new
|
|
340
|
+
rate_limit_credential = Legate::Auth::Credential.new(auth_type: :custom)
|
|
341
|
+
|
|
342
|
+
# Create a middleware that will handle rate limiting
|
|
343
|
+
rate_limit_middleware = Legate::Auth::MiddlewareFactory.create(
|
|
344
|
+
scheme: rate_limit_scheme,
|
|
345
|
+
credential: rate_limit_credential,
|
|
346
|
+
auto_retry: true,
|
|
347
|
+
max_retries: 5,
|
|
348
|
+
backoff_strategy: :fibonacci, # Use fibonacci backoff for rate limiting
|
|
349
|
+
backoff_factor: 0.1, # Small factor to make test run quickly
|
|
350
|
+
retry_on: [429] # Explicitly retry on 429 Too Many Requests
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Set up a connection with our middleware
|
|
354
|
+
connection = Excon.new('https://httpbin.org')
|
|
355
|
+
connection.data[:middlewares] ||= connection.data[:middlewares].dup
|
|
356
|
+
connection.data[:middlewares] << Legate::Auth::ExconMiddleware unless connection.data[:middlewares].include?(Legate::Auth::ExconMiddleware)
|
|
357
|
+
connection.data[:auth_middleware] = rate_limit_middleware
|
|
358
|
+
|
|
359
|
+
# Use a stub to simulate rate limiting
|
|
360
|
+
Excon.stub({}) do |params|
|
|
361
|
+
if params[:headers] && params[:headers]['X-Rate-Limited'] == 'true'
|
|
362
|
+
# Log the attempt
|
|
363
|
+
attempt = params[:headers]['X-Attempt']
|
|
364
|
+
puts "Request attempt #{attempt} - Rate limited"
|
|
365
|
+
|
|
366
|
+
# Simulate rate limiting with Retry-After header
|
|
367
|
+
{
|
|
368
|
+
status: 429,
|
|
369
|
+
headers: { 'Retry-After' => '1' }, # Suggest a 1 second retry time
|
|
370
|
+
body: "{\"error\": \"Too Many Requests\", \"attempt\": #{attempt}}"
|
|
371
|
+
}
|
|
372
|
+
else
|
|
373
|
+
# Log the successful attempt
|
|
374
|
+
attempt = params[:headers]['X-Attempt']
|
|
375
|
+
puts "Request attempt #{attempt} - Request succeeded"
|
|
376
|
+
|
|
377
|
+
# Simulate success
|
|
378
|
+
{
|
|
379
|
+
status: 200,
|
|
380
|
+
body: "{\"status\": \"success\", \"attempt\": #{attempt}}"
|
|
381
|
+
}
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Make a request that will be rate limited and then retry automatically
|
|
386
|
+
puts 'Making request that will be rate limited and retry automatically:'
|
|
387
|
+
response = connection.request(method: :get, path: '/anything')
|
|
388
|
+
|
|
389
|
+
puts "Final response status: #{response[:status]}"
|
|
390
|
+
puts "Response body: #{response[:body]}"
|
|
391
|
+
|
|
392
|
+
# Clean up stubs
|
|
393
|
+
Excon.stubs.clear
|
|
394
|
+
rescue StandardError => e
|
|
395
|
+
puts "Error: #{e.message}"
|
|
396
|
+
Excon.stubs.clear
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
puts "\nExample complete."
|