From bdee0ae06666e7d03ac7514fa867a956d825a6c9 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Wed, 16 Aug 2023 17:24:39 -0400 Subject: [PATCH] Integration specs for auth middleware --- .../lib/high_score_service/auth.rb | 4 +- .../rails_json/lib/rails_json/auth.rb | 4 +- .../projections/weather/lib/weather/auth.rb | 4 +- .../white_label/lib/white_label/auth.rb | 5 +- .../white_label/lib/white_label/client.rb | 100 ++--- .../white_label/lib/white_label/config.rb | 21 +- .../projections/white_label/spec/auth_spec.rb | 380 ++++++++++++++++++ .../integration-specs/auth_spec.rb | 380 ++++++++++++++++++ .../generators/AuthResolverGenerator.java | 22 +- .../factories/AuthMiddlewareFactory.java | 6 +- hearth/lib/hearth/context.rb | 2 +- hearth/lib/hearth/identity_resolver.rb | 2 +- hearth/lib/hearth/middleware/auth.rb | 24 +- hearth/lib/hearth/refreshing_identity.rb | 21 +- .../hearth/refreshing_identity_resolver.rb | 2 +- hearth/spec/hearth/context_spec.rb | 18 +- 16 files changed, 864 insertions(+), 131 deletions(-) create mode 100644 codegen/projections/white_label/spec/auth_spec.rb create mode 100644 codegen/smithy-ruby-codegen-test/integration-specs/auth_spec.rb diff --git a/codegen/projections/high_score_service/lib/high_score_service/auth.rb b/codegen/projections/high_score_service/lib/high_score_service/auth.rb index ef5a0ef94..8ba2f5fdc 100644 --- a/codegen/projections/high_score_service/lib/high_score_service/auth.rb +++ b/codegen/projections/high_score_service/lib/high_score_service/auth.rb @@ -9,10 +9,10 @@ module HighScoreService module Auth - Params = Struct.new(:operation_name) + Params = Struct.new(:operation_name, keyword_init: true) SCHEMES = [ - + Hearth::AuthSchemes::Anonymous.new ].freeze class Resolver diff --git a/codegen/projections/rails_json/lib/rails_json/auth.rb b/codegen/projections/rails_json/lib/rails_json/auth.rb index c2e717572..fab7ce306 100644 --- a/codegen/projections/rails_json/lib/rails_json/auth.rb +++ b/codegen/projections/rails_json/lib/rails_json/auth.rb @@ -9,10 +9,10 @@ module RailsJson module Auth - Params = Struct.new(:operation_name) + Params = Struct.new(:operation_name, keyword_init: true) SCHEMES = [ - + Hearth::AuthSchemes::Anonymous.new ].freeze class Resolver diff --git a/codegen/projections/weather/lib/weather/auth.rb b/codegen/projections/weather/lib/weather/auth.rb index 8420efc1f..b12e99db8 100644 --- a/codegen/projections/weather/lib/weather/auth.rb +++ b/codegen/projections/weather/lib/weather/auth.rb @@ -9,10 +9,10 @@ module Weather module Auth - Params = Struct.new(:operation_name) + Params = Struct.new(:operation_name, keyword_init: true) SCHEMES = [ - + Hearth::AuthSchemes::Anonymous.new ].freeze class Resolver diff --git a/codegen/projections/white_label/lib/white_label/auth.rb b/codegen/projections/white_label/lib/white_label/auth.rb index 598759d99..607233cbf 100644 --- a/codegen/projections/white_label/lib/white_label/auth.rb +++ b/codegen/projections/white_label/lib/white_label/auth.rb @@ -9,13 +9,14 @@ module WhiteLabel module Auth - Params = Struct.new(:operation_name) + Params = Struct.new(:operation_name, keyword_init: true) SCHEMES = [ Hearth::AuthSchemes::HTTPApiKey.new, Hearth::AuthSchemes::HTTPBasic.new, Hearth::AuthSchemes::HTTPBearer.new, - Hearth::AuthSchemes::HTTPDigest.new + Hearth::AuthSchemes::HTTPDigest.new, + Hearth::AuthSchemes::Anonymous.new ].freeze class Resolver diff --git a/codegen/projections/white_label/lib/white_label/client.rb b/codegen/projections/white_label/lib/white_label/client.rb index 4afd4b9b9..c719c4d20 100644 --- a/codegen/projections/white_label/lib/white_label/client.rb +++ b/codegen/projections/white_label/lib/white_label/client.rb @@ -146,13 +146,12 @@ def defaults_test(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :defaults_test), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -223,13 +222,12 @@ def endpoint_operation(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :endpoint_operation), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -302,13 +300,12 @@ def endpoint_with_host_label_operation(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :endpoint_with_host_label_operation), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -375,13 +372,12 @@ def http_api_key_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :http_api_key_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -448,13 +444,12 @@ def http_basic_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :http_basic_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -521,13 +516,12 @@ def http_bearer_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :http_bearer_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -594,13 +588,12 @@ def http_digest_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :http_digest_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -834,13 +827,12 @@ def kitchen_sink(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :kitchen_sink), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -911,13 +903,12 @@ def mixin_test(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :mixin_test), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -984,13 +975,12 @@ def no_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :no_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1057,13 +1047,12 @@ def optional_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :optional_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1130,13 +1119,12 @@ def ordered_auth(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :ordered_auth), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1208,13 +1196,12 @@ def paginators_test(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :paginators_test), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1286,13 +1273,12 @@ def paginators_test_with_items(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :paginators_test_with_items), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1367,13 +1353,12 @@ def request_compression_operation(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :request_compression_operation), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1447,13 +1432,12 @@ def request_compression_streaming_operation(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :request_compression_streaming_operation), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1522,13 +1506,12 @@ def streaming_operation(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :streaming_operation), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1597,13 +1580,12 @@ def streaming_with_length(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :streaming_with_length), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1673,13 +1655,12 @@ def waiters_test(params = {}, options = {}, &block) error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :waiters_test), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( @@ -1752,13 +1733,12 @@ def operation____paginators_test_with_bad_names(params = {}, options = {}, &bloc error_inspector_class: Hearth::HTTP::ErrorInspector ) stack.use(Hearth::Middleware::Auth, - http_digest_identity_resolver: options.fetch(:http_digest_identity_resolver, config.http_digest_identity_resolver), + auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), auth_params: Auth::Params.new(operation_name: :operation____paginators_test_with_bad_names), http_api_key_identity_resolver: options.fetch(:http_api_key_identity_resolver, config.http_api_key_identity_resolver), auth_resolver: options.fetch(:auth_resolver, config.auth_resolver), - auth_schemes: options.fetch(:auth_schemes, config.auth_schemes), http_bearer_identity_resolver: options.fetch(:http_bearer_identity_resolver, config.http_bearer_identity_resolver), - http_basic_identity_resolver: options.fetch(:http_basic_identity_resolver, config.http_basic_identity_resolver) + http_login_identity_resolver: options.fetch(:http_login_identity_resolver, config.http_login_identity_resolver) ) stack.use(Hearth::Middleware::Parse, error_parser: Hearth::HTTP::ErrorParser.new( diff --git a/codegen/projections/white_label/lib/white_label/config.rb b/codegen/projections/white_label/lib/white_label/config.rb index 1da4ab387..ef579ea22 100644 --- a/codegen/projections/white_label/lib/white_label/config.rb +++ b/codegen/projections/white_label/lib/white_label/config.rb @@ -31,17 +31,14 @@ module WhiteLabel # @option args [Hearth::IdentityResolver] :http_api_key_identity_resolver # A Hearth::IdentityResolver for the smithy.api httpApiKeyAuth auth scheme. # - # @option args [Hearth::IdentityResolver] :http_basic_identity_resolver - # A Hearth::IdentityResolver for the smithy.api httpBasicAuth auth scheme. - # # @option args [Hearth::IdentityResolver] :http_bearer_identity_resolver # A Hearth::IdentityResolver for the smithy.api httpBearerAuth auth scheme. # # @option args [Hearth::HTTP::Client] :http_client (Hearth::HTTP::Client.new) # The HTTP Client to use for request transport. # - # @option args [Hearth::IdentityResolver] :http_digest_identity_resolver - # A Hearth::IdentityResolver for the smithy.api httpDigestAuth auth scheme. + # @option args [Hearth::IdentityResolver] :http_login_identity_resolver + # A Hearth::IdentityResolver for the smithy.api httpBasicAuth auth scheme. # # @option args [Hearth::InterceptorList] :interceptors (Hearth::InterceptorList.new) # A list of Interceptors to apply to the client. Interceptors are a generic extension point that allows injecting logic at specific stages of execution within the SDK. Logic injection is done with hooks that the interceptor implements. Hooks are either read-only or read/write. Read-only hooks allow an interceptor to read the input, transport request, transport response or output messages. Read/write hooks allow an interceptor to modify one of these messages. @@ -98,16 +95,13 @@ module WhiteLabel # @!attribute http_api_key_identity_resolver # @return [Hearth::IdentityResolver] # - # @!attribute http_basic_identity_resolver - # @return [Hearth::IdentityResolver] - # # @!attribute http_bearer_identity_resolver # @return [Hearth::IdentityResolver] # # @!attribute http_client # @return [Hearth::HTTP::Client] # - # @!attribute http_digest_identity_resolver + # @!attribute http_login_identity_resolver # @return [Hearth::IdentityResolver] # # @!attribute interceptors @@ -144,10 +138,9 @@ module WhiteLabel :disable_request_compression, :endpoint, :http_api_key_identity_resolver, - :http_basic_identity_resolver, :http_bearer_identity_resolver, :http_client, - :http_digest_identity_resolver, + :http_login_identity_resolver, :interceptors, :log_level, :logger, @@ -170,10 +163,9 @@ def validate! Hearth::Validator.validate_types!(disable_request_compression, TrueClass, FalseClass, context: 'config[:disable_request_compression]') Hearth::Validator.validate_types!(endpoint, String, context: 'config[:endpoint]') Hearth::Validator.validate_types!(http_api_key_identity_resolver, Hearth::IdentityResolver, context: 'config[:http_api_key_identity_resolver]') - Hearth::Validator.validate_types!(http_basic_identity_resolver, Hearth::IdentityResolver, context: 'config[:http_basic_identity_resolver]') Hearth::Validator.validate_types!(http_bearer_identity_resolver, Hearth::IdentityResolver, context: 'config[:http_bearer_identity_resolver]') Hearth::Validator.validate_types!(http_client, Hearth::HTTP::Client, context: 'config[:http_client]') - Hearth::Validator.validate_types!(http_digest_identity_resolver, Hearth::IdentityResolver, context: 'config[:http_digest_identity_resolver]') + Hearth::Validator.validate_types!(http_login_identity_resolver, Hearth::IdentityResolver, context: 'config[:http_login_identity_resolver]') Hearth::Validator.validate_types!(interceptors, Hearth::InterceptorList, context: 'config[:interceptors]') Hearth::Validator.validate_types!(log_level, Symbol, context: 'config[:log_level]') Hearth::Validator.validate_types!(logger, Logger, context: 'config[:logger]') @@ -194,10 +186,9 @@ def self.defaults disable_request_compression: [false], endpoint: [proc { |cfg| cfg[:stub_responses] ? 'http://localhost' : nil }], http_api_key_identity_resolver: [proc { |cfg| cfg[:stub_responses] ? Hearth::IdentityResolver.new(proc { Hearth::Identities::HTTPApiKey.new(key: 'stubbed api key') }) : nil }], - http_basic_identity_resolver: [proc { |cfg| cfg[:stub_responses] ? Hearth::IdentityResolver.new(proc { Hearth::Identities::HTTPLogin.new(username: 'stubbed username', password: 'stubbed password') }) : nil }], http_bearer_identity_resolver: [proc { |cfg| cfg[:stub_responses] ? Hearth::IdentityResolver.new(proc { Hearth::Identities::HTTPBearer.new(token: 'stubbed bearer') }) : nil }], http_client: [proc { |cfg| Hearth::HTTP::Client.new(logger: cfg[:logger]) }], - http_digest_identity_resolver: [proc { |cfg| cfg[:stub_responses] ? Hearth::IdentityResolver.new(proc { Hearth::Identities::HTTPLogin.new(username: 'stubbed username', password: 'stubbed password') }) : nil }], + http_login_identity_resolver: [proc { |cfg| cfg[:stub_responses] ? Hearth::IdentityResolver.new(proc { Hearth::Identities::HTTPLogin.new(username: 'stubbed username', password: 'stubbed password') }) : nil }], interceptors: [proc { Hearth::InterceptorList.new }], log_level: [:info], logger: [proc { |cfg| Logger.new($stdout, level: cfg[:log_level]) }], diff --git a/codegen/projections/white_label/spec/auth_spec.rb b/codegen/projections/white_label/spec/auth_spec.rb new file mode 100644 index 000000000..603fc3138 --- /dev/null +++ b/codegen/projections/white_label/spec/auth_spec.rb @@ -0,0 +1,380 @@ +# frozen_string_literal: true + +require_relative 'spec_helper' + +module WhiteLabel + module Auth + describe Params do + it 'is a struct with operation_name' do + params = Auth::Params.new(operation_name: :operation) + expect(params.operation_name).to eq(:operation) + end + end + + describe SCHEMES do + it 'is a constant with supported schemes' do + actual = Auth::SCHEMES.map(&:scheme_id) + expected = [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#noAuth' + ] + expect(actual).to eq(expected) + end + end + + describe Resolver do + context ':http_api_key_auth' do + it 'resolves httpApiKeyAuth' do + params = Params.new(operation_name: :http_api_key_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpApiKeyAuth') + end + + it 'has signing properties' do + params = Params.new(operation_name: :http_api_key_auth) + options = subject.resolve(params) + expect(options.first.signer_properties).to eq( + name: 'X-API-Key', + in: 'header', + scheme: 'Authorization' + ) + end + end + + context ':http_basic_auth' do + it 'resolves httpBasicAuth' do + params = Params.new(operation_name: :http_basic_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpBasicAuth') + end + end + + context ':http_bearer_auth' do + it 'resolves httpBearerAuth' do + params = Params.new(operation_name: :http_bearer_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpBearerAuth') + end + end + + context ':http_digest_auth' do + it 'resolves httpDigestAuth' do + params = Params.new(operation_name: :http_digest_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpDigestAuth') + end + end + + context ':no_auth' do + it 'resolves noAuth' do + params = Params.new(operation_name: :no_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#noAuth') + end + end + + context ':optional_auth' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#noAuth' + ] + end + + it 'resolves all auth including noAuth' do + params = Params.new(operation_name: :optional_auth) + options = subject.resolve(params) + expect(options.size).to eq(5) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + + context ':ordered_auth' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpBasicAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpApiKeyAuth' + ] + end + + it 'resolves all auth in order' do + params = Params.new(operation_name: :ordered_auth) + options = subject.resolve(params) + expect(options.size).to eq(4) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + + context 'operation without auth traits' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth' + ] + end + + it 'resolves all auth without noAuth' do + params = Params.new(operation_name: :kitchen_sink) + options = subject.resolve(params) + expect(options.size).to eq(4) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + end + end + + describe Config do + it 'adds identity resolvers to config' do + expect(subject.respond_to?(:http_api_key_identity_resolver)).to be(true) + expect(subject.respond_to?(:http_bearer_identity_resolver)).to be(true) + expect(subject.respond_to?(:http_login_identity_resolver)).to be(true) + end + + it 'validates identity resolvers' do + msg = /to be in \[Hearth::IdentityResolver\], got String/ + expect { + Config.new(http_api_key_identity_resolver: 'foo') + }.to raise_error(ArgumentError, msg) + + expect { + Config.new(http_bearer_identity_resolver: 'foo') + }.to raise_error(ArgumentError, msg) + + expect { + Config.new(http_login_identity_resolver: 'foo') + }.to raise_error(ArgumentError, msg) + end + + it 'does not set defaults' do + expect(subject.http_api_key_identity_resolver).to be(nil) + expect(subject.http_bearer_identity_resolver).to be(nil) + expect(subject.http_login_identity_resolver).to be(nil) + end + + context 'stub_responses' do + subject { Config.new(stub_responses: true) } + + it 'sets default identities' do + expect(subject.http_api_key_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_api_key_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPApiKey) + + expect(subject.http_bearer_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_bearer_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPBearer) + + expect(subject.http_login_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_login_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPLogin) + end + end + end + + describe Client do + let(:config) do + Config.new(**{ stub_responses: true }.merge(config_hash)) + end + + let(:identity_resolver) do + Hearth::IdentityResolver.new(proc { identity }) + end + + let(:client) { Client.new(config) } + + let(:before_sign) do + Class.new do + def initialize(&block) + @block = block + end + + def read_before_signing(context) + @block.call(context) + end + end + end + + describe '#http_api_key_auth' do + let(:config_hash) do + { http_api_key_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPApiKey.new(key: 'foo') + end + + it 'resolves httpApiKeyAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPApiKey) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpApiKeyAuth') + end + + client.http_api_key_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_basic_auth' do + let(:config_hash) do + { http_login_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + it 'resolves httpBasicAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPBasic) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpBasicAuth') + end + + client.http_basic_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_bearer_auth' do + let(:config_hash) do + { http_bearer_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPBearer.new(token: 'foo') + end + + it 'resolves httpBearerAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPBearer) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpBearerAuth') + end + + client.http_bearer_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_digest_auth' do + let(:config_hash) do + { http_login_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + it 'resolves httpDigestAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPDigest) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpDigestAuth') + end + + client.http_basic_auth({}, interceptors: [interceptor]) + end + end + + describe '#optional_auth' do + context 'no resolvers' do + let(:config_hash) do + { + http_login_identity_resolver: nil, + http_bearer_identity_resolver: nil, + http_api_key_identity_resolver: nil + } + end + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.optional_auth({}, interceptors: [interceptor]) + end + end + + context 'only anonymous auth scheme' do + # default identity resolvers are configured + let(:config_hash) do + { auth_schemes: [Hearth::AuthSchemes::Anonymous.new] } + end + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.optional_auth({}, interceptors: [interceptor]) + end + end + end + + describe '#no_auth' do + # default identity resolvers are configured + let(:config_hash) { {} } + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.no_auth({}, interceptors: [interceptor]) + end + end + + describe '#ordered_auth' do + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + let(:config_hash) do + { + http_login_identity_resolver: identity_resolver, + http_bearer_identity_resolver: nil, + http_api_key_identity_resolver: nil + } + end + + it 'resolves httpDigestAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPDigest) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpDigestAuth') + end + + client.ordered_auth({}, interceptors: [interceptor]) + end + end + end +end diff --git a/codegen/smithy-ruby-codegen-test/integration-specs/auth_spec.rb b/codegen/smithy-ruby-codegen-test/integration-specs/auth_spec.rb new file mode 100644 index 000000000..0e18ec61f --- /dev/null +++ b/codegen/smithy-ruby-codegen-test/integration-specs/auth_spec.rb @@ -0,0 +1,380 @@ +# frozen_string_literal: true + +require_relative 'spec_helper' + +module WhiteLabel + module Auth + describe Params do + it 'is a struct with operation_name' do + params = Auth::Params.new(operation_name: :operation) + expect(params.operation_name).to eq(:operation) + end + end + + describe SCHEMES do + it 'is a constant with supported schemes' do + actual = Auth::SCHEMES.map(&:scheme_id) + expected = [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#noAuth' + ] + expect(actual).to eq(expected) + end + end + + describe Resolver do + context ':http_api_key_auth' do + it 'resolves httpApiKeyAuth' do + params = Params.new(operation_name: :http_api_key_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpApiKeyAuth') + end + + it 'has signing properties' do + params = Params.new(operation_name: :http_api_key_auth) + options = subject.resolve(params) + expect(options.first.signer_properties).to eq( + name: 'X-API-Key', + in: 'header', + scheme: 'Authorization' + ) + end + end + + context ':http_basic_auth' do + it 'resolves httpBasicAuth' do + params = Params.new(operation_name: :http_basic_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpBasicAuth') + end + end + + context ':http_bearer_auth' do + it 'resolves httpBearerAuth' do + params = Params.new(operation_name: :http_bearer_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpBearerAuth') + end + end + + context ':http_digest_auth' do + it 'resolves httpDigestAuth' do + params = Params.new(operation_name: :http_digest_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#httpDigestAuth') + end + end + + context ':no_auth' do + it 'resolves noAuth' do + params = Params.new(operation_name: :no_auth) + options = subject.resolve(params) + expect(options.first.scheme_id).to eq('smithy.api#noAuth') + end + end + + context ':optional_auth' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#noAuth' + ] + end + + it 'resolves all auth including noAuth' do + params = Params.new(operation_name: :optional_auth) + options = subject.resolve(params) + expect(options.size).to eq(5) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + + context ':ordered_auth' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpBasicAuth', + 'smithy.api#httpDigestAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpApiKeyAuth' + ] + end + + it 'resolves all auth in order' do + params = Params.new(operation_name: :ordered_auth) + options = subject.resolve(params) + expect(options.size).to eq(4) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + + context 'operation without auth traits' do + let(:auth_option_schemes) do + [ + 'smithy.api#httpApiKeyAuth', + 'smithy.api#httpBasicAuth', + 'smithy.api#httpBearerAuth', + 'smithy.api#httpDigestAuth' + ] + end + + it 'resolves all auth without noAuth' do + params = Params.new(operation_name: :kitchen_sink) + options = subject.resolve(params) + expect(options.size).to eq(4) + expect(options.map(&:scheme_id)).to eq(auth_option_schemes) + end + end + end + end + + describe Config do + it 'adds identity resolvers to config' do + expect(subject.respond_to?(:http_api_key_identity_resolver)).to be(true) + expect(subject.respond_to?(:http_bearer_identity_resolver)).to be(true) + expect(subject.respond_to?(:http_login_identity_resolver)).to be(true) + end + + it 'validates identity resolvers' do + msg = /to be in \[Hearth::IdentityResolver\], got String/ + expect do + Config.new(http_api_key_identity_resolver: 'foo') + end.to raise_error(ArgumentError, msg) + + expect do + Config.new(http_bearer_identity_resolver: 'foo') + end.to raise_error(ArgumentError, msg) + + expect do + Config.new(http_login_identity_resolver: 'foo') + end.to raise_error(ArgumentError, msg) + end + + it 'does not set defaults' do + expect(subject.http_api_key_identity_resolver).to be(nil) + expect(subject.http_bearer_identity_resolver).to be(nil) + expect(subject.http_login_identity_resolver).to be(nil) + end + + context 'stub_responses' do + subject { Config.new(stub_responses: true) } + + it 'sets default identities' do + expect(subject.http_api_key_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_api_key_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPApiKey) + + expect(subject.http_bearer_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_bearer_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPBearer) + + expect(subject.http_login_identity_resolver) + .to be_a(Hearth::IdentityResolver) + expect(subject.http_login_identity_resolver.identity) + .to be_a(Hearth::Identities::HTTPLogin) + end + end + end + + describe Client do + let(:config) do + Config.new(**{ stub_responses: true }.merge(config_hash)) + end + + let(:identity_resolver) do + Hearth::IdentityResolver.new(proc { identity }) + end + + let(:client) { Client.new(config) } + + let(:before_sign) do + Class.new do + def initialize(&block) + @block = block + end + + def read_before_signing(context) + @block.call(context) + end + end + end + + describe '#http_api_key_auth' do + let(:config_hash) do + { http_api_key_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPApiKey.new(key: 'foo') + end + + it 'resolves httpApiKeyAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPApiKey) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpApiKeyAuth') + end + + client.http_api_key_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_basic_auth' do + let(:config_hash) do + { http_login_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + it 'resolves httpBasicAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPBasic) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpBasicAuth') + end + + client.http_basic_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_bearer_auth' do + let(:config_hash) do + { http_bearer_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPBearer.new(token: 'foo') + end + + it 'resolves httpBearerAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPBearer) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpBearerAuth') + end + + client.http_bearer_auth({}, interceptors: [interceptor]) + end + end + + describe '#http_digest_auth' do + let(:config_hash) do + { http_login_identity_resolver: identity_resolver } + end + + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + it 'resolves httpDigestAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPDigest) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpDigestAuth') + end + + client.http_basic_auth({}, interceptors: [interceptor]) + end + end + + describe '#optional_auth' do + context 'no resolvers' do + let(:config_hash) do + { + http_login_identity_resolver: nil, + http_bearer_identity_resolver: nil, + http_api_key_identity_resolver: nil + } + end + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.optional_auth({}, interceptors: [interceptor]) + end + end + + context 'only anonymous auth scheme' do + # default identity resolvers are configured + let(:config_hash) do + { auth_schemes: [Hearth::AuthSchemes::Anonymous.new] } + end + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.optional_auth({}, interceptors: [interceptor]) + end + end + end + + describe '#no_auth' do + # default identity resolvers are configured + let(:config_hash) { {} } + + it 'resolves noAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::Anonymous) + expect(auth_scheme.identity).to be_a(Hearth::Identities::Anonymous) + expect(auth_scheme.auth_option.scheme_id).to eq('smithy.api#noAuth') + end + + client.no_auth({}, interceptors: [interceptor]) + end + end + + describe '#ordered_auth' do + let(:identity) do + Hearth::Identities::HTTPLogin.new(username: 'foo', password: 'bar') + end + + let(:config_hash) do + { + http_login_identity_resolver: identity_resolver, + http_bearer_identity_resolver: nil, + http_api_key_identity_resolver: nil + } + end + + it 'resolves httpDigestAuth' do + interceptor = before_sign.new do |context| + auth_scheme = context.auth_scheme + expect(auth_scheme).to be_a(Hearth::AuthSchemes::HTTPDigest) + expect(auth_scheme.identity).to be(identity) + expect(auth_scheme.auth_option.scheme_id) + .to eq('smithy.api#httpDigestAuth') + end + + client.ordered_auth({}, interceptors: [interceptor]) + end + end + end +end diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/AuthResolverGenerator.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/AuthResolverGenerator.java index 2cefc1bd5..8608bdf5b 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/AuthResolverGenerator.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/AuthResolverGenerator.java @@ -77,16 +77,28 @@ public void render() { private void renderAuthParamsClass(RubyCodeWriter writer) { // TODO: this should have more params when hooked up with endpoint/auth rules? - writer.write("Params = Struct.new(:operation_name)"); + writer.write("Params = Struct.new(:operation_name, keyword_init: true)"); } private void renderAuthSchemesConstant(RubyCodeWriter writer) { writer .openBlock("SCHEMES = [") .call(() -> { - serviceAuthSchemes.forEach((shapeId, trait) -> { - renderAuthScheme(writer, trait); - }); + if (serviceAuthSchemes.isEmpty()) { + renderAuthScheme(writer, new OptionalAuthTrait()); + } else { + serviceAuthSchemes.forEach((shapeId, trait) -> { + renderAuthScheme(writer, trait); + }); + // if any operation is optional, also add anonymous auth scheme + operations.stream().forEach(operation -> { + boolean isOptional = operation.hasTrait(OptionalAuthTrait.class); + if (isOptional) { + OptionalAuthTrait trait = operation.getTrait(OptionalAuthTrait.class).get(); + renderAuthScheme(writer, trait); + } + }); + } }) .unwrite(",\n") .write("") @@ -102,6 +114,8 @@ private void renderAuthScheme(RubyCodeWriter writer, Trait trait) { writer.write("$L::HTTPBearer.new,", Hearth.AUTH_SCHEMES); } else if (trait instanceof HttpDigestAuthTrait) { writer.write("$L::HTTPDigest.new,", Hearth.AUTH_SCHEMES); + } else if (trait instanceof OptionalAuthTrait) { + writer.write("$L::Anonymous.new,", Hearth.AUTH_SCHEMES); } else { // Custom auth schemes not supported right now } diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/AuthMiddlewareFactory.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/AuthMiddlewareFactory.java index 919565bee..947be4a43 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/AuthMiddlewareFactory.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/factories/AuthMiddlewareFactory.java @@ -109,10 +109,8 @@ private static String identityResolverConfigNameForAuthTrait(Trait trait) { return "http_api_key_identity_resolver"; } else if (trait instanceof HttpBearerAuthTrait) { return "http_bearer_identity_resolver"; - } else if (trait instanceof HttpBasicAuthTrait) { - return "http_basic_identity_resolver"; - } else if (trait instanceof HttpDigestAuthTrait) { - return "http_digest_identity_resolver"; + } else if (trait instanceof HttpBasicAuthTrait || trait instanceof HttpDigestAuthTrait) { + return "http_login_identity_resolver"; } else { throw new IllegalStateException("Unknown auth trait: " + trait); } diff --git a/hearth/lib/hearth/context.rb b/hearth/lib/hearth/context.rb index b673e86ac..727ed14a7 100644 --- a/hearth/lib/hearth/context.rb +++ b/hearth/lib/hearth/context.rb @@ -37,7 +37,7 @@ def initialize(options = {}) # @return [Array] An ordered list of interceptors attr_reader :interceptors - # @return [SelectedAuthScheme, nil] The auth scheme selected for the request. + # @return [SelectedAuthScheme, nil] The auth scheme for the request. attr_accessor :auth_scheme # @api private diff --git a/hearth/lib/hearth/identity_resolver.rb b/hearth/lib/hearth/identity_resolver.rb index e0c369d48..882c621f8 100644 --- a/hearth/lib/hearth/identity_resolver.rb +++ b/hearth/lib/hearth/identity_resolver.rb @@ -10,7 +10,7 @@ def initialize(proc) end # @param [Hash] properties - def identity(properties) + def identity(properties = {}) @proc.call(properties) end end diff --git a/hearth/lib/hearth/middleware/auth.rb b/hearth/lib/hearth/middleware/auth.rb index 602632df9..b993ce11b 100644 --- a/hearth/lib/hearth/middleware/auth.rb +++ b/hearth/lib/hearth/middleware/auth.rb @@ -13,11 +13,12 @@ def initialize(app, auth_resolver:, auth_params:, @auth_params = auth_params @auth_schemes = auth_schemes.to_h { |s| [s.scheme_id, s] } - @identity_resolvers = {} + @identity_resolvers = { + Identities::Anonymous => anonymous_identity_resolver + } kwargs.each do |key, value| next unless key.end_with?('_identity_resolver') - instance_variable_set("@#{key}", value) @identity_resolvers[identity_type_for(key)] = value end end @@ -35,11 +36,15 @@ def call(input, context) SelectedAuthScheme = Struct.new(:identity, :signer, :auth_option) + def anonymous_identity_resolver + Hearth::IdentityResolver.new(proc { Identities::Anonymous.new }) + end + def identity_type_for(config_key) case config_key when :http_api_key_identity_resolver Identities::HTTPApiKey - when :http_basic_identity_resolver, :http_digest_identity_resolver + when :http_login_identity_resolver Identities::HTTPLogin when :http_bearer_identity_resolver Identities::HTTPBearer @@ -52,12 +57,12 @@ def select_auth_scheme(auth_options) failures = [] auth_options.each do |auth_option| - if auth_option.scheme_id == 'smithy.api#noAuth' - return SelectedAuthScheme.new(nil, nil, auth_option) - end - auth_scheme = @auth_schemes[auth_option.scheme_id] - selected_auth_scheme = try_load_auth_scheme(auth_option, auth_scheme, failures) + selected_auth_scheme = try_load_auth_scheme( + auth_option, + auth_scheme, + failures + ) return selected_auth_scheme if selected_auth_scheme end @@ -68,7 +73,8 @@ def select_auth_scheme(auth_options) def try_load_auth_scheme(auth_option, auth_scheme, failures) scheme_id = auth_option.scheme_id unless auth_scheme - failures << "Auth scheme #{scheme_id} was not enabled for this request" + failures << "Auth scheme #{scheme_id} was not enabled " \ + 'for this request' return end diff --git a/hearth/lib/hearth/refreshing_identity.rb b/hearth/lib/hearth/refreshing_identity.rb index 0907ed925..a0e4e3312 100644 --- a/hearth/lib/hearth/refreshing_identity.rb +++ b/hearth/lib/hearth/refreshing_identity.rb @@ -6,11 +6,8 @@ module RefreshingIdentity SYNC_EXPIRATION_LENGTH = 300 # 5 minutes ASYNC_EXPIRATION_LENGTH = 600 # 10 minutes - def initialize(options = {}) + def initialize @mutex = Mutex.new - @before_refresh = options.delete(:before_refresh) - - _refresh end # @return [Identity] @@ -19,11 +16,6 @@ def identity(properties = {}) @identity end - # Refresh identity. - def refresh! - @mutex.synchronize { _refresh } - end - private def sync_expiration_length @@ -34,11 +26,6 @@ def async_expiration_length self.class::ASYNC_EXPIRATION_LENGTH end - def _refresh(properties) - @before_refresh&.call(self) - refresh(properties) - end - # Refreshes identity asynchronously and synchronously. # If we are near to expiration, block while refreshing the identity. # Otherwise, if we're approaching expiration, use the existing identity @@ -49,15 +36,13 @@ def refresh_if_near_expiration!(properties) # then we check within the mutex to avoid a race condition. if near_expiration?(sync_expiration_length) @mutex.synchronize do - _refresh(properties) if near_expiration?(sync_expiration_length) + refresh(properties) if near_expiration?(sync_expiration_length) end elsif @async_refresh && near_expiration?(async_expiration_length) unless @mutex.locked? Thread.new do @mutex.synchronize do - if near_expiration?(async_expiration_length) - _refresh(properties) - end + refresh(properties) if near_expiration?(async_expiration_length) end end end diff --git a/hearth/lib/hearth/refreshing_identity_resolver.rb b/hearth/lib/hearth/refreshing_identity_resolver.rb index 1f314c04d..b862c6b59 100644 --- a/hearth/lib/hearth/refreshing_identity_resolver.rb +++ b/hearth/lib/hearth/refreshing_identity_resolver.rb @@ -7,7 +7,7 @@ class RefreshingIdentityResolver < IdentityResolver include RefreshingIdentity # @param [Hash] properties - def refresh(properties) + def refresh(properties = {}) @identity = @proc.call(properties) end end diff --git a/hearth/spec/hearth/context_spec.rb b/hearth/spec/hearth/context_spec.rb index 421633372..b6b5e7d1b 100644 --- a/hearth/spec/hearth/context_spec.rb +++ b/hearth/spec/hearth/context_spec.rb @@ -7,7 +7,6 @@ module Hearth let(:response) { double('response') } let(:logger) { Logger.new($stdout) } let(:params) { { key: 'value' } } - let(:signer_params) { { region: 'region' } } let(:metadata) { { foo: 'bar' } } subject do @@ -17,7 +16,6 @@ module Hearth response: response, logger: logger, params: params, - signer_params: signer_params, metadata: metadata ) end @@ -30,18 +28,10 @@ module Hearth expect(context.response).to be_nil expect(context.logger).to be_nil expect(context.params).to be_nil - expect(context.signer_params).to eq({}) expect(context.metadata).to eq({}) end end - describe '#signer_params' do - it 'allows for signer params to be set' do - subject.signer_params[:service] = 'service' - expect(subject.signer_params).to include(service: 'service') - end - end - describe '#metadata' do it 'allows for metadata to be set' do subject.metadata[:bar] = 'baz' @@ -61,5 +51,13 @@ module Hearth expect(ictx.output).to eq(output) end end + + describe '#auth_scheme' do + it 'allows for auth_scheme to be set' do + auth_scheme = Hearth::Middleware::Auth::SelectedAuthScheme.new + subject.auth_scheme = auth_scheme + expect(subject.auth_scheme).to eq(auth_scheme) + end + end end end