diff --git a/changelog.txt b/changelog.txt
index 054ef35f95..a9bb74f532 100644
--- a/changelog.txt
+++ b/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
diff --git a/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationException.java b/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationException.java
new file mode 100644
index 0000000000..b914f90291
--- /dev/null
+++ b/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 RemoteAuthenticationManager
cannot validate the
+ * presented authentication request.
+ *
+ *
+ * This is thrown rather than the normal AuthenticationException
+ * because AuthenticationException
contains additional properties
+ * which may cause issues for the remoting protocol.
+ *
RemoteAuthenticationException
with the
+ * specified message and no root cause.
+ *
+ * @param msg the detail message
+ */
+ public RemoteAuthenticationException(String msg) {
+ super(msg);
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManager.java b/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManager.java
new file mode 100644
index 0000000000..12e51bc3b3
--- /dev/null
+++ b/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
+ * GrantedAuthority[]
objects will be returned.
+ *
+ * + * 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. + *
+ * + * @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; +} diff --git a/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImpl.java b/core/src/main/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImpl.java new file mode 100644 index 0000000000..70f4168b84 --- /dev/null +++ b/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. + * + *
+ * This bean requires no security interceptor to protect it. Instead, the bean
+ * uses the configured AuthenticationManager
to resolve an
+ * authentication request.
+ *
+ * A new Authentication
object is created by this class comprising
+ * the request Authentication
object's principal
,
+ * credentials
and the GrantedAuthority
[]s returned
+ * by the RemoteAuthenticationManager
.
+ *
+ * The RemoteAuthenticationManager
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 Authentication
request object.
+ * In practical terms this means the RemoteAuthenticationManager
+ * will not be protected by BASIC or any other HTTP-level
+ * authentication.
+ *
+ * If authentication fails, a RemoteAuthenticationException
will
+ * be thrown. This exception should be caught and displayed to the user,
+ * enabling them to retry with alternative credentials etc.
+ *
Authentication
object.
+
+
+
diff --git a/core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImplTests.java b/core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationManagerImplTests.java
new file mode 100644
index 0000000000..e6c4ecc8f7
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/rcp/RemoteAuthenticationProviderTests.java
new file mode 100644
index 0000000000..060bcbfb36
--- /dev/null
+++ b/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");
+ }
+ }
+ }
+}