From acd4205d204b4f8a3480705d5c30df5bf1f89f17 Mon Sep 17 00:00:00 2001 From: Johannes Freden Jansson Date: Wed, 27 Nov 2024 14:01:00 +0100 Subject: [PATCH] Resolve user attribute names once --- .../authc/saml/SamlRealmSettings.java | 11 ++++++++++ .../xpack/security/authc/saml/SamlRealm.java | 22 ++++++++++--------- .../authc/saml/SamlRealmTestHelper.java | 4 +++- .../security/authc/saml/SamlRealmTests.java | 11 +++++++++- 4 files changed, 36 insertions(+), 12 deletions(-) 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 a40e0546fdd6a..4aff7d3a65fea 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 @@ -231,6 +231,17 @@ public static Set> getSettings(String type) { return set; } + public record UserAttributeNameConfiguration(String principal, String dn, String name, String mail) { + public static UserAttributeNameConfiguration fromConfig(RealmConfig config) { + return new UserAttributeNameConfiguration( + PRINCIPAL_ATTRIBUTE.apply(config.type()).name(config), + DN_ATTRIBUTE.apply(config.type()).name(config), + NAME_ATTRIBUTE.apply(config.type()).name(config), + MAIL_ATTRIBUTE.apply(config.type()).name(config) + ); + } + } + /** * The SAML realm offers a number of settings that rely on attributes that are populate by the Identity Provider in the SAML Response. * Each attribute has 2 settings: 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 a71e587265346..e5d4e81b2bf4b 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 @@ -171,6 +171,7 @@ public final class SamlRealm extends Realm implements Releasable { private final Supplier idpDescriptor; private final SpConfiguration serviceProvider; + private final SamlRealmSettings.UserAttributeNameConfiguration userAttributeNameConfiguration; private final SamlAuthnRequestBuilder.NameIDPolicySettings nameIdPolicy; private final Boolean forceAuthn; private final boolean useSingleLogout; @@ -224,6 +225,8 @@ public static SamlRealm create( serviceProvider, maxSkew ); + SamlRealmSettings.UserAttributeNameConfiguration userAttributeNameConfiguration = SamlRealmSettings.UserAttributeNameConfiguration + .fromConfig(config); final SamlRealm realm = new SamlRealm( config, @@ -232,7 +235,8 @@ public static SamlRealm create( logoutHandler, logoutResponseHandler, idpDescriptor, - serviceProvider + serviceProvider, + userAttributeNameConfiguration ); // the metadata resolver needs to be destroyed since it runs a timer task in the background and destroying stops it! @@ -253,7 +257,8 @@ public SpConfiguration getServiceProvider() { SamlLogoutRequestHandler logoutHandler, SamlLogoutResponseHandler logoutResponseHandler, Supplier idpDescriptor, - SpConfiguration spConfiguration + SpConfiguration spConfiguration, + SamlRealmSettings.UserAttributeNameConfiguration userAttributeNameConfiguration ) throws Exception { super(config); @@ -268,6 +273,7 @@ public SpConfiguration getServiceProvider() { this.idpDescriptor = idpDescriptor; this.serviceProvider = spConfiguration; + this.userAttributeNameConfiguration = userAttributeNameConfiguration; this.nameIdPolicy = new SamlAuthnRequestBuilder.NameIDPolicySettings( config.getSetting(NAMEID_FORMAT), @@ -556,11 +562,7 @@ public void authenticate(AuthenticationToken authenticationToken, ActionListener } private void buildUser(SamlAttributes attributes, ActionListener> baseListener) { - final String principal = resolveSingleValueAttribute( - attributes, - principalAttribute, - PRINCIPAL_ATTRIBUTE.apply(config.type()).name(config) - ); + final String principal = resolveSingleValueAttribute(attributes, principalAttribute, userAttributeNameConfiguration.principal()); if (Strings.isNullOrEmpty(principal)) { final String msg = principalAttribute + " not found in saml attributes" @@ -606,9 +608,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.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)); + final String dn = resolveSingleValueAttribute(attributes, dnAttribute, userAttributeNameConfiguration.dn()); + final String name = resolveSingleValueAttribute(attributes, nameAttribute, userAttributeNameConfiguration.name()); + final String mail = resolveSingleValueAttribute(attributes, mailAttribute, userAttributeNameConfiguration.mail()); UserRoleMapper.UserData userData = new UserRoleMapper.UserData(principal, dn, groups, userMeta, config); logger.debug("SAML attribute mapping = [{}]", userData); roleMapper.resolveRoles(userData, wrappedListener.delegateFailureAndWrap((l, roles) -> { 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 b116b8e44e7a1..4a5636ad5fb0d 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 @@ -8,6 +8,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.xpack.core.security.authc.RealmConfig; +import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.metadata.EntityDescriptor; @@ -56,7 +57,8 @@ public static SamlRealm buildRealm(RealmConfig realmConfig, @Nullable X509Creden mock(SamlLogoutRequestHandler.class), mock(SamlLogoutResponseHandler.class), () -> idpDescriptor, - spConfiguration + spConfiguration, + mock(SamlRealmSettings.UserAttributeNameConfiguration.class) ); } 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 b34441cd79a13..de795b2800eef 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 @@ -648,7 +648,16 @@ public SamlRealm buildRealm( SpConfiguration sp ) throws Exception { try { - return new SamlRealm(config, roleMapper, authenticator, logoutHandler, mock(SamlLogoutResponseHandler.class), () -> idp, sp); + return new SamlRealm( + config, + roleMapper, + authenticator, + logoutHandler, + mock(SamlLogoutResponseHandler.class), + () -> idp, + sp, + mock(SamlRealmSettings.UserAttributeNameConfiguration.class) + ); } catch (SettingsException e) { logger.info(() -> format("Settings are invalid:\n%s", config.settings().toDelimitedString('\n')), e); throw e;