From 2c23c75f91f895e7e16e2faec1f84dae5aaf8661 Mon Sep 17 00:00:00 2001 From: Ray Krueger Date: Wed, 27 Apr 2005 04:47:41 +0000 Subject: [PATCH] SecureContextLoginModule as requested from list with Test --- .../jaas/SecureContextLoginModule.java | 174 ++++++++++++++++++ .../jaas/SecureContextLoginModuleTest.java | 108 +++++++++++ 2 files changed, 282 insertions(+) create mode 100644 core/src/main/java/org/acegisecurity/providers/jaas/SecureContextLoginModule.java create mode 100644 core/src/test/java/org/acegisecurity/providers/jaas/SecureContextLoginModuleTest.java diff --git a/core/src/main/java/org/acegisecurity/providers/jaas/SecureContextLoginModule.java b/core/src/main/java/org/acegisecurity/providers/jaas/SecureContextLoginModule.java new file mode 100644 index 0000000000..614759f164 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/jaas/SecureContextLoginModule.java @@ -0,0 +1,174 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * 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 + * + * 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.sf.acegisecurity.providers.jaas; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.security.SecureContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + + +/** + * An implementation of {@link LoginModule} that uses an Acegi Security {@link + * SecureContext} to provide authentication.
+ * This LoginModule provides opposite functionality to the {@link + * JaasAuthenticationProvider} API, and should not really be used in + * conjunction.
+ * The {@link JaasAuthenticationProvider} allows Acegi to authenticate against + * Jaas.
+ * The SecureContextLoginModule allows a Jaas based application to authenticate + * against Acegi. + * + * @author Brian Moseley + * @author Ray Krueger + */ +public class SecureContextLoginModule implements LoginModule { + //~ Static fields/initializers ============================================= + + private static final Log log = LogFactory.getLog(SecureContextLoginModule.class); + + //~ Instance fields ======================================================== + + private Authentication authen; + private Subject subject; + + //~ Methods ================================================================ + + /** + * Abort the authentication process by forgetting the Acegi Security + * Authentication. + * + * @return true if this method succeeded, or false if this + * LoginModule should be ignored. + * + * @exception LoginException if the abort fails + */ + public boolean abort() throws LoginException { + if (authen == null) { + return false; + } + + authen = null; + + return true; + } + + /** + * Authenticate the Subject (phase two) by adding the Acegi + * Security Authentication to the Subject's + * principals. + * + * @return true if this method succeeded, or false if this + * LoginModule should be ignored. + * + * @exception LoginException if the commit fails + */ + public boolean commit() throws LoginException { + if (authen == null) { + return false; + } + + subject.getPrincipals().add(authen); + + return true; + } + + /** + * Initialize this LoginModule. Ignores the callback handler, + * since the code establishing the LoginContext likely won't + * provide one that understands Acegi Security. Also ignores the + * sharedState and options parameters, since + * none are recognized. + * + * @param subject the Subject to be authenticated.

+ * @param callbackHandler is ignored + * @param sharedState is ignored + * @param options are ignored + */ + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + this.subject = subject; + } + + /** + * Authenticate the Subject (phase one) by extracting the + * Acegi Security Authentication from the current + * SecureContext. + * + * @return true if the authentication succeeded, or false if this + * LoginModule should be ignored. + * + * @throws LoginException if the authentication fails + */ + public boolean login() throws LoginException { + if (ContextHolder.getContext() == null) { + log.debug("no security context found"); + return false; + } + + if (!(ContextHolder.getContext() instanceof SecureContext)) { + log.debug("security context not instance of SecureContext"); + + return false; + } + + SecureContext context = (SecureContext) ContextHolder.getContext(); + authen = context.getAuthentication(); + + if (authen == null) { + throw new LoginException("Authentication not found in security" + + " context"); + } + + return true; + } + + /** + * Log out the Subject. + * + * @return true if this method succeeded, or false if this + * LoginModule should be ignored. + * + * @exception LoginException if the logout fails + */ + public boolean logout() throws LoginException { + if (authen == null) { + return false; + } + + subject.getPrincipals().remove(authen); + authen = null; + + return true; + } + + Authentication getAuthentication() { + return authen; + } + + Subject getSubject() { + return subject; + } +} diff --git a/core/src/test/java/org/acegisecurity/providers/jaas/SecureContextLoginModuleTest.java b/core/src/test/java/org/acegisecurity/providers/jaas/SecureContextLoginModuleTest.java new file mode 100644 index 0000000000..56f17522ff --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/jaas/SecureContextLoginModuleTest.java @@ -0,0 +1,108 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * 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 + * + * 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.sf.acegisecurity.providers.jaas; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.ContextImpl; +import net.sf.acegisecurity.context.security.SecureContextImpl; +import net.sf.acegisecurity.context.security.SecureContext; +import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; + +import java.util.HashSet; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + + +/** + * DOCUMENT ME! + * + * @author Ray Krueger + */ +public class SecureContextLoginModuleTest extends TestCase { + //~ Instance fields ======================================================== + + private SecureContextLoginModule module = null; + private Subject subject = new Subject(false, new HashSet(), new HashSet(), + new HashSet()); + private UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("principal", "credentials"); + + //~ Methods ================================================================ + + public void testLoginException() throws Exception { + try { + module.login(); + fail("LoginException expected, there is no Authentication in the SecureContext"); + } catch (LoginException e) { + } + } + + public void testLoginSuccess() throws Exception { + SecureContext sc = (SecureContext) ContextHolder.getContext(); + sc.setAuthentication(auth); + assertTrue("Login should succeed, there is an authentication set", module.login()); + assertTrue("The authentication is not null, this should return true", module.commit()); + assertTrue("Principals should contain the authentication", subject.getPrincipals().contains(auth)); + } + + public void testNoContext() throws Exception { + ContextHolder.setContext(null); + assertFalse("Should return false and ask to be ignored", module.login()); + } + + public void testUnsupportedContext() throws Exception { + ContextHolder.setContext(new ContextImpl()); + assertFalse("Should return false and ask to be ignored", module.login()); + } + + public void testLogout() throws Exception { + SecureContext sc = (SecureContext) ContextHolder.getContext(); + sc.setAuthentication(auth); + module.login(); + assertTrue("Should return true as it succeeds", module.logout()); + assertEquals("Authentication should be null", null, module.getAuthentication()); + + assertFalse("Principals should not contain the authentication after logout", subject.getPrincipals().contains(auth)); + } + + public void testNullLogout() throws Exception { + assertFalse(module.logout()); + } + + public void testAbort() throws Exception { + assertFalse("Should return false, no auth is set", module.abort()); + SecureContext sc = (SecureContext) ContextHolder.getContext(); + sc.setAuthentication(auth); + module.login(); + module.commit(); + assertTrue(module.abort()); + } + + protected void setUp() throws Exception { + module = new SecureContextLoginModule(); + + module.initialize(subject, null, null, null); + + ContextHolder.setContext(new SecureContextImpl()); + } + + protected void tearDown() throws Exception { + ContextHolder.setContext(null); + module = null; + } +}