[java-identity-provider] branch master updated: IDP-1391 - Add a service layer for password validators.
Scott Cantor
cantor.2 at osu.edu
Mon Aug 12 14:30:09 EDT 2019
This is an automated email from the git hooks/post-receive script.
scantor 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=5c59c83f204f5e577294f56bedfba7cae75a522a
The following commit(s) were added to refs/heads/master by this push:
new 5c59c83 IDP-1391 - Add a service layer for password validators.
5c59c83 is described below
commit 5c59c83f204f5e577294f56bedfba7cae75a522a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Aug 12 14:30:06 2019 -0400
IDP-1391 - Add a service layer for password validators.
https://issues.shibboleth.net/jira/browse/IDP-1391
Renaming some tests.
Add htpasswd validator.
---
.../net/shibboleth/idp/authn/AuthnEventIds.java | 3 +
.../authn/impl/HTPasswdCredentialValidator.java | 259 +++++++++++++++++++++
...t.java => HTPasswdCredentialValidatorTest.java} | 199 +++++-----------
...STest.java => JAASCredentialValidatorTest.java} | 2 +-
...PTest.java => LDAPCredentialValidatorTest.java} | 2 +-
.../net/shibboleth/idp/authn/impl/htpasswd.txt | 3 +
.../resources/conf/authn/password-authn-config.xml | 1 +
.../system/flows/authn/password-authn-beans.xml | 5 +
8 files changed, 334 insertions(+), 140 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
index 524c144..c449094 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnEventIds.java
@@ -44,6 +44,9 @@ public final class AuthnEventIds {
/** ID of event returned if there are no credentials available in the request. */
@Nonnull @NotEmpty public static final String NO_CREDENTIALS = "NoCredentials";
+
+ /** ID of event returned if a username is unknown. */
+ @Nonnull @NotEmpty public static final String UNKNOWN_USERNAME = "UnknownUsername";
/** ID of event returned if the given credentials are invalid. */
@Nonnull @NotEmpty public static final String INVALID_CREDENTIALS = "InvalidCredentials";
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidator.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidator.java
new file mode 100644
index 0000000..d1ecf48
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidator.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.authn.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
+import net.shibboleth.idp.authn.AbstractUsernamePasswordCredentialValidator;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.UsernamePasswordContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.codec.StringDigester;
+import net.shibboleth.utilities.java.support.codec.StringDigester.OutputFormat;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.apache.commons.codec.digest.Crypt;
+import org.apache.commons.codec.digest.Md5Crypt;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+
+import com.google.common.base.Strings;
+
+/**
+ * A password validator that authenticates against Apache htpasswd files.
+ *
+ * @since 4.0.0
+ */
+ at ThreadSafe
+public class HTPasswdCredentialValidator extends AbstractUsernamePasswordCredentialValidator {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(HTPasswdCredentialValidator.class);
+
+ /** Digester for SHA-1. */
+ @NonnullAfterInit private StringDigester digester;
+
+ /** Source of information. */
+ @Nullable private Resource htPasswdResource;
+
+ /** File timestamp. */
+ @Nullable private long lastModified;
+
+ /** In-memory copy of entries. */
+ @Nonnull @NonnullElements private final Map<String,String> credentialMap;
+
+ /** Constructor. */
+ public HTPasswdCredentialValidator() {
+ lastModified = 0;
+ credentialMap = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Set the resource to use.
+ *
+ * @param resource resource to use
+ */
+ public void setResource(@Nonnull final Resource resource) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ htPasswdResource = Constraint.isNotNull(resource, "Resource cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ try {
+ if (htPasswdResource == null) {
+ throw new ComponentInitializationException("Resource cannot be null");
+ }
+
+ digester = new StringDigester("SHA1", OutputFormat.BASE64);
+
+ try (final InputStream is = htPasswdResource.getInputStream()) {
+ credentialMap.putAll(readCredentials(is));
+ }
+
+ if (htPasswdResource.isFile()) {
+ lastModified = htPasswdResource.lastModified();
+ } else {
+ htPasswdResource = null;
+ }
+
+ } catch (final IOException e) {
+ throw new ComponentInitializationException("Error reading htpasswd resource", e);
+ } catch (final NoSuchAlgorithmException e) {
+ throw new ComponentInitializationException("Error creating digester", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable protected Subject doValidate(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final AuthenticationContext authenticationContext,
+ @Nonnull final UsernamePasswordContext usernamePasswordContext,
+ @Nullable final WarningHandler warningHandler,
+ @Nullable final ErrorHandler errorHandler) throws Exception {
+
+
+ final String passwd = credentialMap.get(usernamePasswordContext.getUsername());
+ if (passwd == null) {
+ log.debug("{} Username '{}' not found in password resource", getLogPrefix(),
+ usernamePasswordContext.getUsername());
+ final LoginException e = new LoginException(AuthnEventIds.UNKNOWN_USERNAME);
+ if (errorHandler != null) {
+ errorHandler.handleError(profileRequestContext, authenticationContext, e,
+ AuthnEventIds.UNKNOWN_USERNAME);
+ }
+ throw e;
+ }
+
+ log.debug("{} Attempting to authenticate user '{}' ", getLogPrefix(),
+ usernamePasswordContext.getUsername());
+
+
+ if (authenticate(usernamePasswordContext, passwd)) {
+ log.info("{} Login by '{}' succeeded", getLogPrefix(), usernamePasswordContext.getUsername());
+ return populateSubject(new Subject(), usernamePasswordContext);
+ }
+
+ log.info("{} Login by '{}' failed", getLogPrefix(), usernamePasswordContext.getUsername());
+
+ final LoginException e = new LoginException(AuthnEventIds.INVALID_CREDENTIALS);
+ if (errorHandler != null) {
+ errorHandler.handleError(profileRequestContext, authenticationContext, e,
+ AuthnEventIds.INVALID_CREDENTIALS);
+ }
+ throw e;
+ }
+
+ /**
+ * Compare input password to stored value.
+ *
+ * @param usernamePasswordContext input context
+ * @param storedPassword the stored string
+ *
+ * @return true iff the password matches
+ */
+ @Nonnull private boolean authenticate(@Nonnull final UsernamePasswordContext usernamePasswordContext,
+ @Nonnull final String storedPassword) {
+
+ refreshCredentials();
+
+ // test Apache MD5 variant encrypted password
+ if (storedPassword.startsWith("$apr1$")) {
+ if (storedPassword.equals(Md5Crypt.apr1Crypt(usernamePasswordContext.getPassword(), storedPassword))) {
+ return true;
+ }
+ } else if (storedPassword.startsWith("{SHA}")) {
+ if (storedPassword.substring("{SHA}".length()).equals(
+ digester.apply(usernamePasswordContext.getPassword()))) {
+ return true;
+ }
+ } else if (storedPassword.equals(Crypt.crypt(usernamePasswordContext.getPassword(), storedPassword))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check for file refresh.
+ */
+ private void refreshCredentials() {
+ if (htPasswdResource == null) {
+ // Nothing to do.
+ return;
+ }
+
+ try {
+ if (htPasswdResource.isFile() && htPasswdResource.exists()
+ && (htPasswdResource.lastModified() > lastModified)) {
+ try (final InputStream is = htPasswdResource.getInputStream()) {
+ credentialMap.clear();
+ credentialMap.putAll(readCredentials(is));
+ }
+ }
+ } catch (final IOException e) {
+ log.error("{} Error reloading credentials", getLogPrefix(), e);
+ }
+ }
+
+ /**
+ * Reads the credentials from stream.
+ *
+ * @param is input stream
+ *
+ * @return map of credentials
+ */
+ @Nonnull @NonnullElements private Map<String,String> readCredentials(@Nonnull final InputStream is) {
+
+ final Map<String,String> credentials = new HashMap<>();
+
+ final Pattern entry = Pattern.compile("^([^:]+):(.+)");
+ try (final Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) {
+ while (scanner.hasNextLine()) {
+ final String line = scanner.nextLine().trim();
+ if (!line.isEmpty() && !line.startsWith("#")) {
+ final Matcher m = entry.matcher(line);
+ if (m.matches()) {
+ final String username = m.group(1);
+ final String password = m.group(2);
+ if (Strings.isNullOrEmpty(username)) {
+ log.warn("{} Skipping line with empty username", getLogPrefix());
+ continue;
+ }
+ if (Strings.isNullOrEmpty(password)) {
+ log.warn("{} Skipping '{}' user with blank password", getLogPrefix(), username);
+ continue;
+ }
+
+ credentials.put(username.trim(), password.trim());
+ }
+ }
+ }
+ }
+
+ log.debug("{} Loaded {} password entries", getLogPrefix(), credentials.size());
+
+ return credentials;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidatorTest.java
similarity index 67%
copy from idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java
copy to idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidatorTest.java
index 3757665..589631f 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/HTPasswdCredentialValidatorTest.java
@@ -20,8 +20,6 @@ package net.shibboleth.idp.authn.impl;
import java.io.File;
import java.io.IOException;
import java.security.Principal;
-import java.security.URIParameter;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -39,69 +37,39 @@ import net.shibboleth.idp.authn.principal.TestPrincipal;
import net.shibboleth.idp.authn.principal.UsernamePrincipal;
import net.shibboleth.idp.authn.principal.impl.ExactPrincipalEvalPredicateFactory;
import net.shibboleth.idp.profile.ActionTestingSupport;
-import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.net.URISupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.webflow.execution.Event;
import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import com.unboundid.ldap.listener.InMemoryDirectoryServer;
-import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
-import com.unboundid.ldap.listener.InMemoryListenerConfig;
-import com.unboundid.ldap.sdk.LDAPException;
+/** Unit test for htpasswd file validation. */
+public class HTPasswdCredentialValidatorTest extends BaseAuthenticationContextTest {
-/** Unit test for JAAS validation. */
-public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationContextTest {
-
- private static final String DATA_PATH = "src/test/resources/net/shibboleth/idp/authn/impl/";
+ private static final String DATA_PATH = "net/shibboleth/idp/authn/impl/";
- private JAASCredentialValidator validator;
+ private HTPasswdCredentialValidator validator;
private ValidateCredentials action;
- private InMemoryDirectoryServer directoryServer;
-
- /**
- * Creates an UnboundID in-memory directory server. Leverages LDIF found in test resources.
- *
- * @throws LDAPException if the in-memory directory server cannot be created
- */
- @BeforeClass public void setupDirectoryServer() throws LDAPException {
-
- final InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=shibboleth,dc=net");
- config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", 10389));
- config.addAdditionalBindCredentials("cn=Directory Manager", "password");
- directoryServer = new InMemoryDirectoryServer(config);
- directoryServer.importFromLDIF(true, DATA_PATH + "loginLDAPTest.ldif");
- directoryServer.startListening();
- }
-
- /**
- * Shutdown the in-memory directory server.
- */
- @AfterClass public void teardownDirectoryServer() {
- directoryServer.shutDown(true);
- }
-
@BeforeMethod public void setUp() throws Exception {
super.setUp();
- validator = new JAASCredentialValidator();
- validator.setId("jaastest");
+ validator = new HTPasswdCredentialValidator();
+ validator.setResource(new ClassPathResource(DATA_PATH + "htpasswd.txt"));
+ validator.setId("htpasswdtest");
action = new ValidateCredentials();
action.setValidators(Collections.singletonList(validator));
final Map<String,Collection<String>> mappings = new HashMap<>();
- mappings.put("UnknownUsername", Collections.singleton("DN_RESOLUTION_FAILURE"));
- mappings.put("InvalidPassword", Collections.singleton("INVALID_CREDENTIALS"));
+ mappings.put("InvalidPassword", Collections.singleton(AuthnEventIds.INVALID_CREDENTIALS));
+ mappings.put(AuthnEventIds.UNKNOWN_USERNAME, Collections.singleton(AuthnEventIds.UNKNOWN_USERNAME));
action.setClassifiedMessages(mappings);
action.setHttpServletRequest(new MockHttpServletRequest());
@@ -136,50 +104,8 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final Event event = action.execute(src);
ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
}
-
- @Test public void testNoConfig() throws Exception {
- ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "foo");
- ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "bar");
-
- final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- ac.setAttemptedFlow(authenticationFlows.get(0));
-
- validator.initialize();
- action.initialize();
-
- doExtract(prc);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
- AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
- Assert.assertEquals(errorCtx.getExceptions().size(), 1);
- Assert.assertTrue(errorCtx.getExceptions().get(0) instanceof LoginException);
- }
-
- @Test public void testBadConfig() throws Exception {
- ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "foo");
- ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "bar");
-
- final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
- ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigNames(Collections.singletonList("ShibBadAuth"));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
- validator.initialize();
-
- action.initialize();
-
- doExtract(prc);
-
- final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
- AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
- Assert.assertEquals(errorCtx.getExceptions().size(), 1);
- Assert.assertTrue(errorCtx.getExceptions().get(0) instanceof LoginException);
- }
-
- @Test public void testUnsupportedConfig() throws Exception {
+
+ @Test public void testUnsupported() throws Exception {
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "foo");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "bar");
@@ -192,11 +118,7 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
rpc.setOperator("exact");
rpc.setRequestedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("test1")));
- validator.setLoginConfigurations(Collections.singletonList(new Pair<String,Collection<Principal>>("ShibUserPassAuth",
- Collections.<Principal>singletonList(new TestPrincipal("test2")))));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
+ validator.setSupportedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("test2")));
validator.initialize();
action.initialize();
@@ -232,20 +154,18 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
validator.initialize();
+
action.initialize();
doExtract(prc);
final Event event = action.execute(src);
- ActionTestingSupport.assertEvent(event, "UnknownUsername");
+ ActionTestingSupport.assertEvent(event, AuthnEventIds.UNKNOWN_USERNAME);
AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
Assert.assertTrue(errorCtx.getExceptions().get(0) instanceof LoginException);
- Assert.assertTrue(errorCtx.isClassifiedError("UnknownUsername"));
+ Assert.assertTrue(errorCtx.isClassifiedError(AuthnEventIds.UNKNOWN_USERNAME));
Assert.assertFalse(errorCtx.isClassifiedError("InvalidPassword"));
}
@@ -255,9 +175,7 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
+
validator.initialize();
action.initialize();
@@ -272,16 +190,13 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
Assert.assertTrue(errorCtx.isClassifiedError("InvalidPassword"));
}
- @Test public void testAuthorized() throws Exception {
+ @Test public void testAuthorizedMD5() throws Exception {
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
validator.initialize();
action.initialize();
@@ -297,17 +212,14 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
.next().getName(), "PETER_THE_PRINCIPAL");
}
- @Test public void testAuthorizedAndKeep() throws Exception {
+ @Test public void testAuthorizedMD5WithFile() throws Exception {
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
- validator.setRemoveContextAfterValidation(false);
+ validator.setResource(new FileSystemResource(getCurrentDir() + "/src/test/resources/" + DATA_PATH + "/htpasswd.txt"));
validator.initialize();
action.initialize();
@@ -317,30 +229,41 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final Event event = action.execute(src);
ActionTestingSupport.assertProceedEvent(event);
- Assert.assertNotNull(ac.getSubcontext(UsernamePasswordContext.class));
+ Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
Assert.assertNotNull(ac.getAuthenticationResult());
Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(UsernamePrincipal.class).iterator()
.next().getName(), "PETER_THE_PRINCIPAL");
}
- @Test public void testSupported() throws Exception {
- ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL");
+ @Test public void testAuthorizedSHA() throws Exception {
+ ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL2");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
+
+ validator.initialize();
- final RequestedPrincipalContext rpc = ac.getSubcontext(RequestedPrincipalContext.class, true);
- rpc.getPrincipalEvalPredicateFactoryRegistry().register(
- TestPrincipal.class, "exact", new ExactPrincipalEvalPredicateFactory());
- rpc.setOperator("exact");
- rpc.setRequestedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("test1")));
+ action.initialize();
+
+ doExtract(prc);
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
+ Assert.assertNotNull(ac.getAuthenticationResult());
+ Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(UsernamePrincipal.class).iterator()
+ .next().getName(), "PETER_THE_PRINCIPAL2");
+ }
+
+ @Test public void testAuthorizedCrypt() throws Exception {
+ ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL3");
+ ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
+
+ final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+ ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigurations(Collections.singletonList(new Pair<String,Collection<Principal>>("ShibUserPassAuth",
- Collections.<Principal>singletonList(new TestPrincipal("test1")))));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
validator.initialize();
action.initialize();
@@ -353,22 +276,17 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
Assert.assertNotNull(ac.getAuthenticationResult());
Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(UsernamePrincipal.class).iterator()
- .next().getName(), "PETER_THE_PRINCIPAL");
- Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(TestPrincipal.class).iterator()
- .next().getName(), "test1");
+ .next().getName(), "PETER_THE_PRINCIPAL3");
}
- @Test public void testMultiConfigAuthorized() throws Exception {
+ @Test public void testAuthorizedAndKeep() throws Exception {
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
- validator.setLoginConfigNames(Arrays.asList("ShibBadAuth", "ShibUserPassAuth"));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
+ validator.setRemoveContextAfterValidation(false);
validator.initialize();
action.initialize();
@@ -378,23 +296,26 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final Event event = action.execute(src);
ActionTestingSupport.assertProceedEvent(event);
- Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
+ Assert.assertNotNull(ac.getSubcontext(UsernamePasswordContext.class));
Assert.assertNotNull(ac.getAuthenticationResult());
Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(UsernamePrincipal.class).iterator()
.next().getName(), "PETER_THE_PRINCIPAL");
}
-
- @Test public void testMatchAndAuthorized() throws Exception {
+
+ @Test public void testSupported() throws Exception {
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("username", "PETER_THE_PRINCIPAL");
((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("password", "changeit");
final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
ac.setAttemptedFlow(authenticationFlows.get(0));
+
+ final RequestedPrincipalContext rpc = ac.getSubcontext(RequestedPrincipalContext.class, true);
+ rpc.getPrincipalEvalPredicateFactoryRegistry().register(
+ TestPrincipal.class, "exact", new ExactPrincipalEvalPredicateFactory());
+ rpc.setOperator("exact");
+ rpc.setRequestedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("test1")));
- validator.setLoginConfigType("JavaLoginConfig");
- validator.setLoginConfigParameters(new URIParameter(URISupport.fileURIFromAbsolutePath(getCurrentDir()
- + '/' + DATA_PATH + "jaas.config")));
- validator.setMatchExpression(Pattern.compile(".+_THE_.+"));
+ validator.setSupportedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("test1")));
validator.initialize();
action.initialize();
@@ -403,11 +324,13 @@ public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationC
final Event event = action.execute(src);
ActionTestingSupport.assertProceedEvent(event);
- Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
+ Assert.assertNull(ac.getSubcontext(UsernamePasswordContext.class));
Assert.assertNotNull(ac.getAuthenticationResult());
Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(UsernamePrincipal.class).iterator()
.next().getName(), "PETER_THE_PRINCIPAL");
+ Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(TestPrincipal.class).iterator()
+ .next().getName(), "test1");
}
private void doExtract(ProfileRequestContext prc) throws Exception {
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/JAASCredentialValidatorTest.java
similarity index 99%
rename from idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java
rename to idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/JAASCredentialValidatorTest.java
index 3757665..bcf7669 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstJAASTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/JAASCredentialValidatorTest.java
@@ -58,7 +58,7 @@ import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.sdk.LDAPException;
/** Unit test for JAAS validation. */
-public class ValidateUsernamePasswordAgainstJAASTest extends BaseAuthenticationContextTest {
+public class JAASCredentialValidatorTest extends BaseAuthenticationContextTest {
private static final String DATA_PATH = "src/test/resources/net/shibboleth/idp/authn/impl/";
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstLDAPTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/LDAPCredentialValidatorTest.java
similarity index 99%
rename from idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstLDAPTest.java
rename to idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/LDAPCredentialValidatorTest.java
index 8175e77..adf8ada 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/ValidateUsernamePasswordAgainstLDAPTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/LDAPCredentialValidatorTest.java
@@ -64,7 +64,7 @@ import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.sdk.LDAPException;
/** Unit test for LDAP credential validation. */
-public class ValidateUsernamePasswordAgainstLDAPTest extends BaseAuthenticationContextTest {
+public class LDAPCredentialValidatorTest extends BaseAuthenticationContextTest {
private static final String DATA_PATH = "src/test/resources/net/shibboleth/idp/authn/impl/";
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/authn/impl/htpasswd.txt b/idp-authn-impl/src/test/resources/net/shibboleth/idp/authn/impl/htpasswd.txt
new file mode 100644
index 0000000..43d0606
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/authn/impl/htpasswd.txt
@@ -0,0 +1,3 @@
+PETER_THE_PRINCIPAL:$apr1$vBz5k7hO$RuB./7oGOpH05ga4aeb2f/
+PETER_THE_PRINCIPAL2:{SHA}BzE/DjIPIsv6Nc/CIFCOs/9FfH4=
+PETER_THE_PRINCIPAL3:fhGkPZphLLGwE
diff --git a/idp-conf/src/main/resources/conf/authn/password-authn-config.xml b/idp-conf/src/main/resources/conf/authn/password-authn-config.xml
index 3b8e3bb..bfde882 100644
--- a/idp-conf/src/main/resources/conf/authn/password-authn-config.xml
+++ b/idp-conf/src/main/resources/conf/authn/password-authn-config.xml
@@ -65,6 +65,7 @@
<entry key="UnknownUsername">
<list>
<value>NoCredentials</value>
+ <value>UnknownUsername</value>
<value>CLIENT_NOT_FOUND</value>
<value>Client not found</value>
<value>DN_RESOLUTION_FAILURE</value>
diff --git a/idp-conf/src/main/resources/system/flows/authn/password-authn-beans.xml b/idp-conf/src/main/resources/system/flows/authn/password-authn-beans.xml
index d0dd086..00533d1 100644
--- a/idp-conf/src/main/resources/system/flows/authn/password-authn-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/password-authn-beans.xml
@@ -70,6 +70,11 @@
p:removeContextAfterValidation="#{getObject('shibboleth.authn.Password.RemoveAfterValidation') ?: true}"
p:matchExpression="#{getObject('shibboleth.authn.Password.matchExpression')}" />
+ <!-- New validator(s) that didn't exist in prior versions. -->
+ <bean id="shibboleth.HTPasswdCredentialValidator" parent="shibboleth.CredentialValidator" abstract="true"
+ class="net.shibboleth.idp.authn.impl.HTPasswdCredentialValidator"
+ p:id="htpasswd" />
+
<!-- Alias the legacy names into "officially" supported parent bean names. -->
<alias alias="shibboleth.JAASValidator" name="ValidateUsernamePasswordAgainstJAAS" />
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list