Jelajahi Sumber

SecureContextLoginModule as requested from list with Test

Ray Krueger 20 tahun lalu
induk
melakukan
2c23c75f91

+ 174 - 0
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. <br>
+ * This LoginModule provides opposite functionality to the {@link
+ * JaasAuthenticationProvider} API, and should not really be used in
+ * conjunction. <br>
+ * The {@link JaasAuthenticationProvider} allows Acegi to authenticate against
+ * Jaas. <br>
+ * 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
+     * <code>Authentication</code>.
+     *
+     * @return true if this method succeeded, or false if this
+     *         <code>LoginModule</code> 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 <code>Subject</code> (phase two) by adding the Acegi
+     * Security <code>Authentication</code> to the <code>Subject</code>'s
+     * principals.
+     *
+     * @return true if this method succeeded, or false if this
+     *         <code>LoginModule</code> 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 <code>LoginModule</code>. Ignores the callback handler,
+     * since the code establishing the <code>LoginContext</code> likely won't
+     * provide one that understands Acegi Security. Also ignores the
+     * <code>sharedState</code> and <code>options</code> parameters, since
+     * none are recognized.
+     *
+     * @param subject the <code>Subject</code> to be authenticated. <p>
+     * @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 <code>Subject</code> (phase one) by extracting the
+     * Acegi Security <code>Authentication</code> from the current
+     * <code>SecureContext</code>.
+     *
+     * @return true if the authentication succeeded, or false if this
+     *         <code>LoginModule</code> 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 <code>Subject</code>.
+     *
+     * @return true if this method succeeded, or false if this
+     *         <code>LoginModule</code> 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;
+    }
+}

+ 108 - 0
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;
+    }
+}