[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