Przeglądaj źródła

Initial commit of remote client authentication interface.

Ben Alex 21 lat temu
rodzic
commit
c1e109da74

+ 1 - 0
changelog.txt

@@ -11,6 +11,7 @@ Changes in version 0.6 (2004-xx-xx)
 * Added support for EL expressions in the authz tag library
 * Added failed Authentication object to AuthenticationExceptions
 * Added signed JARs to all official release builds (see readme.txt)
+* Added remote client authentication validation package
 * Updated Authentication to be serializable (Weblogic support)
 * Updated to Clover 1.3
 * Updated to HSQLDB version 1.7.2 Release Candidate 6D

+ 46 - 0
core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationException.java

@@ -0,0 +1,46 @@
+/* Copyright 2004 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.rcp;
+
+import net.sf.acegisecurity.AcegiSecurityException;
+
+
+/**
+ * Thrown if a <code>RemoteAuthenticationManager</code> cannot validate the
+ * presented authentication request.
+ * 
+ * <P>
+ * This is thrown rather than the normal <code>AuthenticationException</code>
+ * because <code>AuthenticationException</code> contains additional properties
+ * which may cause issues for the remoting protocol.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RemoteAuthenticationException extends AcegiSecurityException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>RemoteAuthenticationException</code> with the
+     * specified message and no root cause.
+     *
+     * @param msg the detail message
+     */
+    public RemoteAuthenticationException(String msg) {
+        super(msg);
+    }
+}

+ 56 - 0
core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManager.java

@@ -0,0 +1,56 @@
+/* Copyright 2004 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.rcp;
+
+import net.sf.acegisecurity.GrantedAuthority;
+
+
+/**
+ * Allows remote clients to attempt authentication.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface RemoteAuthenticationManager {
+    //~ Methods ================================================================
+
+    /**
+     * Attempts to authenticate the remote client using the presented username
+     * and password. If authentication is successful, an array of
+     * <code>GrantedAuthority[]</code> objects will be returned.
+     * 
+     * <P>
+     * In order to maximise remoting protocol compatibility, a design decision
+     * was taken to operate with minimal arguments and return only the minimal
+     * amount information required for remote clients to enable/disable
+     * relevant user interface commands etc. There is nothing preventing users
+     * from implementing their own equivalent package that works with more
+     * complex object types.
+     * </p>
+     *
+     * @param username the username the remote client wishes to authenticate
+     *        with
+     * @param password the password the remote client wishes to authenticate
+     *        wish
+     *
+     * @return all of the granted authorities the specified username and
+     *         password have access to
+     *
+     * @throws RemoteAuthenticationException if the authentication failed
+     */
+    public GrantedAuthority[] attemptAuthentication(String username,
+        String password) throws RemoteAuthenticationException;
+}

+ 73 - 0
core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImpl.java

@@ -0,0 +1,73 @@
+/* Copyright 2004 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.rcp;
+
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * Server-side processor of a remote authentication request.
+ * 
+ * <P>
+ * This bean requires no security interceptor to protect it. Instead, the bean
+ * uses the configured <code>AuthenticationManager</code> to resolve an
+ * authentication request.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RemoteAuthenticationManagerImpl
+    implements RemoteAuthenticationManager, InitializingBean {
+    //~ Instance fields ========================================================
+
+    private AuthenticationManager authenticationManager;
+
+    //~ Methods ================================================================
+
+    public void setAuthenticationManager(
+        AuthenticationManager authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+
+    public AuthenticationManager getAuthenticationManager() {
+        return authenticationManager;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (this.authenticationManager == null) {
+            throw new IllegalArgumentException(
+                "authenticationManager is required");
+        }
+    }
+
+    public GrantedAuthority[] attemptAuthentication(String username,
+        String password) throws RemoteAuthenticationException {
+        UsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken(username,
+                password);
+
+        try {
+            return authenticationManager.authenticate(request).getAuthorities();
+        } catch (AuthenticationException authEx) {
+            throw new RemoteAuthenticationException(authEx.getMessage());
+        }
+    }
+}

+ 102 - 0
core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationProvider.java

@@ -0,0 +1,102 @@
+/* Copyright 2004 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.rcp;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.providers.AuthenticationProvider;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * Client-side object which queries a  {@link RemoteAuthenticationManager} to
+ * validate an authentication request.
+ * 
+ * <P>
+ * A new <code>Authentication</code> object is created by this class comprising
+ * the request <code>Authentication</code> object's <code>principal</code>,
+ * <code>credentials</code> and the <code>GrantedAuthority</code>[]s returned
+ * by the <code>RemoteAuthenticationManager</code>.
+ * </p>
+ * 
+ * <P>
+ * The <code>RemoteAuthenticationManager</code> should not require any special
+ * username or password setting on the remoting client proxy factory to
+ * execute the call. Instead the entire authentication request must be
+ * encapsulated solely within the <code>Authentication</code> request object.
+ * In practical terms this means the <code>RemoteAuthenticationManager</code>
+ * will <B>not</B> be protected by BASIC or any other HTTP-level
+ * authentication.
+ * </p>
+ * 
+ * <P>
+ * If authentication fails, a <code>RemoteAuthenticationException</code> will
+ * be thrown. This exception should be caught and displayed to the user,
+ * enabling them to retry with alternative credentials etc.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RemoteAuthenticationProvider implements AuthenticationProvider,
+    InitializingBean {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(RemoteAuthenticationProvider.class);
+
+    //~ Instance fields ========================================================
+
+    private RemoteAuthenticationManager remoteAuthenticationManager;
+
+    //~ Methods ================================================================
+
+    public void setRemoteAuthenticationManager(
+        RemoteAuthenticationManager remoteAuthenticationManager) {
+        this.remoteAuthenticationManager = remoteAuthenticationManager;
+    }
+
+    public RemoteAuthenticationManager getRemoteAuthenticationManager() {
+        return remoteAuthenticationManager;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (this.remoteAuthenticationManager == null) {
+            throw new IllegalArgumentException(
+                "remoteAuthenticationManager is mandatory");
+        }
+    }
+
+    public Authentication authenticate(Authentication authentication)
+        throws AuthenticationException {
+        String username = authentication.getPrincipal().toString();
+        String password = authentication.getCredentials().toString();
+        GrantedAuthority[] authorities = remoteAuthenticationManager
+            .attemptAuthentication(username, password);
+
+        return new UsernamePasswordAuthenticationToken(username, password,
+            authorities);
+    }
+
+    public boolean supports(Class authentication) {
+        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
+    }
+}

+ 7 - 0
core/src/main/java/org/acegisecurity/providers/rcp/package.html

@@ -0,0 +1,7 @@
+<html>
+<body>
+Allows remote clients to authenticate and obtain a populated
+<code>Authentication</code> object.
+</body>
+</html>
+

+ 83 - 0
core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImplTests.java

@@ -0,0 +1,83 @@
+/* Copyright 2004 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.rcp;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.MockAuthenticationManager;
+
+
+/**
+ * Tests {@link RemoteAuthenticationManagerImpl}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RemoteAuthenticationManagerImplTests extends TestCase {
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(RemoteAuthenticationManagerImplTests.class);
+    }
+
+    public void testFailedAuthenticationReturnsRemoteAuthenticationException() {
+        RemoteAuthenticationManagerImpl manager = new RemoteAuthenticationManagerImpl();
+        manager.setAuthenticationManager(new MockAuthenticationManager(false));
+
+        try {
+            manager.attemptAuthentication("marissa", "password");
+            fail("Should have thrown RemoteAuthenticationException");
+        } catch (RemoteAuthenticationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGettersSetters() {
+        RemoteAuthenticationManagerImpl manager = new RemoteAuthenticationManagerImpl();
+        manager.setAuthenticationManager(new MockAuthenticationManager(true));
+        assertNotNull(manager.getAuthenticationManager());
+    }
+
+    public void testStartupChecksAuthenticationManagerSet()
+        throws Exception {
+        RemoteAuthenticationManagerImpl manager = new RemoteAuthenticationManagerImpl();
+
+        try {
+            manager.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        manager.setAuthenticationManager(new MockAuthenticationManager(true));
+        manager.afterPropertiesSet();
+        assertTrue(true);
+    }
+
+    public void testSuccessfulAuthentication() {
+        RemoteAuthenticationManagerImpl manager = new RemoteAuthenticationManagerImpl();
+        manager.setAuthenticationManager(new MockAuthenticationManager(true));
+
+        GrantedAuthority[] result = manager.attemptAuthentication("marissa",
+                "password");
+        assertTrue(true);
+    }
+}

+ 117 - 0
core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationProviderTests.java

@@ -0,0 +1,117 @@
+/* Copyright 2004 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.rcp;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+
+/**
+ * Tests {@link RemoteAuthenticationProvider}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RemoteAuthenticationProviderTests extends TestCase {
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(RemoteAuthenticationProviderTests.class);
+    }
+
+    public void testExceptionsGetPassedBackToCaller() {
+        RemoteAuthenticationProvider provider = new RemoteAuthenticationProvider();
+        provider.setRemoteAuthenticationManager(new MockRemoteAuthenticationManager(
+                false));
+
+        try {
+            provider.authenticate(new UsernamePasswordAuthenticationToken(
+                    "marissa", "password"));
+            fail("Should have thrown RemoteAuthenticationException");
+        } catch (RemoteAuthenticationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGettersSetters() {
+        RemoteAuthenticationProvider provider = new RemoteAuthenticationProvider();
+        provider.setRemoteAuthenticationManager(new MockRemoteAuthenticationManager(
+                true));
+        assertNotNull(provider.getRemoteAuthenticationManager());
+    }
+
+    public void testStartupChecksAuthenticationManagerSet()
+        throws Exception {
+        RemoteAuthenticationProvider provider = new RemoteAuthenticationProvider();
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        provider.setRemoteAuthenticationManager(new MockRemoteAuthenticationManager(
+                true));
+        provider.afterPropertiesSet();
+        assertTrue(true);
+    }
+
+    public void testSuccessfulAuthenticationCreatesObject() {
+        RemoteAuthenticationProvider provider = new RemoteAuthenticationProvider();
+        provider.setRemoteAuthenticationManager(new MockRemoteAuthenticationManager(
+                true));
+
+        Authentication result = provider.authenticate(new UsernamePasswordAuthenticationToken(
+                    "marissa", "password"));
+        assertEquals("marissa", result.getPrincipal());
+        assertEquals("password", result.getCredentials());
+        assertEquals("foo", result.getAuthorities()[0].getAuthority());
+    }
+
+    public void testSupports() {
+        RemoteAuthenticationProvider provider = new RemoteAuthenticationProvider();
+        assertTrue(provider.supports(UsernamePasswordAuthenticationToken.class));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockRemoteAuthenticationManager
+        implements RemoteAuthenticationManager {
+        private boolean grantAccess;
+
+        public MockRemoteAuthenticationManager(boolean grantAccess) {
+            this.grantAccess = grantAccess;
+        }
+
+        public GrantedAuthority[] attemptAuthentication(String username,
+            String password) throws RemoteAuthenticationException {
+            if (grantAccess) {
+                return new GrantedAuthority[] {new GrantedAuthorityImpl("foo")};
+            } else {
+                throw new RemoteAuthenticationException("as requested");
+            }
+        }
+    }
+}