From 4f681059475f1c72ed803c7d5682c60512ab613b Mon Sep 17 00:00:00 2001 From: Johannes Freden Jansson Date: Thu, 21 Nov 2024 08:28:15 +0100 Subject: [PATCH] Make SAML SP Config Proivder Plugable --- .../authc/InternalRealmsSettings.java | 4 +- .../core/security/authc/RealmSettings.java | 5 + .../authc/saml/SamlRealmSettings.java | 254 +++++++++--------- .../authc/saml/SingleSpSamlRealmSettings.java | 35 +++ .../authc/AuthenticationTestHelper.java | 4 +- .../xpack/security/authc/InternalRealms.java | 15 +- .../authc/saml/SamlMetadataCommand.java | 7 +- .../xpack/security/authc/saml/SamlRealm.java | 66 ++--- .../authc/saml/SingleSamlSpConfiguration.java | 93 +++++++ .../security/authc/saml/SpConfiguration.java | 87 +++--- .../rest/action/saml/SamlBaseRestHandler.java | 4 +- ...sportSamlInvalidateSessionActionTests.java | 12 +- .../saml/TransportSamlLogoutActionTests.java | 21 +- .../security/authc/InternalRealmsTests.java | 19 +- .../xpack/security/authc/RealmsTests.java | 4 +- .../authc/saml/SamlAuthenticatorTests.java | 2 +- .../saml/SamlAuthnRequestBuilderTests.java | 17 +- .../saml/SamlLogoutRequestHandlerTests.java | 2 +- .../SamlLogoutRequestMessageBuilderTests.java | 2 +- ...ogoutResponseHandlerHttpRedirectTests.java | 2 +- .../authc/saml/SamlRealmTestHelper.java | 2 +- .../security/authc/saml/SamlRealmTests.java | 103 +++++-- .../authc/saml/SamlRedirectTests.java | 2 +- .../authc/saml/SamlResponseHandlerTests.java | 2 +- 24 files changed, 486 insertions(+), 278 deletions(-) create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SingleSpSamlRealmSettings.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SingleSamlSpConfiguration.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/InternalRealmsSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/InternalRealmsSettings.java index a00163928e5f8..a00f96fb6a16f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/InternalRealmsSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/InternalRealmsSettings.java @@ -14,7 +14,7 @@ import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; import org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; -import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import java.util.Collections; import java.util.HashSet; @@ -34,7 +34,7 @@ public static Set> getSettings() { set.addAll(LdapRealmSettings.getSettings(LdapRealmSettings.AD_TYPE)); set.addAll(LdapRealmSettings.getSettings(LdapRealmSettings.LDAP_TYPE)); set.addAll(PkiRealmSettings.getSettings()); - set.addAll(SamlRealmSettings.getSettings()); + set.addAll(SingleSpSamlRealmSettings.getSettings()); set.addAll(KerberosRealmSettings.getSettings()); set.addAll(OpenIdConnectRealmSettings.getSettings()); set.addAll(JwtRealmSettings.getSettings()); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java index e6cd3102bd5fb..205241016fa01 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java @@ -14,6 +14,7 @@ import org.elasticsearch.core.Tuple; import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings; import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import java.util.Arrays; import java.util.HashMap; @@ -243,6 +244,10 @@ private static void verifyRealmSettings(RealmConfig.RealmIdentifier identifier, } } + public static String getFullSettingKey(String realmName, Function> setting) { + return setting.apply(SingleSpSamlRealmSettings.TYPE).getConcreteSettingForNamespace(realmName).getKey(); + } + public static String getFullSettingKey(String realmName, Setting.AffixSetting setting) { return setting.getConcreteSettingForNamespace(realmName).getKey(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java index 831afcf44a1d0..a40e0546fdd6a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java @@ -23,168 +23,172 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import static org.elasticsearch.xpack.core.security.authc.support.SecuritySettingsUtil.verifyNonNullNotEmpty; public class SamlRealmSettings { - public static final String TYPE = "saml"; - // these settings will be used under the prefix xpack.security.authc.realms.REALM_NAME. private static final String IDP_METADATA_SETTING_PREFIX = "idp.metadata."; - public static final Setting.AffixSetting IDP_ENTITY_ID = RealmSettings.simpleString( - TYPE, + public static final Function> IDP_ENTITY_ID = (type) -> RealmSettings.simpleString( + type, "idp.entity_id", Setting.Property.NodeScope ); - public static final Setting.AffixSetting IDP_METADATA_PATH = RealmSettings.simpleString( - TYPE, + public static final Function> IDP_METADATA_PATH = (type) -> RealmSettings.simpleString( + type, IDP_METADATA_SETTING_PREFIX + "path", Setting.Property.NodeScope ); - public static final Setting.AffixSetting IDP_METADATA_HTTP_REFRESH = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> IDP_METADATA_HTTP_REFRESH = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), IDP_METADATA_SETTING_PREFIX + "http.refresh", key -> Setting.timeSetting(key, TimeValue.timeValueHours(1), Setting.Property.NodeScope) ); - public static final Setting.AffixSetting IDP_METADATA_HTTP_MIN_REFRESH = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> IDP_METADATA_HTTP_MIN_REFRESH = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), IDP_METADATA_SETTING_PREFIX + "http.minimum_refresh", key -> Setting.timeSetting(key, TimeValue.timeValueMinutes(5), TimeValue.timeValueMillis(500), Setting.Property.NodeScope) ); - public static final Setting.AffixSetting IDP_METADATA_HTTP_FAIL_ON_ERROR = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> IDP_METADATA_HTTP_FAIL_ON_ERROR = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), IDP_METADATA_SETTING_PREFIX + "http.fail_on_error", key -> Setting.boolSetting(key, false, Setting.Property.NodeScope) ); - public static final Setting.AffixSetting IDP_SINGLE_LOGOUT = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> IDP_SINGLE_LOGOUT = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "idp.use_single_logout", key -> Setting.boolSetting(key, true, Setting.Property.NodeScope) ); - public static final Setting.AffixSetting SP_ENTITY_ID = RealmSettings.simpleString( - TYPE, - "sp.entity_id", - Setting.Property.NodeScope - ); - - public static final Setting.AffixSetting SP_ACS = RealmSettings.simpleString(TYPE, "sp.acs", Setting.Property.NodeScope); - public static final Setting.AffixSetting SP_LOGOUT = RealmSettings.simpleString(TYPE, "sp.logout", Setting.Property.NodeScope); - - public static final Setting.AffixSetting NAMEID_FORMAT = RealmSettings.simpleString( - TYPE, + public static final Function> NAMEID_FORMAT = (type) -> RealmSettings.simpleString( + type, "nameid_format", Setting.Property.NodeScope ); - public static final Setting.AffixSetting NAMEID_ALLOW_CREATE = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> NAMEID_ALLOW_CREATE = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "nameid.allow_create", key -> Setting.boolSetting(key, false, Setting.Property.NodeScope) ); - public static final Setting.AffixSetting NAMEID_SP_QUALIFIER = RealmSettings.simpleString( - TYPE, + public static final Function> NAMEID_SP_QUALIFIER = (type) -> RealmSettings.simpleString( + type, "nameid.sp_qualifier", Setting.Property.NodeScope ); - public static final Setting.AffixSetting FORCE_AUTHN = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> FORCE_AUTHN = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "force_authn", key -> Setting.boolSetting(key, false, Setting.Property.NodeScope) ); - public static final Setting.AffixSetting POPULATE_USER_METADATA = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> POPULATE_USER_METADATA = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "populate_user_metadata", key -> Setting.boolSetting(key, true, Setting.Property.NodeScope) ); - public static final AttributeSetting PRINCIPAL_ATTRIBUTE = new AttributeSetting("principal"); - public static final AttributeSettingWithDelimiter GROUPS_ATTRIBUTE = new AttributeSettingWithDelimiter("groups"); - public static final AttributeSetting DN_ATTRIBUTE = new AttributeSetting("dn"); - public static final AttributeSetting NAME_ATTRIBUTE = new AttributeSetting("name"); - public static final AttributeSetting MAIL_ATTRIBUTE = new AttributeSetting("mail"); + public static final Function PRINCIPAL_ATTRIBUTE = (type) -> new AttributeSetting(type, "principal"); + public static final Function GROUPS_ATTRIBUTE = (type) -> new AttributeSettingWithDelimiter( + type, + "groups" + ); + public static final Function DN_ATTRIBUTE = (type) -> new AttributeSetting(type, "dn"); + public static final Function NAME_ATTRIBUTE = (type) -> new AttributeSetting(type, "name"); + public static final Function MAIL_ATTRIBUTE = (type) -> new AttributeSetting(type, "mail"); public static final String ENCRYPTION_SETTING_KEY = "encryption."; - public static final Setting.AffixSetting ENCRYPTION_KEY_ALIAS = RealmSettings.simpleString( - TYPE, + public static final Function> ENCRYPTION_KEY_ALIAS = (type) -> RealmSettings.simpleString( + type, ENCRYPTION_SETTING_KEY + "keystore.alias", Setting.Property.NodeScope ); public static final String SIGNING_SETTING_KEY = "signing."; - public static final Setting.AffixSetting SIGNING_KEY_ALIAS = RealmSettings.simpleString( - TYPE, + public static final Function> SIGNING_KEY_ALIAS = (type) -> RealmSettings.simpleString( + type, SIGNING_SETTING_KEY + "keystore.alias", Setting.Property.NodeScope ); - public static final Setting.AffixSetting> SIGNING_MESSAGE_TYPES = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function>> SIGNING_MESSAGE_TYPES = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "signing.saml_messages", key -> Setting.stringListSetting(key, List.of("*"), Setting.Property.NodeScope) ); - public static final Setting.AffixSetting> REQUESTED_AUTHN_CONTEXT_CLASS_REF = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), - "req_authn_context_class_ref", - key -> Setting.stringListSetting(key, Setting.Property.NodeScope) - ); + public static final Function>> REQUESTED_AUTHN_CONTEXT_CLASS_REF = (type) -> Setting + .affixKeySetting( + RealmSettings.realmSettingPrefix(type), + "req_authn_context_class_ref", + key -> Setting.stringListSetting(key, Setting.Property.NodeScope) + ); - public static final Setting.AffixSetting CLOCK_SKEW = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), + public static final Function> CLOCK_SKEW = (type) -> Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), "allowed_clock_skew", key -> Setting.positiveTimeSetting(key, TimeValue.timeValueMinutes(3), Setting.Property.NodeScope) ); - public static final Setting.AffixSetting> EXCLUDE_ROLES = Setting.affixKeySetting( - RealmSettings.realmSettingPrefix(TYPE), - "exclude_roles", - key -> Setting.stringListSetting(key, new Setting.Validator<>() { - - @Override - public void validate(List excludedRoles) { - excludedRoles.forEach(excludedRole -> verifyNonNullNotEmpty(key, excludedRole)); - } - - @Override - public void validate(List excludedRoles, Map, Object> settings) { - if (false == excludedRoles.isEmpty()) { - final String namespace = EXCLUDE_ROLES.getNamespace(EXCLUDE_ROLES.getConcreteSetting(key)); - final Setting> authorizationRealmsSetting = DelegatedAuthorizationSettings.AUTHZ_REALMS.apply(TYPE) - .getConcreteSettingForNamespace(namespace); - @SuppressWarnings("unchecked") - final List authorizationRealms = (List) settings.get(authorizationRealmsSetting); - if (authorizationRealms != null && false == authorizationRealms.isEmpty()) { - throw new SettingsException( - "Setting [" - + EXCLUDE_ROLES.getConcreteSettingForNamespace(namespace).getKey() - + "] is not permitted when setting [" - + authorizationRealmsSetting.getKey() - + "] is configured." + public static final Function>> EXCLUDE_ROLES = (type) -> { + final AtomicReference>> excludedRolesSettingRef = new AtomicReference<>(); + + excludedRolesSettingRef.set( + Setting.affixKeySetting( + RealmSettings.realmSettingPrefix(type), + "exclude_roles", + key -> Setting.stringListSetting(key, new Setting.Validator<>() { + + @Override + public void validate(List excludedRoles) { + excludedRoles.forEach(excludedRole -> verifyNonNullNotEmpty(key, excludedRole)); + } + + @Override + public void validate(List excludedRoles, Map, Object> settings) { + if (false == excludedRoles.isEmpty()) { + final String namespace = excludedRolesSettingRef.get() + .getNamespace(excludedRolesSettingRef.get().getConcreteSetting(key)); + final Setting> authorizationRealmsSetting = DelegatedAuthorizationSettings.AUTHZ_REALMS.apply(type) + .getConcreteSettingForNamespace(namespace); + @SuppressWarnings("unchecked") + final List authorizationRealms = (List) settings.get(authorizationRealmsSetting); + if (authorizationRealms != null && false == authorizationRealms.isEmpty()) { + throw new SettingsException( + "Setting [" + + excludedRolesSettingRef.get().getConcreteSettingForNamespace(namespace).getKey() + + "] is not permitted when setting [" + + authorizationRealmsSetting.getKey() + + "] is configured." + ); + } + } + } + + @Override + public Iterator> settings() { + final String namespace = excludedRolesSettingRef.get() + .getNamespace(excludedRolesSettingRef.get().getConcreteSetting(key)); + final List> settings = List.of( + DelegatedAuthorizationSettings.AUTHZ_REALMS.apply(type).getConcreteSettingForNamespace(namespace) ); + return settings.iterator(); } - } - } - - @Override - public Iterator> settings() { - final String namespace = EXCLUDE_ROLES.getNamespace(EXCLUDE_ROLES.getConcreteSetting(key)); - final List> settings = List.of( - DelegatedAuthorizationSettings.AUTHZ_REALMS.apply(TYPE).getConcreteSettingForNamespace(namespace) - ); - return settings.iterator(); - } - }, Setting.Property.NodeScope) - ); + }, Setting.Property.NodeScope) + ) + ); + return excludedRolesSettingRef.get(); + }; public static final String SSL_PREFIX = "ssl."; @@ -193,39 +197,37 @@ private SamlRealmSettings() {} /** * @return The {@link Setting setting configuration} for this realm type */ - public static Set> getSettings() { + public static Set> getSettings(String type) { final Set> set = Sets.newHashSet( - IDP_ENTITY_ID, - IDP_METADATA_PATH, - IDP_METADATA_HTTP_REFRESH, - IDP_METADATA_HTTP_MIN_REFRESH, - IDP_METADATA_HTTP_FAIL_ON_ERROR, - IDP_SINGLE_LOGOUT, - SP_ENTITY_ID, - SP_ACS, - SP_LOGOUT, - NAMEID_FORMAT, - NAMEID_ALLOW_CREATE, - NAMEID_SP_QUALIFIER, - FORCE_AUTHN, - POPULATE_USER_METADATA, - CLOCK_SKEW, - ENCRYPTION_KEY_ALIAS, - SIGNING_KEY_ALIAS, - SIGNING_MESSAGE_TYPES, - REQUESTED_AUTHN_CONTEXT_CLASS_REF + IDP_ENTITY_ID.apply(type), + IDP_METADATA_PATH.apply(type), + IDP_METADATA_HTTP_REFRESH.apply(type), + IDP_METADATA_HTTP_MIN_REFRESH.apply(type), + IDP_METADATA_HTTP_FAIL_ON_ERROR.apply(type), + IDP_SINGLE_LOGOUT.apply(type), + NAMEID_FORMAT.apply(type), + NAMEID_ALLOW_CREATE.apply(type), + NAMEID_SP_QUALIFIER.apply(type), + FORCE_AUTHN.apply(type), + POPULATE_USER_METADATA.apply(type), + CLOCK_SKEW.apply(type), + ENCRYPTION_KEY_ALIAS.apply(type), + SIGNING_KEY_ALIAS.apply(type), + SIGNING_MESSAGE_TYPES.apply(type), + REQUESTED_AUTHN_CONTEXT_CLASS_REF.apply(type) ); - set.addAll(X509KeyPairSettings.affix(RealmSettings.realmSettingPrefix(TYPE), ENCRYPTION_SETTING_KEY, false)); - set.addAll(X509KeyPairSettings.affix(RealmSettings.realmSettingPrefix(TYPE), SIGNING_SETTING_KEY, false)); - set.addAll(SSLConfigurationSettings.getRealmSettings(TYPE)); - set.addAll(PRINCIPAL_ATTRIBUTE.settings()); - set.addAll(GROUPS_ATTRIBUTE.settings()); - set.addAll(DN_ATTRIBUTE.settings()); - set.addAll(NAME_ATTRIBUTE.settings()); - set.addAll(MAIL_ATTRIBUTE.settings()); - - set.addAll(DelegatedAuthorizationSettings.getSettings(TYPE)); - set.addAll(RealmSettings.getStandardSettings(TYPE)); + + set.addAll(X509KeyPairSettings.affix(RealmSettings.realmSettingPrefix(type), ENCRYPTION_SETTING_KEY, false)); + set.addAll(X509KeyPairSettings.affix(RealmSettings.realmSettingPrefix(type), SIGNING_SETTING_KEY, false)); + set.addAll(SSLConfigurationSettings.getRealmSettings(type)); + set.addAll(PRINCIPAL_ATTRIBUTE.apply(type).settings()); + set.addAll(GROUPS_ATTRIBUTE.apply(type).settings()); + set.addAll(DN_ATTRIBUTE.apply(type).settings()); + set.addAll(NAME_ATTRIBUTE.apply(type).settings()); + set.addAll(MAIL_ATTRIBUTE.apply(type).settings()); + + set.addAll(DelegatedAuthorizationSettings.getSettings(type)); + set.addAll(RealmSettings.getStandardSettings(type)); return set; } @@ -247,9 +249,9 @@ public static final class AttributeSetting { private final Setting.AffixSetting attribute; private final Setting.AffixSetting pattern; - public AttributeSetting(String name) { - attribute = RealmSettings.simpleString(TYPE, ATTRIBUTES_PREFIX + name, Setting.Property.NodeScope); - pattern = RealmSettings.simpleString(TYPE, ATTRIBUTE_PATTERNS_PREFIX + name, Setting.Property.NodeScope); + public AttributeSetting(String type, String name) { + attribute = RealmSettings.simpleString(type, ATTRIBUTES_PREFIX + name, Setting.Property.NodeScope); + pattern = RealmSettings.simpleString(type, ATTRIBUTE_PATTERNS_PREFIX + name, Setting.Property.NodeScope); } public Collection> settings() { @@ -289,9 +291,9 @@ public AttributeSetting getAttributeSetting() { return attributeSetting; } - public AttributeSettingWithDelimiter(String name) { - this.attributeSetting = new AttributeSetting(name); - this.delimiter = RealmSettings.simpleString(TYPE, ATTRIBUTE_DELIMITERS_PREFIX + name, Setting.Property.NodeScope); + public AttributeSettingWithDelimiter(String type, String name) { + this.attributeSetting = new AttributeSetting(type, name); + this.delimiter = RealmSettings.simpleString(type, ATTRIBUTE_DELIMITERS_PREFIX + name, Setting.Property.NodeScope); } public Setting.AffixSetting getDelimiter() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SingleSpSamlRealmSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SingleSpSamlRealmSettings.java new file mode 100644 index 0000000000000..b627c1fd03d16 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SingleSpSamlRealmSettings.java @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.authc.saml; + +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.xpack.core.security.authc.RealmSettings; + +import java.util.Set; + +public class SingleSpSamlRealmSettings { + public static final String TYPE = "saml"; + + public static final Setting.AffixSetting SP_ENTITY_ID = RealmSettings.simpleString( + TYPE, + "sp.entity_id", + Setting.Property.NodeScope + ); + + public static final Setting.AffixSetting SP_ACS = RealmSettings.simpleString(TYPE, "sp.acs", Setting.Property.NodeScope); + public static final Setting.AffixSetting SP_LOGOUT = RealmSettings.simpleString(TYPE, "sp.logout", Setting.Property.NodeScope); + + public static Set> getSettings() { + Set> samlSettings = SamlRealmSettings.getSettings(TYPE); + samlSettings.add(SP_ENTITY_ID); + samlSettings.add(SP_ACS); + samlSettings.add(SP_LOGOUT); + + return samlSettings; + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java index 483b2426e6ad2..7506437273852 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java @@ -25,7 +25,7 @@ import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; import org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; -import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.RoleDescriptorsIntersection; @@ -193,7 +193,7 @@ private static Supplier randomRealmTypeSupplier(boolean includeInternal) LdapRealmSettings.LDAP_TYPE, JwtRealmSettings.TYPE, OpenIdConnectRealmSettings.TYPE, - SamlRealmSettings.TYPE, + SingleSpSamlRealmSettings.TYPE, KerberosRealmSettings.TYPE, PkiRealmSettings.TYPE, ESTestCase.randomAlphaOfLengthBetween(3, 8) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java index c9c8f156cd5e7..994c0b8b9127a 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java @@ -25,7 +25,7 @@ import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; import org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; -import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.security.Security; @@ -39,6 +39,7 @@ import org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm; import org.elasticsearch.xpack.security.authc.pki.PkiRealm; import org.elasticsearch.xpack.security.authc.saml.SamlRealm; +import org.elasticsearch.xpack.security.authc.saml.SingleSamlSpConfiguration; import org.elasticsearch.xpack.security.authc.support.RoleMappingFileBootstrapCheck; import org.elasticsearch.xpack.security.support.SecurityIndexManager; @@ -63,7 +64,7 @@ public final class InternalRealms { static final String LDAP_TYPE = LdapRealmSettings.LDAP_TYPE; static final String AD_TYPE = LdapRealmSettings.AD_TYPE; static final String PKI_TYPE = PkiRealmSettings.TYPE; - static final String SAML_TYPE = SamlRealmSettings.TYPE; + static final String SAML_TYPE = SingleSpSamlRealmSettings.TYPE; static final String OIDC_TYPE = OpenIdConnectRealmSettings.TYPE; static final String JWT_TYPE = JwtRealmSettings.TYPE; static final String KERBEROS_TYPE = KerberosRealmSettings.TYPE; @@ -154,8 +155,14 @@ public static Map getFactories( PkiRealmSettings.TYPE, config -> new PkiRealm(config, resourceWatcherService, userRoleMapper), // SAML realm - SamlRealmSettings.TYPE, - config -> SamlRealm.create(config, sslService, resourceWatcherService, userRoleMapper), + SingleSpSamlRealmSettings.TYPE, + config -> SamlRealm.create( + config, + sslService, + resourceWatcherService, + userRoleMapper, + SingleSamlSpConfiguration.create(config) + ), // Kerberos realm KerberosRealmSettings.TYPE, config -> new KerberosRealm(config, userRoleMapper, threadPool), diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java index 106b550a1e23c..59e4280bb7748 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java @@ -32,6 +32,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; import org.elasticsearch.xpack.security.authc.saml.SamlSpMetadataBuilder.ContactInfo; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; @@ -166,7 +167,7 @@ EntityDescriptor buildEntityDescriptor(Terminal terminal, OptionSet options, Env final Locale locale = findLocale(options); terminal.println(Terminal.Verbosity.VERBOSE, "Using locale: " + locale.toLanguageTag()); - final SpConfiguration spConfig = SamlRealm.getSpConfiguration(realm); + final SpConfiguration spConfig = SingleSamlSpConfiguration.create(realm); final SamlSpMetadataBuilder builder = new SamlSpMetadataBuilder(locale, spConfig.getEntityId()).assertionConsumerServiceUrl( spConfig.getAscUrl() ) @@ -455,7 +456,7 @@ private RealmConfig findRealm(Terminal terminal, OptionSet options, Environment final Map realms = RealmSettings.getRealmSettings(settings); if (options.has(realmSpec)) { final String name = realmSpec.value(options); - final RealmConfig.RealmIdentifier identifier = new RealmConfig.RealmIdentifier(SamlRealmSettings.TYPE, name); + final RealmConfig.RealmIdentifier identifier = new RealmConfig.RealmIdentifier(SingleSpSamlRealmSettings.TYPE, name); final Settings realmSettings = realms.get(identifier); if (realmSettings == null) { throw new UserException(ExitCodes.CONFIG, "No such realm '" + name + "' defined in " + env.configFile()); @@ -500,7 +501,7 @@ private static RealmConfig buildRealm(RealmConfig.RealmIdentifier identifier, En } private static boolean isSamlRealm(RealmConfig.RealmIdentifier realmIdentifier) { - return SamlRealmSettings.TYPE.equals(realmIdentifier.getType()); + return SingleSpSamlRealmSettings.TYPE.equals(realmIdentifier.getType()); } private Locale findLocale(OptionSet options) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java index 9adfd15e23207..a71e587265346 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java @@ -132,13 +132,9 @@ import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.NAME_ATTRIBUTE; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.POPULATE_USER_METADATA; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.PRINCIPAL_ATTRIBUTE; -import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.REQUESTED_AUTHN_CONTEXT_CLASS_REF; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SIGNING_KEY_ALIAS; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SIGNING_MESSAGE_TYPES; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SIGNING_SETTING_KEY; -import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SP_ACS; -import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SP_ENTITY_ID; -import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SP_LOGOUT; /** * This class is {@link Releasable} because it uses a library that thinks timers and timer tasks @@ -165,7 +161,6 @@ public final class SamlRealm extends Realm implements Releasable { // to assume that there's any relationship between SAML realms. However, because all the metadata loading code is in static methods, we // live with this limitation for now. private static final AtomicBoolean REFRESHING_METADATA = new AtomicBoolean(false); - private final List releasables; private final SamlAuthenticator authenticator; @@ -198,7 +193,8 @@ public static SamlRealm create( RealmConfig config, SSLService sslService, ResourceWatcherService watcherService, - UserRoleMapper roleMapper + UserRoleMapper roleMapper, + SpConfiguration serviceProvider ) throws Exception { SamlUtils.initialize(logger); @@ -217,8 +213,6 @@ public static SamlRealm create( final AbstractReloadingMetadataResolver metadataResolver = tuple.v1(); final Supplier idpDescriptor = tuple.v2(); - final SpConfiguration serviceProvider = getSpConfiguration(config); - final Clock clock = Clock.systemUTC(); final IdpConfiguration idpConfiguration = getIdpConfiguration(config, metadataResolver, idpDescriptor); final TimeValue maxSkew = config.getSetting(CLOCK_SKEW); @@ -301,6 +295,10 @@ public void initialize(Iterable realms, XPackLicenseState licenseState) { delegatedRealms = new DelegatedAuthorizationSupport(realms, config, licenseState); } + static String require(RealmConfig config, Function> settingFactory) { + return require(config, settingFactory.apply(config.type())); + } + static String require(RealmConfig config, Setting.AffixSetting setting) { final String value = config.getSetting(setting); if (value.isEmpty()) { @@ -387,27 +385,12 @@ private static void logDiff(final List newCredentials, final AtomicR } } - static SpConfiguration getSpConfiguration(RealmConfig config) throws IOException, GeneralSecurityException { - final String serviceProviderId = require(config, SP_ENTITY_ID); - final String assertionConsumerServiceURL = require(config, SP_ACS); - final String logoutUrl = config.getSetting(SP_LOGOUT); - final List reqAuthnCtxClassRef = config.getSetting(REQUESTED_AUTHN_CONTEXT_CLASS_REF); - return new SpConfiguration( - serviceProviderId, - assertionConsumerServiceURL, - logoutUrl, - buildSigningConfiguration(config), - buildEncryptionCredential(config), - reqAuthnCtxClassRef - ); - } - // Package-private for testing static List buildEncryptionCredential(RealmConfig config) throws IOException, GeneralSecurityException { return buildCredential( config, RealmSettings.realmSettingPrefix(config.identifier()) + ENCRYPTION_SETTING_KEY, - ENCRYPTION_KEY_ALIAS, + ENCRYPTION_KEY_ALIAS.apply(config.type()), true ); } @@ -416,7 +399,7 @@ static SigningConfiguration buildSigningConfiguration(RealmConfig config) throws final List credentials = buildCredential( config, RealmSettings.realmSettingPrefix(config.identifier()) + SIGNING_SETTING_KEY, - SIGNING_KEY_ALIAS, + SIGNING_KEY_ALIAS.apply(config.type()), false ); if (credentials == null || credentials.isEmpty()) { @@ -573,7 +556,11 @@ public void authenticate(AuthenticationToken authenticationToken, ActionListener } private void buildUser(SamlAttributes attributes, ActionListener> baseListener) { - final String principal = resolveSingleValueAttribute(attributes, principalAttribute, PRINCIPAL_ATTRIBUTE.name(config)); + final String principal = resolveSingleValueAttribute( + attributes, + principalAttribute, + PRINCIPAL_ATTRIBUTE.apply(config.type()).name(config) + ); if (Strings.isNullOrEmpty(principal)) { final String msg = principalAttribute + " not found in saml attributes" @@ -619,9 +606,9 @@ private void buildUser(SamlAttributes attributes, ActionListener userMeta = Map.copyOf(userMetaBuilder); final List groups = groupsAttribute.getAttribute(attributes); - final String dn = resolveSingleValueAttribute(attributes, dnAttribute, DN_ATTRIBUTE.name(config)); - final String name = resolveSingleValueAttribute(attributes, nameAttribute, NAME_ATTRIBUTE.name(config)); - final String mail = resolveSingleValueAttribute(attributes, mailAttribute, MAIL_ATTRIBUTE.name(config)); + final String dn = resolveSingleValueAttribute(attributes, dnAttribute, DN_ATTRIBUTE.apply(config.type()).name(config)); + final String name = resolveSingleValueAttribute(attributes, nameAttribute, NAME_ATTRIBUTE.apply(config.type()).name(config)); + final String mail = resolveSingleValueAttribute(attributes, mailAttribute, MAIL_ATTRIBUTE.apply(config.type()).name(config)); UserRoleMapper.UserData userData = new UserRoleMapper.UserData(principal, dn, groups, userMeta, config); logger.debug("SAML attribute mapping = [{}]", userData); roleMapper.resolveRoles(userData, wrappedListener.delegateFailureAndWrap((l, roles) -> { @@ -796,10 +783,10 @@ private static Tuple settingFactory, + RealmConfig realmConfig + ) { + return forSetting(logger, settingFactory.apply(realmConfig.type()), realmConfig); + } + static AttributeParser forSetting(Logger logger, SamlRealmSettings.AttributeSettingWithDelimiter setting, RealmConfig realmConfig) { SamlRealmSettings.AttributeSetting attributeSetting = setting.getAttributeSetting(); if (realmConfig.hasSetting(setting.getDelimiter())) { @@ -1090,6 +1085,15 @@ static AttributeParser forSetting(Logger logger, SamlRealmSettings.AttributeSett return AttributeParser.forSetting(logger, attributeSetting, realmConfig, false); } + static AttributeParser forSetting( + Logger logger, + Function settingFactory, + RealmConfig realmConfig, + boolean required + ) { + return forSetting(logger, settingFactory.apply(realmConfig.type()), realmConfig, required); + } + static AttributeParser forSetting( Logger logger, SamlRealmSettings.AttributeSetting setting, diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SingleSamlSpConfiguration.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SingleSamlSpConfiguration.java new file mode 100644 index 0000000000000..15b80fd217157 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SingleSamlSpConfiguration.java @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.saml; + +import org.elasticsearch.xpack.core.security.authc.RealmConfig; +import org.opensaml.security.x509.X509Credential; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.REQUESTED_AUTHN_CONTEXT_CLASS_REF; +import static org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings.SP_ACS; +import static org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings.SP_ENTITY_ID; +import static org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings.SP_LOGOUT; +import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.buildEncryptionCredential; +import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.buildSigningConfiguration; +import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.require; + +public class SingleSamlSpConfiguration implements SpConfiguration { + private final String entityId; + private final String ascUrl; + private final String logoutUrl; + private final SigningConfiguration signingConfiguration; + private final List reqAuthnCtxClassRef; + private final List encryptionCredentials; + + // Visible for testing + SingleSamlSpConfiguration( + final String entityId, + final String ascUrl, + final String logoutUrl, + final SigningConfiguration signingConfiguration, + final List encryptionCredentials, + final List reqAuthnCtxClassRef + ) { + this.entityId = entityId; + this.ascUrl = ascUrl; + this.logoutUrl = logoutUrl; + this.signingConfiguration = signingConfiguration; + this.reqAuthnCtxClassRef = reqAuthnCtxClassRef; + this.encryptionCredentials = encryptionCredentials; + } + + public static SingleSamlSpConfiguration create(RealmConfig realmConfig) throws GeneralSecurityException, IOException { + List encryptionCredential = buildEncryptionCredential(realmConfig); + + return new SingleSamlSpConfiguration( + require(realmConfig, SP_ENTITY_ID), + require(realmConfig, SP_ACS), + require(realmConfig, SP_LOGOUT), + buildSigningConfiguration(realmConfig), + encryptionCredential != null ? encryptionCredential : Collections.emptyList(), + realmConfig.getSetting(REQUESTED_AUTHN_CONTEXT_CLASS_REF) + ); + } + + @Override + public String getEntityId() { + return entityId; + } + + @Override + public String getAscUrl() { + return ascUrl; + } + + @Override + public String getLogoutUrl() { + return logoutUrl; + } + + @Override + public List getEncryptionCredentials() { + return encryptionCredentials; + } + + @Override + public SigningConfiguration getSigningConfiguration() { + return signingConfiguration; + } + + @Override + public List getReqAuthnCtxClassRef() { + return reqAuthnCtxClassRef; + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java index 16592223c1c7d..2bb911015c596 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java @@ -4,70 +4,57 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + package org.elasticsearch.xpack.security.authc.saml; -import org.elasticsearch.core.Nullable; import org.opensaml.security.x509.X509Credential; -import java.util.Collections; import java.util.List; /** - * A simple container class that holds all configuration related to a SAML Service Provider (SP). + * Configuration interface for a SAML Service Provider (SP). */ -public class SpConfiguration { - - private final String entityId; - private final String ascUrl; - private final String logoutUrl; - private final SigningConfiguration signingConfiguration; - private final List reqAuthnCtxClassRef; - private final List encryptionCredentials; - - public SpConfiguration( - final String entityId, - final String ascUrl, - final String logoutUrl, - final SigningConfiguration signingConfiguration, - @Nullable final List encryptionCredential, - final List authnCtxClassRef - ) { - this.entityId = entityId; - this.ascUrl = ascUrl; - this.logoutUrl = logoutUrl; - this.signingConfiguration = signingConfiguration; - if (encryptionCredential != null) { - this.encryptionCredentials = Collections.unmodifiableList(encryptionCredential); - } else { - this.encryptionCredentials = Collections.emptyList(); - } - this.reqAuthnCtxClassRef = authnCtxClassRef; - } +public interface SpConfiguration { /** - * The SAML identifier (as a URI) for the Sp + * Returns the entity ID of the SAML Service Provider. + * + * @return the entity ID */ - public String getEntityId() { - return entityId; - } + String getEntityId(); - public String getAscUrl() { - return ascUrl; - } + /** + * Returns the Assertion Consumer Service (ACS) URL of the SAML Service Provider. + * + * @return the ACS URL + */ + String getAscUrl(); - public String getLogoutUrl() { - return logoutUrl; - } + /** + * Returns the URL for handling SAML logout requests. + * + * @return the logout URL + */ + String getLogoutUrl(); - public List getEncryptionCredentials() { - return encryptionCredentials; - } + /** + * Returns the list of X.509 credentials used for encryption. + * + * @return the encryption credentials + */ + List getEncryptionCredentials(); - public SigningConfiguration getSigningConfiguration() { - return signingConfiguration; - } + /** + * Returns the signing configuration for the SAML Service Provider. + * + * @return the signing configuration + */ + SigningConfiguration getSigningConfiguration(); - List getReqAuthnCtxClassRef() { - return reqAuthnCtxClassRef; - } + /** + * Returns the list of requested authentication context class references. + * + * @return the authentication context class references + */ + List getReqAuthnCtxClassRef(); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/SamlBaseRestHandler.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/SamlBaseRestHandler.java index 0ecc9e8728aff..14f08260fb176 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/SamlBaseRestHandler.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/SamlBaseRestHandler.java @@ -12,7 +12,7 @@ import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.security.authc.Realms; import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler; @@ -22,7 +22,7 @@ public abstract class SamlBaseRestHandler extends SecurityBaseRestHandler { private static final Logger logger = LogManager.getLogger(SamlBaseRestHandler.class); - private static final String SAML_REALM_TYPE = SamlRealmSettings.TYPE; + private static final String SAML_REALM_TYPE = SingleSpSamlRealmSettings.TYPE; public SamlBaseRestHandler(Settings settings, XPackLicenseState licenseState) { super(settings, licenseState); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java index 269f8cb0471fc..000b268cbae85 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlInvalidateSessionActionTests.java @@ -75,6 +75,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.Realms; @@ -142,10 +143,13 @@ public void setup() throws Exception { .put("path.home", createTempDir()) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_METADATA_PATH), metadata.toString()) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_ENTITY_ID), SamlRealmTests.TEST_IDP_ENTITY_ID) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_ENTITY_ID), SamlRealmTestHelper.SP_ENTITY_ID) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_ACS), SamlRealmTestHelper.SP_ACS_URL) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_LOGOUT), SamlRealmTestHelper.SP_LOGOUT_URL) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid") + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_ENTITY_ID), SamlRealmTestHelper.SP_ENTITY_ID) + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_ACS), SamlRealmTestHelper.SP_ACS_URL) + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_LOGOUT), SamlRealmTestHelper.SP_LOGOUT_URL) + .put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(SingleSpSamlRealmSettings.TYPE).getAttribute()), + "uid" + ) .put(getFullSettingKey(realmId, RealmSettings.ORDER_SETTING), 0) .build(); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java index 855f96e30ffa0..866f32f917bf4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/saml/TransportSamlLogoutActionTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig.RealmIdentifier; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.ssl.SSLService; @@ -62,6 +63,7 @@ import org.elasticsearch.xpack.security.authc.saml.SamlRealm; import org.elasticsearch.xpack.security.authc.saml.SamlRealmTests; import org.elasticsearch.xpack.security.authc.saml.SamlTestCase; +import org.elasticsearch.xpack.security.authc.saml.SingleSamlSpConfiguration; import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.junit.After; import org.junit.Before; @@ -112,9 +114,12 @@ public void setup() throws Exception { .put("path.home", createTempDir()) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_METADATA_PATH), metadata.toString()) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_ENTITY_ID), SamlRealmTests.TEST_IDP_ENTITY_ID) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_ENTITY_ID), SP_URL) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_ACS), SP_URL) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid") + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_ENTITY_ID), SP_URL) + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_ACS), SP_URL) + .put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(SingleSpSamlRealmSettings.TYPE).getAttribute()), + "uid" + ) .put(getFullSettingKey(realmIdentifier, RealmSettings.ORDER_SETTING), 0) .build(); @@ -240,7 +245,13 @@ public void setup() throws Exception { final Environment env = TestEnvironment.newEnvironment(settings); final RealmConfig realmConfig = new RealmConfig(realmIdentifier, settings, env, threadContext); - samlRealm = SamlRealm.create(realmConfig, mock(SSLService.class), mock(ResourceWatcherService.class), mock(UserRoleMapper.class)); + samlRealm = SamlRealm.create( + realmConfig, + mock(SSLService.class), + mock(ResourceWatcherService.class), + mock(UserRoleMapper.class), + SingleSamlSpConfiguration.create(realmConfig) + ); when(realms.realm(realmConfig.name())).thenReturn(samlRealm); } @@ -260,7 +271,7 @@ public void testLogoutInvalidatesToken() throws Exception { nameId ); final User user = new User("punisher", new String[] { "superuser" }, null, null, userMetadata, true); - final Authentication.RealmRef realmRef = new Authentication.RealmRef(samlRealm.name(), SamlRealmSettings.TYPE, "node01"); + final Authentication.RealmRef realmRef = new Authentication.RealmRef(samlRealm.name(), SingleSpSamlRealmSettings.TYPE, "node01"); final Map tokenMetadata = samlRealm.createTokenMetadata( new SamlNameId(NameID.TRANSIENT, nameId, null, null, null), session diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/InternalRealmsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/InternalRealmsTests.java index 21d3467654154..baff785726b3e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/InternalRealmsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/InternalRealmsTests.java @@ -30,6 +30,7 @@ import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.CachingRealm; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.ssl.SSLService; @@ -180,22 +181,28 @@ public void testRealmsRegisterForRefreshAtRoleMapper() throws Exception { verify(userRoleMapper, times(1)).clearRealmCacheOnChange(same((CachingRealm) realm)); } { - RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier(SamlRealmSettings.TYPE, "test"); + RealmConfig.RealmIdentifier realmId = new RealmConfig.RealmIdentifier(SingleSpSamlRealmSettings.TYPE, "test"); Settings settings = Settings.builder() .put("path.home", createTempDir()) .put(RealmSettings.getFullSettingKey(realmId, RealmSettings.ORDER_SETTING), 0) .put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true) .put(getFullSettingKey(realmId.getName(), SamlRealmSettings.IDP_METADATA_PATH), metadata.toString()) .put(getFullSettingKey(realmId.getName(), SamlRealmSettings.IDP_ENTITY_ID), SamlRealmTests.TEST_IDP_ENTITY_ID) - .put(getFullSettingKey(realmId.getName(), SamlRealmSettings.SP_ENTITY_ID), "mock") - .put(getFullSettingKey(realmId.getName(), SamlRealmSettings.SP_ACS), "http://mock") - .put(getFullSettingKey(realmId.getName(), SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid") + .put(getFullSettingKey(realmId.getName(), SingleSpSamlRealmSettings.SP_ENTITY_ID), "mock") + .put(getFullSettingKey(realmId.getName(), SingleSpSamlRealmSettings.SP_ACS), "http://mock") + .put( + getFullSettingKey( + realmId.getName(), + SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(SingleSpSamlRealmSettings.TYPE).getAttribute() + ), + "uid" + ) .build(); final Environment env = TestEnvironment.newEnvironment(settings); final ThreadContext threadContext = new ThreadContext(settings); - assertThat(factories, hasEntry(is(SamlRealmSettings.TYPE), any(Realm.Factory.class))); + assertThat(factories, hasEntry(is(SingleSpSamlRealmSettings.TYPE), any(Realm.Factory.class))); try ( - SamlRealm ignored = (SamlRealm) factories.get(SamlRealmSettings.TYPE) + SamlRealm ignored = (SamlRealm) factories.get(SingleSpSamlRealmSettings.TYPE) .create(new RealmConfig(realmId, settings, env, threadContext)) ) { // SAML realm is not caching diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java index b66b035cec447..ef1adf0707fc3 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java @@ -44,7 +44,7 @@ import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; import org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; -import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.User; @@ -1165,7 +1165,7 @@ public void testUnlicensedWithBasicRealmSettings() throws Exception { public void testUnlicensedWithNonStandardRealms() throws Exception { final List platinumRealms = CollectionUtils.arrayAsArrayList( - SamlRealmSettings.TYPE, + SingleSpSamlRealmSettings.TYPE, KerberosRealmSettings.TYPE, OpenIdConnectRealmSettings.TYPE, JwtRealmSettings.TYPE diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java index 83f09bad0d27d..35981ababed4f 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java @@ -128,7 +128,7 @@ private SamlAuthenticator buildAuthenticator(Supplier> credenti final List spEncryptionCredentials = buildOpenSamlCredential(spEncryptionCertificatePairs).stream() .map((cred) -> (X509Credential) cred) .collect(Collectors.toList()); - final SpConfiguration sp = new SpConfiguration( + final SpConfiguration sp = new SingleSamlSpConfiguration( SP_ENTITY_ID, SP_ACS_URL, null, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java index 11bb21de22d11..9ca4a2612eb6a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java @@ -42,7 +42,7 @@ public void init() throws Exception { } public void testBuildRequestWithDefaultSettingsHasNoNameIdPolicy() { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); + SpConfiguration sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, @@ -65,7 +65,7 @@ public void testBuildRequestWithDefaultSettingsHasNoNameIdPolicy() { } public void testBuildRequestWithPersistentNameAndNoForceAuth() throws Exception { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); + SpConfiguration sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, @@ -93,7 +93,7 @@ public void testBuildRequestWithPersistentNameAndNoForceAuth() throws Exception } public void testBuildRequestWithTransientNameAndForceAuthTrue() throws Exception { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); + SpConfiguration sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, @@ -123,7 +123,14 @@ public void testBuildRequestWithTransientNameAndForceAuthTrue() throws Exception } public void testBuildRequestWithRequestedAuthnContext() throws Exception { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.singletonList(KERBEROS_AUTHN_CTX)); + SpConfiguration sp = new SingleSamlSpConfiguration( + SP_ENTITY_ID, + ACS_URL, + null, + null, + null, + Collections.singletonList(KERBEROS_AUTHN_CTX) + ); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, @@ -153,7 +160,7 @@ public void testBuildRequestWithRequestedAuthnContext() throws Exception { public void testBuildRequestWithRequestedAuthnContexts() throws Exception { List reqAuthnCtxClassRef = Arrays.asList(KERBEROS_AUTHN_CTX, SMARTCARD_AUTHN_CTX, "http://an.arbitrary/mfa-profile"); - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, reqAuthnCtxClassRef); + SpConfiguration sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, reqAuthnCtxClassRef); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java index 77370540b22c8..b76ebddfd4c91 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java @@ -205,7 +205,7 @@ private SamlLogoutRequestHandler buildHandler() throws Exception { final X509Credential spCredential = (X509Credential) buildOpenSamlCredential(readRandomKeyPair()).get(0); final SigningConfiguration signingConfiguration = new SigningConfiguration(Collections.singleton("*"), spCredential); - final SpConfiguration sp = new SpConfiguration( + final SpConfiguration sp = new SingleSamlSpConfiguration( "https://sp.test/", "https://sp.test/saml/asc", LOGOUT_URL, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java index 928cf7ac146ed..691922b70c0c4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java @@ -40,7 +40,7 @@ public class SamlLogoutRequestMessageBuilderTests extends SamlTestCase { @Before public void init() throws Exception { SamlUtils.initialize(logger); - sp = new SpConfiguration(SP_ENTITY_ID, "http://sp.example.com/saml/acs", null, null, null, Collections.emptyList()); + sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, "http://sp.example.com/saml/acs", null, null, null, Collections.emptyList()); idpRole = SamlUtils.buildObject(IDPSSODescriptor.class, IDPSSODescriptor.DEFAULT_ELEMENT_NAME); idp = SamlUtils.buildObject(EntityDescriptor.class, EntityDescriptor.DEFAULT_ELEMENT_NAME); idp.setEntityID(IDP_ENTITY_ID); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutResponseHandlerHttpRedirectTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutResponseHandlerHttpRedirectTests.java index 90c13b2f2762a..035ec1cc14f15 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutResponseHandlerHttpRedirectTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutResponseHandlerHttpRedirectTests.java @@ -55,7 +55,7 @@ public void setupHandler() throws Exception { final IdpConfiguration idp = new IdpConfiguration(IDP_ENTITY_ID, () -> Collections.singletonList(credential)); final X509Credential spCredential = (X509Credential) buildOpenSamlCredential(readRandomKeyPair()).get(0); final SigningConfiguration signingConfiguration = new SigningConfiguration(Collections.singleton("*"), spCredential); - final SpConfiguration sp = new SpConfiguration( + final SpConfiguration sp = new SingleSamlSpConfiguration( "https://sp.test/", "https://sp.test/saml/asc", LOGOUT_URL, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java index fb866cd892361..b116b8e44e7a1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java @@ -41,7 +41,7 @@ public static SamlRealm buildRealm(RealmConfig realmConfig, @Nullable X509Creden slo.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); slo.setLocation(IDP_LOGOUT_URL); - final SpConfiguration spConfiguration = new SpConfiguration( + final SpConfiguration spConfiguration = new SingleSamlSpConfiguration( SP_ENTITY_ID, SP_ACS_URL, SP_LOGOUT_URL, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java index f294e8f5f3243..b34441cd79a13 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java @@ -31,6 +31,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.saml.SingleSpSamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.user.User; @@ -56,10 +57,12 @@ import org.opensaml.security.credential.Credential; import org.opensaml.security.x509.X509Credential; +import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PrivilegedActionException; @@ -266,7 +269,7 @@ private void doTestReloadFailedHttpsMetadata(String metadataBody, TestsSSLServic } } - public void testMinRefreshGreaterThanRefreshThrowsSettingsException() { + public void testMinRefreshGreaterThanRefreshThrowsSettingsException() throws GeneralSecurityException, IOException { var refresh = randomTimeValue(20, 110, TimeUnit.MINUTES, TimeUnit.SECONDS); var minRefresh = randomTimeValue(2, 8, TimeUnit.HOURS); @@ -280,7 +283,13 @@ public void testMinRefreshGreaterThanRefreshThrowsSettingsException() { final SettingsException settingsException = expectThrows( SettingsException.class, - () -> SamlRealm.create(config, sslService, mock(ResourceWatcherService.class), mock(UserRoleMapper.class)) + () -> SamlRealm.create( + config, + sslService, + mock(ResourceWatcherService.class), + mock(UserRoleMapper.class), + SingleSamlSpConfiguration.create(config) + ) ); assertThat( @@ -323,7 +332,13 @@ public void testAbsurdlyLowMinimumRefreshThrowsException() { final IllegalArgumentException settingsException = expectThrows( IllegalArgumentException.class, - () -> SamlRealm.create(config, sslService, mock(ResourceWatcherService.class), mock(UserRoleMapper.class)) + () -> SamlRealm.create( + config, + sslService, + mock(ResourceWatcherService.class), + mock(UserRoleMapper.class), + SingleSamlSpConfiguration.create(config) + ) ); assertThat( @@ -522,13 +537,14 @@ private AuthenticationResult performAuthentication( List rolesToExclude ) throws Exception { final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final String userPrincipal = useNameId ? "clint.barton" : "cbarton"; final String nameIdValue = principalIsEmailAddress ? "clint.barton@shield.gov" : "clint.barton"; final String uidValue = principalIsEmailAddress ? "cbarton@shield.gov" : "cbarton"; + final String realmType = SingleSpSamlRealmSettings.TYPE; final RealmConfig.RealmIdentifier realmIdentifier = new RealmConfig.RealmIdentifier("mock", "mock_lookup"); final MockLookupRealm lookupRealm = new MockLookupRealm( @@ -541,17 +557,26 @@ private AuthenticationResult performAuthentication( ); final Settings.Builder settingsBuilder = Settings.builder() - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), useNameId ? "nameid" : "uid") - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.GROUPS_ATTRIBUTE.getAttributeSetting().getAttribute()), "groups") - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.MAIL_ATTRIBUTE.getAttribute()), "mail"); + .put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), + useNameId ? "nameid" : "uid" + ) + .put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.GROUPS_ATTRIBUTE.apply(realmType).getAttributeSetting().getAttribute()), + "groups" + ) + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.MAIL_ATTRIBUTE.apply(realmType).getAttribute()), "mail"); if (groupsDelimiter != null) { - settingsBuilder.put(getFullSettingKey(REALM_NAME, SamlRealmSettings.GROUPS_ATTRIBUTE.getDelimiter()), groupsDelimiter); + settingsBuilder.put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.GROUPS_ATTRIBUTE.apply(realmType).getDelimiter()), + groupsDelimiter + ); } if (principalIsEmailAddress) { final boolean anchoredMatch = randomBoolean(); settingsBuilder.put( - getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getPattern()), + getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getPattern()), anchoredMatch ? "^([^@]+)@shield.gov$" : "^([^@]+)@" ); } @@ -658,7 +683,10 @@ private List performAttributeSelectionWithSplit(String delimiter, String final RealmConfig config = buildConfig(settings); - final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter("groups"); + final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter( + config.type(), + "groups" + ); final SamlRealm.AttributeParser parser = SamlRealm.AttributeParser.forSetting(logger, groupSetting, config); final SamlAttributes attributes = new SamlAttributes( @@ -684,7 +712,10 @@ public void testAttributeSelectionWithDelimiterAndPatternThrowsSettingsException final RealmConfig config = buildConfig(settings); - final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter("groups"); + final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter( + config.type(), + "groups" + ); final SettingsException settingsException = expectThrows( SettingsException.class, @@ -699,7 +730,10 @@ public void testAttributeSelectionNoGroupsConfiguredThrowsSettingsException() { String delimiter = ","; final Settings settings = Settings.builder().put(REALM_SETTINGS_PREFIX + ".attribute_delimiters.groups", delimiter).build(); final RealmConfig config = buildConfig(settings); - final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter("groups"); + final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter( + config.type(), + "groups" + ); final SettingsException settingsException = expectThrows( SettingsException.class, @@ -720,7 +754,10 @@ public void testAttributeSelectionWithSplitAndListThrowsSecurityException() { final RealmConfig config = buildConfig(settings); - final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter("groups"); + final SamlRealmSettings.AttributeSettingWithDelimiter groupSetting = new SamlRealmSettings.AttributeSettingWithDelimiter( + config.type(), + "groups" + ); final SamlRealm.AttributeParser parser = SamlRealm.AttributeParser.forSetting(logger, groupSetting, config); final SamlAttributes attributes = new SamlAttributes( @@ -752,7 +789,7 @@ public void testAttributeSelectionWithRegex() { final RealmConfig config = buildConfig(settings); - final SamlRealmSettings.AttributeSetting principalSetting = new SamlRealmSettings.AttributeSetting("principal"); + final SamlRealmSettings.AttributeSetting principalSetting = new SamlRealmSettings.AttributeSetting(config.type(), "principal"); final SamlRealm.AttributeParser parser = SamlRealm.AttributeParser.forSetting(logger, principalSetting, config, false); final SamlAttributes attributes = new SamlAttributes( @@ -772,9 +809,10 @@ public void testAttributeSelectionWithRegex() { } public void testSettingPatternWithoutAttributeThrowsSettingsException() throws Exception { + final String realmType = SingleSpSamlRealmSettings.TYPE; final Settings realmSettings = Settings.builder() - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "nameid") - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.NAME_ATTRIBUTE.getPattern()), "^\\s*(\\S.*\\S)\\s*$") + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), "nameid") + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.NAME_ATTRIBUTE.apply(realmType).getPattern()), "^\\s*(\\S.*\\S)\\s*$") .build(); final RealmConfig config = buildConfig(realmSettings); @@ -782,7 +820,7 @@ public void testSettingPatternWithoutAttributeThrowsSettingsException() throws E final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SettingsException settingsException = expectThrows( SettingsException.class, @@ -806,7 +844,7 @@ public void testSettingExcludeRolesAndAuthorizationRealmsThrowsException() throw final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); var e = expectThrows(IllegalArgumentException.class, () -> buildRealm(config, roleMapper, authenticator, logoutHandler, idp, sp)); @@ -830,7 +868,7 @@ public void testMissingPrincipalSettingThrowsSettingsException() throws Exceptio final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SettingsException settingsException = expectThrows( SettingsException.class, @@ -842,13 +880,17 @@ public void testMissingPrincipalSettingThrowsSettingsException() throws Exceptio public void testNonMatchingPrincipalPatternThrowsSamlException() throws Exception { final UserRoleMapper roleMapper = mock(UserRoleMapper.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); + final String realmType = SingleSpSamlRealmSettings.TYPE; final Settings realmSettings = Settings.builder() - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "mail") - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getPattern()), "^([^@]+)@mycorp\\.example\\.com$") + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), "mail") + .put( + getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getPattern()), + "^([^@]+)@mycorp\\.example\\.com$" + ) .build(); final RealmConfig config = buildConfig(realmSettings); @@ -1149,12 +1191,13 @@ public void testBuildLogoutRequest() throws Exception { slo.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); slo.setLocation("https://logout.saml/"); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); + final String realmType = SingleSpSamlRealmSettings.TYPE; final Settings.Builder realmSettings = Settings.builder() - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid"); + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), "uid"); if (useSingleLogout != null) { realmSettings.put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_SINGLE_LOGOUT), useSingleLogout.booleanValue()); } @@ -1184,14 +1227,15 @@ public void testCorrectRealmSelected() throws Exception { final String acsUrl = "https://idp.test/saml/login"; final UserRoleMapper roleMapper = mock(UserRoleMapper.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", acsUrl, null, null, null, Collections.emptyList()); + final SpConfiguration sp = new SingleSamlSpConfiguration("", acsUrl, null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); + final String realmType = SingleSpSamlRealmSettings.TYPE; final Settings.Builder realmSettings = Settings.builder() - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid") + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), "uid") .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_METADATA_PATH), "http://url.to/metadata") .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_ENTITY_ID), TEST_IDP_ENTITY_ID) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.SP_ACS), acsUrl); + .put(getFullSettingKey(REALM_NAME, SingleSpSamlRealmSettings.SP_ACS), acsUrl); final RealmConfig config = buildConfig(realmSettings.build()); final SamlRealm realm = buildRealm(config, roleMapper, authenticator, logoutHandler, idp, sp); final Realms realms = mock(Realms.class); @@ -1221,7 +1265,7 @@ public void testReadDifferentIdpMetadataSameKeyFromFiles() throws Exception { // Use a temp file to trigger load and reload by ResourceWatcherService final Path realmMetadataPath = Files.createTempFile(PathUtils.get(createTempDir().toString()), "idp1-metadata", "xml"); - final RealmConfig.RealmIdentifier realmIdentifier = new RealmConfig.RealmIdentifier(SamlRealmSettings.TYPE, "saml-idp1"); + final RealmConfig.RealmIdentifier realmIdentifier = new RealmConfig.RealmIdentifier(SingleSpSamlRealmSettings.TYPE, "saml-idp1"); final RealmConfig realmConfig = new RealmConfig( realmIdentifier, Settings.builder().put(RealmSettings.getFullSettingKey(realmIdentifier, RealmSettings.ORDER_SETTING), 1).build(), @@ -1305,6 +1349,7 @@ private Tuple buildConfig(String idpMetadataPath, Consu private Settings.Builder buildSettings(String idpMetadataPath) { MockSecureSettings secureSettings = new MockSecureSettings(); + final String realmType = SingleSpSamlRealmSettings.TYPE; secureSettings.setString(REALM_SETTINGS_PREFIX + ".ssl.secure_key_passphrase", "testnode"); return Settings.builder() .put(REALM_SETTINGS_PREFIX + ".ssl.verification_mode", "certificate") @@ -1323,7 +1368,7 @@ private Settings.Builder buildSettings(String idpMetadataPath) { .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_METADATA_PATH), idpMetadataPath) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_ENTITY_ID), TEST_IDP_ENTITY_ID) .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.IDP_METADATA_HTTP_REFRESH), METADATA_REFRESH.getStringRep()) - .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.getAttribute()), "uid") + .put(getFullSettingKey(REALM_NAME, SamlRealmSettings.PRINCIPAL_ATTRIBUTE.apply(realmType).getAttribute()), "uid") .put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true) .put("path.home", createTempDir()) .setSecureSettings(secureSettings); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRedirectTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRedirectTests.java index d3eb0e2264364..001d3f4cf7a1a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRedirectTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRedirectTests.java @@ -134,7 +134,7 @@ public void testAuthnRequestSigning() throws Exception { invalidCredential = (X509Credential) buildOpenSamlCredential(readRandomKeyPair()).get(0); } final SigningConfiguration signingConfig = new SigningConfiguration(singleton("*"), credential); - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, LOGOUT_URL, signingConfig, null, Collections.emptyList()); + SpConfiguration sp = new SingleSamlSpConfiguration(SP_ENTITY_ID, ACS_URL, LOGOUT_URL, signingConfig, null, Collections.emptyList()); EntityDescriptor idpDescriptor = buildIdPDescriptor(IDP_URL, IDP_ENTITY_ID); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlResponseHandlerTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlResponseHandlerTests.java index 8280fff418fc5..0e59ec4ec8476 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlResponseHandlerTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlResponseHandlerTests.java @@ -132,7 +132,7 @@ protected SpConfiguration getSpConfiguration(List reqAuthnCtxClassRef) { final List spEncryptionCredentials = buildOpenSamlCredential(spEncryptionCertificatePairs).stream() .map((cred) -> (X509Credential) cred) .collect(Collectors.toList()); - return new SpConfiguration( + return new SingleSamlSpConfiguration( SP_ENTITY_ID, SP_ACS_URL, SP_LOGOUT_URL,