Merge pull request #184 from fpapon/SHIRO-669
[SHIRO-669] Included a boolean flag in FirstSuccessfulStrategy to bre…
diff --git a/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java
index 6a99ff0..87cb5cc 100644
--- a/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java
+++ b/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java
@@ -37,6 +37,17 @@
*/
public class FirstSuccessfulStrategy extends AbstractAuthenticationStrategy {
+ private boolean stopAfterFirstSuccess;
+
+ public void setStopAfterFirstSuccess (boolean stopAfterFirstSuccess ) {
+
+ this.stopAfterFirstSuccess = stopAfterFirstSuccess ;
+ }
+
+ public boolean getStopAfterFirstSuccess() {
+ return stopAfterFirstSuccess ;
+ }
+
/**
* Returns {@code null} immediately, relying on this class's {@link #merge merge} implementation to return
* only the first {@code info} object it encounters, ignoring all subsequent ones.
@@ -45,6 +56,22 @@
return null;
}
+
+ /**
+ * Throws ShortCircuitIterationException if stopAfterFirstSuccess is set and authentication is
+ * successful with a previously consulted realm.
+ * Returns the <code>aggregate</code> method argument, without modification
+ * otherwise.
+ */
+ public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
+ if (getStopAfterFirstSuccess() && aggregate != null && isEmpty(aggregate.getPrincipals())) {
+ throw new ShortCircuitIterationException();
+ }
+ return aggregate;
+ }
+
+
+
private static boolean isEmpty(PrincipalCollection pc) {
return pc == null || pc.isEmpty();
}
diff --git a/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java b/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java
index 41ebe41..02bd3aa 100644
--- a/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java
+++ b/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java
@@ -207,7 +207,13 @@
for (Realm realm : realms) {
- aggregate = strategy.beforeAttempt(realm, token, aggregate);
+ try {
+ aggregate = strategy.beforeAttempt(realm, token, aggregate);
+ } catch (ShortCircuitIterationException shortCircuitSignal) {
+ // Break from continuing with subsequnet realms on receiving
+ // short circuit signal from strategy
+ break;
+ }
if (realm.supports(token)) {
diff --git a/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java b/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java
new file mode 100644
index 0000000..0522b24
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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 org.apache.shiro.authc.pam;
+
+import org.apache.shiro.authc.AuthenticationException;
+
+
+/**
+ * Exception thrown during the authentication process using
+ * {@link org.apache.shiro.authc.pam.FirstSuccessfulStrategy}, with
+ * <code>stopAfterFirstSuccess</code> set.
+ * This is a signal to short circuit the authentication from proceeding
+ * with subsequent {@link org.apache.shiro.realm.Realm Realm}s
+ * after a first successful authentication.
+ *
+ * @see org.apache.shiro.authc.pam.AuthenticationStrategy
+ * @see org.apache.shiro.authc.pam.FirstSuccessfulStrategy
+ * @since 1.4.1
+ */
+public class ShortCircuitIterationException extends AuthenticationException {
+
+ /**
+ * Creates a new ShortCircuitIterationException.
+ */
+ public ShortCircuitIterationException() {
+ super();
+ }
+
+ /**
+ * Constructs a new ShortCircuitIterationException.
+ *
+ * @param message the reason for the exception
+ */
+ public ShortCircuitIterationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new ShortCircuitIterationException.
+ *
+ * @param cause the underlying Throwable that caused this exception to be thrown.
+ */
+ public ShortCircuitIterationException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs a new ShortCircuitIterationException.
+ *
+ * @param message the reason for the exception
+ * @param cause the underlying Throwable that caused this exception to be thrown.
+ */
+ public ShortCircuitIterationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java b/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java
index ce95416..68fe395 100644
--- a/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java
+++ b/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java
@@ -1,35 +1,34 @@
/*
- * Copyright (c) 2019 Nova Ordis LLC
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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
*
- * Licensed 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
*
- * 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.
+ * 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 org.apache.shiro.authc.pam;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.MergableAuthenticationInfo;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
-/**
- * Created by lei.li4 on 2019/1/23
- */
+
public class FirstSuccessfulStrategyTest {
private FirstSuccessfulStrategy strategy;
@@ -37,6 +36,7 @@
@Before
public void setUp() {
strategy = new FirstSuccessfulStrategy();
+ strategy.setStopAfterFirstSuccess(true);
}
@Test
@@ -89,6 +89,19 @@
AuthenticationInfo authInfo = new SimpleAuthenticationInfo();
AuthenticationInfo mergeResult = strategy.merge(authInfo, aggregate);
assertEquals(authInfo, mergeResult);
+ AuthenticationInfo info = strategy.beforeAllAttempts(null, null);
+ assertNull(info);
+ }
+
+ @Test
+ public void testBeforeAttemptNull() {
+ assertNull(strategy.beforeAttempt(null, null, null));
+ }
+
+ @Test (expected=ShortCircuitIterationException.class)
+ public void testBeforeAttemptStopAfterFirstSuccess() {
+ AuthenticationInfo aggregate = new SimpleAuthenticationInfo();
+ strategy.beforeAttempt(null, null, aggregate);
}
}