[java-identity-provider] branch master updated: IDP-1602 LDAP SASL configuration
Daniel Fisher
dfisher at vt.edu
Tue May 12 03:24:22 UTC 2020
This is an automated email from the git hooks/post-receive script.
dfisher pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=ea76bef8aa25bfbde881f8a43f76c948115d2a5a
The following commit(s) were added to refs/heads/master by this push:
new ea76bef8a IDP-1602 LDAP SASL configuration
ea76bef8a is described below
commit ea76bef8aa25bfbde881f8a43f76c948115d2a5a
Author: Daniel Fisher <dfisher at vt.edu>
AuthorDate: Mon May 11 23:11:50 2020 -0400
IDP-1602 LDAP SASL configuration
Update resolver XSD to support additional SASL configuration options.
Add deprecation warning for <AuthenticatinType />.
Add unit test for DIGEST-MD5; GSSAPI proved very difficult to mock.
---
.../dc/ldap/impl/LDAPDataConnectorParser.java | 76 +++++++++++++++++++++-
.../dc/ldap/LDAPDataConnectorParserTest.java | 51 +++++++++++++++
.../resolver/ldap-attribute-resolver-v2-sasl.xml | 25 +++++++
.../schema/shibboleth-attribute-resolver.xsd | 33 +++++++++-
4 files changed, 183 insertions(+), 2 deletions(-)
diff --git a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/impl/LDAPDataConnectorParser.java b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/impl/LDAPDataConnectorParser.java
index 6dfb2754e..bd4f680b9 100644
--- a/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/impl/LDAPDataConnectorParser.java
+++ b/idp-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/impl/LDAPDataConnectorParser.java
@@ -45,6 +45,9 @@ import org.ldaptive.pool.IdlePruneStrategy;
import org.ldaptive.pool.PoolConfig;
import org.ldaptive.pool.PooledConnectionFactory;
import org.ldaptive.pool.SearchValidator;
+import org.ldaptive.referral.SearchReferralHandler;
+import org.ldaptive.sasl.DigestMd5Config;
+import org.ldaptive.sasl.GssApiConfig;
import org.ldaptive.sasl.Mechanism;
import org.ldaptive.sasl.SaslConfig;
import org.ldaptive.ssl.AllowAnyHostnameVerifier;
@@ -286,12 +289,17 @@ public class LDAPDataConnectorParser extends AbstractDataConnectorParser {
connectionInitializer.addPropertyValue("bindCredential", credential.getBeanDefinition());
}
if (authenticationType != null) {
+ // V4 Deprecation
+ DeprecationSupport.warn(ObjectType.ATTRIBUTE, "authenticationType", "<LDAPDirectory>",
+ "<SaslConfig>");
final Mechanism mechanism = Mechanism.valueOf(authenticationType);
if (mechanism != null) {
final SaslConfig config = new SaslConfig();
config.setMechanism(mechanism);
connectionInitializer.addPropertyValue("bindSaslConfig", config);
}
+ } else {
+ connectionInitializer.addPropertyValue("bindSaslConfig", createSaslConfig());
}
if (principal != null || principalCredential != null || authenticationType != null) {
connectionConfig.addPropertyValue("connectionInitializer", connectionInitializer.getBeanDefinition());
@@ -658,6 +666,73 @@ public class LDAPDataConnectorParser extends AbstractDataConnectorParser {
return poolConfig.getBeanDefinition();
}
+ /**
+ * Creates a new sasl config bean definition from a v2 XML configuration.
+ *
+ * @return sasl config bean definition
+ */
+ @Nullable protected BeanDefinition createSaslConfig() {
+ final List<Element> saslConfigElements = ElementSupport.getChildElementsByTagNameNS(configElement,
+ AttributeResolverNamespaceHandler.NAMESPACE, "SaslConfig");
+
+ if (saslConfigElements.isEmpty()) {
+ return null;
+ } else if (saslConfigElements.size() > 1) {
+ log.warn("{} Only one <SaslConfig> element can be specified; "+
+ "only the first has been consulted.", getLogPrefix());
+ }
+ final Element saslConfigElement = saslConfigElements.get(0);
+ final String mechanism = AttributeSupport.getAttributeValue(saslConfigElement, new QName("mechanism"));
+ final String authorizationId = AttributeSupport.getAttributeValue(
+ saslConfigElement, new QName("authorizationId"));
+ final String realm = AttributeSupport.getAttributeValue(saslConfigElement, new QName("realm"));
+ final List<Element> saslProperties = ElementSupport.getChildElementsByTagNameNS(saslConfigElement,
+ AttributeResolverNamespaceHandler.NAMESPACE, "SASLProperty");
+
+ final BeanDefinitionBuilder saslConfig;
+ if ("DIGEST_MD5".equals(mechanism)) {
+ saslConfig = BeanDefinitionBuilder.genericBeanDefinition(DigestMd5Config.class);
+ if (realm != null) {
+ saslConfig.addPropertyValue("realm", realm);
+ }
+ } else if ("GSSAPI".equals(mechanism)) {
+ saslConfig = BeanDefinitionBuilder.genericBeanDefinition(GssApiConfig.class);
+ if (realm != null) {
+ saslConfig.addPropertyValue("realm", realm);
+ }
+ } else {
+ saslConfig = BeanDefinitionBuilder.genericBeanDefinition(SaslConfig.class);
+ saslConfig.addPropertyValue("mechanism", mechanism);
+ }
+ if (authorizationId != null) {
+ saslConfig.addPropertyValue("authorizationId", authorizationId);
+ }
+
+ if (!saslProperties.isEmpty()) {
+ for (final Element property : saslProperties) {
+ final String name = AttributeSupport.getAttributeValue(property, null, "name");
+ final String value = AttributeSupport.getAttributeValue(property, null, "value");
+ if ("javax.security.sasl.qop".equals(name)) {
+ if ("auth".equalsIgnoreCase(value)) {
+ saslConfig.addPropertyValue("qualityOfProtection", "AUTH");
+ } else if ("auth-int".equalsIgnoreCase(value)) {
+ saslConfig.addPropertyValue("qualityOfProtection", "AUTH_INT");
+ } else if ("auth-conf".equalsIgnoreCase(value)) {
+ saslConfig.addPropertyValue("qualityOfProtection", "AUTH_CONF");
+ } else {
+ saslConfig.addPropertyValue("qualityOfProtection", value);
+ }
+ } else if ("javax.security.sasl.strength".equals(name)) {
+ saslConfig.addPropertyValue("securityStrength", value);
+ } else if ("javax.security.sasl.server.authentication".equals(name)) {
+ saslConfig.addPropertyValue("mutualAuthentication", value);
+ }
+ }
+ }
+
+ return saslConfig.getBeanDefinition();
+ }
+
/**
* Create the result mapping strategy. See {@link net.shibboleth.idp.attribute.resolver.dc.MappingStrategy}.
*
@@ -806,5 +881,4 @@ public class LDAPDataConnectorParser extends AbstractDataConnectorParser {
return handlers;
}
}
-
}
\ No newline at end of file
diff --git a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/LDAPDataConnectorParserTest.java b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/LDAPDataConnectorParserTest.java
index 0007cd426..23f365d82 100644
--- a/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/LDAPDataConnectorParserTest.java
+++ b/idp-attribute-resolver-spring/src/test/java/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/LDAPDataConnectorParserTest.java
@@ -26,9 +26,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import com.unboundid.asn1.ASN1OctetString;
+import com.unboundid.ldap.listener.InMemoryRequestHandler;
+import com.unboundid.ldap.listener.InMemorySASLBindHandler;
+import com.unboundid.ldap.sdk.BindResult;
+import com.unboundid.ldap.sdk.Control;
+import com.unboundid.ldap.sdk.DN;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
import org.ldaptive.BindConnectionInitializer;
import org.ldaptive.ConnectionConfig;
import org.ldaptive.DefaultConnectionFactory;
@@ -38,6 +47,11 @@ import org.ldaptive.pool.IdlePruneStrategy;
import org.ldaptive.pool.PoolConfig;
import org.ldaptive.pool.PooledConnectionFactory;
import org.ldaptive.pool.SearchValidator;
+import org.ldaptive.sasl.DigestMd5Config;
+import org.ldaptive.sasl.Mechanism;
+import org.ldaptive.sasl.QualityOfProtection;
+import org.ldaptive.sasl.SaslConfig;
+import org.ldaptive.sasl.SecurityStrength;
import org.ldaptive.ssl.CredentialConfig;
import org.ldaptive.ssl.SslConfig;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
@@ -113,6 +127,20 @@ public class LDAPDataConnectorParserTest {
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", null, 10389,
sslUtil.createSSLSocketFactory()));
config.addAdditionalBindCredentials("cn=Directory Manager", "password");
+ config.addSASLBindHandler(new InMemorySASLBindHandler() {
+ @Override
+ public String getSASLMechanismName() {
+ return "DIGEST-MD5";
+ }
+
+ @Override
+ public BindResult processSASLBind(final InMemoryRequestHandler handler, final int messageID,
+ final DN bindDN, final ASN1OctetString credentials,
+ final List<Control> controls) {
+ // return success for all digest MD5 bind requests
+ return new BindResult(new LDAPResult(messageID, ResultCode.SUCCESS));
+ }
+ });
directoryServer = new InMemoryDirectoryServer(config);
directoryServer.importFromLDIF(true,
"src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/ldapDataConnectorTest.ldif");
@@ -323,6 +351,29 @@ public class LDAPDataConnectorParserTest {
assertNotNull(attrs.get("entryDN"));
}
+ @Test public void v2SaslConfig() throws Exception {
+ final LDAPDataConnector dataConnector =
+ getLdapDataConnector(new String[] {"net/shibboleth/idp/attribute/resolver/spring/dc/ldap/resolver/ldap-attribute-resolver-v2-sasl.xml"});
+ assertNotNull(dataConnector);
+ assertTrue(dataConnector.isFailFastInitialize());
+ assertEquals(dataConnector.getNoRetryDelay(), Duration.ZERO);
+
+ final DefaultConnectionFactory connFactory = (DefaultConnectionFactory) dataConnector.getConnectionFactory();
+ assertNotNull(connFactory);
+ final ConnectionConfig connConfig = connFactory.getConnectionConfig();
+ assertNotNull(connConfig);
+ final BindConnectionInitializer connInitializer = (BindConnectionInitializer) connConfig.getConnectionInitializer();
+ assertNotNull(connInitializer);
+ final SaslConfig saslConfig = connInitializer.getBindSaslConfig();
+ assertNotNull(saslConfig);
+ assertEquals(saslConfig.getMechanism(), Mechanism.DIGEST_MD5);
+ assertEquals(saslConfig.getAuthorizationId(), "authzID");
+ assertEquals(saslConfig.getMutualAuthentication(), Boolean.TRUE);
+ assertEquals(saslConfig.getQualityOfProtection(), QualityOfProtection.AUTH_INT);
+ assertEquals(saslConfig.getSecurityStrength(), SecurityStrength.HIGH);
+ assertEquals(((DigestMd5Config) saslConfig).getRealm(), "shibboleth.net");
+ }
+
@Test public void springConfig() throws Exception {
final LDAPDataConnector dataConnector =
getLdapDataConnector(new String[] {"net/shibboleth/idp/attribute/resolver/spring/dc/ldap/resolver/ldap-attribute-resolver-spring.xml"});
diff --git a/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/resolver/ldap-attribute-resolver-v2-sasl.xml b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/resolver/ldap-attribute-resolver-v2-sasl.xml
new file mode 100644
index 000000000..0f703b26e
--- /dev/null
+++ b/idp-attribute-resolver-spring/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/ldap/resolver/ldap-attribute-resolver-v2-sasl.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AttributeResolver
+ xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
+
+ <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
+ ldapURL="ldap://localhost:10389"
+ principal="manager at shibboleth.net"
+ failFastInitialize="true"
+ principalCredential="password">
+ <FilterTemplate>
+ <![CDATA[
+ (uid=${resolutionContext.principal})
+ ]]>
+ </FilterTemplate>
+ <SaslConfig mechanism="DIGEST_MD5"
+ authorizationId="authzID"
+ realm="shibboleth.net">
+ <SASLProperty name="javax.security.sasl.qop" value="AUTH-INT"/>
+ <SASLProperty name="javax.security.sasl.strength" value="HIGH"/>
+ <SASLProperty name="javax.security.sasl.server.authentication" value="true"/>
+ </SaslConfig>
+ </DataConnector>
+
+</AttributeResolver>
diff --git a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
index 678f2ba0e..233ac7c4a 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-attribute-resolver.xsd
@@ -806,6 +806,7 @@
</annotation>
</element>
<element name="ConnectionPool" type="resolver:LdapPoolConfigType"/>
+ <element name="SaslConfig" type="resolver:SaslConfigType"/>
<element name="Column" type="resolver:ColumnType"/>
<element name="ResultCache" type="resolver:CacheConfigType"/>
<element name="ResultCacheBean" type="string"/>
@@ -1609,7 +1610,7 @@
</restriction>
</simpleType>
- <simpleType name="AuthenticationType">
+ <simpleType name="AuthenticationType"><!-- Deprecated, remove in V5. -->
<annotation>
<documentation>Supported authentication types.</documentation>
</annotation>
@@ -1624,6 +1625,36 @@
</restriction>
</simpleType>
+ <complexType name="SaslConfigType">
+ <choice maxOccurs="unbounded" minOccurs="0">
+ <element name="SASLProperty" type="resolver:PropertyType">
+ <annotation>
+ <documentation>
+ SASL configuration properties.
+ Common properties include javax.security.sasl.qop, javax.security.sasl.strength, javax.security.sasl.server.authentication.
+ </documentation>
+ </annotation>
+ </element>
+ </choice>
+ <attribute name="mechanism" type="resolver:string">
+ <annotation>
+ <documentation>
+ The SASL mechanism. Common values values are EXTERNAL, DIGEST_MD5, CRAM_MD5, GSSAPI
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="authorizationId" type="resolver:string">
+ <annotation>
+ <documentation>SASL authorization ID.</documentation>
+ </annotation>
+ </attribute>
+ <attribute name="realm" type="resolver:string">
+ <annotation>
+ <documentation>The SASL realm.</documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+
<simpleType name="SearchScopeType">
<annotation>
<documentation>Supported search scopes.</documentation>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list