diff --git a/app/models/cinstance.rb b/app/models/cinstance.rb index fb1fd27a42..395b98487b 100644 --- a/app/models/cinstance.rb +++ b/app/models/cinstance.rb @@ -5,7 +5,7 @@ class Cinstance < Contract self.background_deletion = %i[referrer_filters application_keys alerts] - delegate :backend_version, to: :service, allow_nil: true + delegate :backend_version, :audience_mapper_client_id, to: :service, allow_nil: true belongs_to :plan, class_name: 'ApplicationPlan', foreign_key: :plan_id, inverse_of: :cinstances alias application_plan plan diff --git a/app/models/proxy.rb b/app/models/proxy.rb index 5253d5d0ae..a3bb5660ac 100644 --- a/app/models/proxy.rb +++ b/app/models/proxy.rb @@ -146,6 +146,17 @@ def find_policy_config_by(name:, version:) policies_config.find_by(name: name, version: version) end + def audience_mapper_client_id + policy = find_policy_config_by(name: 'token_introspection', version: 'builtin') + return unless policy&.enabled + + config = policy.configuration&.with_indifferent_access || {} + auth_type = config[:auth_type] + return unless auth_type == 'use_3scale_oidc_issuer_endpoint' || auth_type.blank? + + issuer_endpoint_client_id + end + def oidc_configuration super || build_oidc_configuration(standard_flow_enabled: true) end @@ -470,6 +481,14 @@ def proxy self end + private + + def issuer_endpoint_client_id + URI.parse(oidc_issuer_endpoint).user + rescue URI::InvalidURIError + nil + end + protected class PolicyConfig diff --git a/app/models/service.rb b/app/models/service.rb index dd0abdc84d..0243387a68 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -589,7 +589,7 @@ def notify_deletion delegate :oauth?, to: :authentication_scheme? delegate :authentication_method, to: :proxy, prefix: true, allow_nil: true - delegate :oidc?, :pending_affecting_changes?, to: :proxy, allow_nil: true + delegate :oidc?, :pending_affecting_changes?, :audience_mapper_client_id, to: :proxy, allow_nil: true def authentication_scheme? backend_version.to_s.inquiry diff --git a/app/representers/cinstance_representer.rb b/app/representers/cinstance_representer.rb index e239e20b88..a418f5aa1d 100644 --- a/app/representers/cinstance_representer.rb +++ b/app/representers/cinstance_representer.rb @@ -38,8 +38,9 @@ module CinstanceRepresenter oauth.property :client_secret end - with_options(if: ->(*) { service.oidc? }, render_nil: true) do |oidc| - oidc.property :oidc_configuration, decorator: OIDCConfigurationRepresenter, wrap: false + with_options(if: ->(*) { service.oidc? }, render_nil: true) do + property :oidc_configuration, decorator: OIDCConfigurationRepresenter, wrap: false + property :audience_mapper_client_id end def provider_verification_key diff --git a/test/unit/proxy_test.rb b/test/unit/proxy_test.rb index 50cc6cbb21..de8730188e 100644 --- a/test/unit/proxy_test.rb +++ b/test/unit/proxy_test.rb @@ -843,6 +843,70 @@ class ProxyConfigAffectingChangesTest < ActiveSupport::TestCase end end + class AudienceMapperClientIdTest < ActiveSupport::TestCase + ISSUER_URL = 'https://introspection-client:s3cr3t@sso.example.com/auth/realms/master'.freeze + TOKEN_INTROSPECTION_POLICY = { name: 'token_introspection', version: 'builtin' }.freeze + + def setup + @proxy = FactoryBot.build(:simple_proxy) + end + + def proxy_with_policy(config) + @proxy.policies_config = [TOKEN_INTROSPECTION_POLICY.merge(configuration: config, enabled: true)] + @proxy + end + + test 'returns user from oidc_issuer_endpoint when auth_type is use_3scale_oidc_issuer_endpoint' do + proxy = proxy_with_policy('auth_type' => 'use_3scale_oidc_issuer_endpoint') + proxy.oidc_issuer_endpoint = ISSUER_URL + assert_equal 'introspection-client', proxy.audience_mapper_client_id + end + + test 'returns user from oidc_issuer_endpoint when auth_type is blank' do + proxy = proxy_with_policy({}) + proxy.oidc_issuer_endpoint = ISSUER_URL + assert_equal 'introspection-client', proxy.audience_mapper_client_id + end + + test 'returns nil when auth_type is client_id+client_secret' do + proxy = proxy_with_policy('auth_type' => 'client_id+client_secret', 'client_id' => 'my-introspection-client') + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when auth_type is client_secret_jwt' do + proxy = proxy_with_policy('auth_type' => 'client_secret_jwt', 'client_id' => 'my-introspection-client') + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when auth_type is private_key_jwt' do + proxy = proxy_with_policy('auth_type' => 'private_key_jwt', 'client_id' => 'my-introspection-client') + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when policy is disabled' do + proxy = FactoryBot.build(:simple_proxy) + proxy.policies_config = [TOKEN_INTROSPECTION_POLICY.merge(configuration: { 'auth_type' => 'use_3scale_oidc_issuer_endpoint' }, enabled: false)] + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when token introspection policy is absent' do + proxy = FactoryBot.build(:simple_proxy) + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when oidc_issuer_endpoint has no user' do + proxy = proxy_with_policy('auth_type' => 'use_3scale_oidc_issuer_endpoint') + proxy.oidc_issuer_endpoint = 'https://sso.example.com/auth/realms/master' + assert_nil proxy.audience_mapper_client_id + end + + test 'returns nil when oidc_issuer_endpoint is blank' do + proxy = proxy_with_policy('auth_type' => 'use_3scale_oidc_issuer_endpoint') + proxy.oidc_issuer_endpoint = nil + assert_nil proxy.audience_mapper_client_id + end + end + class StaleObjectErrorTest < ActiveSupport::TestCase test 'proxy does not raise stale object error on concurrent touch' do class ProxyWithFiber < ::Proxy