فهرست منبع

Initial commit.

Ben Alex 21 سال پیش
کامیت
35fe1e7b73
100فایلهای تغییر یافته به همراه6930 افزوده شده و 0 حذف شده
  1. 22 0
      .classpath
  2. 5 0
      .cvsignore
  3. 17 0
      .project
  4. 216 0
      adapters/catalina/src/main/java/org/acegisecurity/adapters/catalina/CatalinaAcegiUserRealm.java
  5. 7 0
      adapters/catalina/src/main/java/org/acegisecurity/adapters/catalina/package.html
  6. 188 0
      adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java
  7. 72 0
      adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossIntegrationFilter.java
  8. 7 0
      adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/package.html
  9. 147 0
      adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/JettyAcegiUserRealm.java
  10. 55 0
      adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/JettyAcegiUserToken.java
  11. 7 0
      adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/package.html
  12. 163 0
      adapters/resin/src/main/java/org/acegisecurity/adapters/resin/ResinAcegiAuthenticator.java
  13. 7 0
      adapters/resin/src/main/java/org/acegisecurity/adapters/resin/package.html
  14. 464 0
      build.xml
  15. 39 0
      changelog.txt
  16. 15 0
      contributors.txt
  17. 55 0
      core/src/main/java/org/acegisecurity/AccessDecisionManager.java
  18. 40 0
      core/src/main/java/org/acegisecurity/AccessDeniedException.java
  19. 48 0
      core/src/main/java/org/acegisecurity/AcegiSecurityException.java
  20. 69 0
      core/src/main/java/org/acegisecurity/Authentication.java
  21. 42 0
      core/src/main/java/org/acegisecurity/AuthenticationCredentialsNotFoundException.java
  22. 41 0
      core/src/main/java/org/acegisecurity/AuthenticationException.java
  23. 61 0
      core/src/main/java/org/acegisecurity/AuthenticationManager.java
  24. 44 0
      core/src/main/java/org/acegisecurity/AuthenticationServiceException.java
  25. 41 0
      core/src/main/java/org/acegisecurity/BadCredentialsException.java
  26. 53 0
      core/src/main/java/org/acegisecurity/ConfigAttribute.java
  27. 62 0
      core/src/main/java/org/acegisecurity/ConfigAttributeDefinition.java
  28. 39 0
      core/src/main/java/org/acegisecurity/ConfigAttributeEditor.java
  29. 40 0
      core/src/main/java/org/acegisecurity/DisabledException.java
  30. 46 0
      core/src/main/java/org/acegisecurity/GrantedAuthority.java
  31. 64 0
      core/src/main/java/org/acegisecurity/GrantedAuthorityImpl.java
  32. 39 0
      core/src/main/java/org/acegisecurity/LockedException.java
  33. 116 0
      core/src/main/java/org/acegisecurity/MethodDefinitionAttributes.java
  34. 181 0
      core/src/main/java/org/acegisecurity/MethodDefinitionMap.java
  35. 44 0
      core/src/main/java/org/acegisecurity/MethodDefinitionSource.java
  36. 70 0
      core/src/main/java/org/acegisecurity/MethodDefinitionSourceEditor.java
  37. 90 0
      core/src/main/java/org/acegisecurity/RunAsManager.java
  38. 58 0
      core/src/main/java/org/acegisecurity/SecurityConfig.java
  39. 303 0
      core/src/main/java/org/acegisecurity/SecurityInterceptor.java
  40. 100 0
      core/src/main/java/org/acegisecurity/adapters/AbstractAdapterAuthenticationToken.java
  41. 146 0
      core/src/main/java/org/acegisecurity/adapters/AbstractIntegrationFilter.java
  42. 35 0
      core/src/main/java/org/acegisecurity/adapters/AuthByAdapter.java
  43. 77 0
      core/src/main/java/org/acegisecurity/adapters/AuthByAdapterProvider.java
  44. 54 0
      core/src/main/java/org/acegisecurity/adapters/AutoIntegrationFilter.java
  45. 42 0
      core/src/main/java/org/acegisecurity/adapters/HttpRequestIntegrationFilter.java
  46. 55 0
      core/src/main/java/org/acegisecurity/adapters/PrincipalAcegiUserToken.java
  47. 8 0
      core/src/main/java/org/acegisecurity/adapters/package.html
  48. 38 0
      core/src/main/java/org/acegisecurity/context/Context.java
  49. 47 0
      core/src/main/java/org/acegisecurity/context/ContextException.java
  50. 30 0
      core/src/main/java/org/acegisecurity/context/ContextHolder.java
  51. 40 0
      core/src/main/java/org/acegisecurity/context/ContextHolderEmptyException.java
  52. 22 0
      core/src/main/java/org/acegisecurity/context/ContextImpl.java
  53. 50 0
      core/src/main/java/org/acegisecurity/context/ContextInterceptor.java
  54. 42 0
      core/src/main/java/org/acegisecurity/context/ContextInvalidException.java
  55. 31 0
      core/src/main/java/org/acegisecurity/context/SecureContext.java
  56. 41 0
      core/src/main/java/org/acegisecurity/context/SecureContextImpl.java
  57. 10 0
      core/src/main/java/org/acegisecurity/context/package.html
  58. 21 0
      core/src/main/java/org/acegisecurity/package.html
  59. 45 0
      core/src/main/java/org/acegisecurity/providers/AbstractAuthenticationToken.java
  60. 50 0
      core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java
  61. 132 0
      core/src/main/java/org/acegisecurity/providers/ProviderManager.java
  62. 44 0
      core/src/main/java/org/acegisecurity/providers/ProviderNotFoundException.java
  63. 46 0
      core/src/main/java/org/acegisecurity/providers/TestingAuthenticationProvider.java
  64. 67 0
      core/src/main/java/org/acegisecurity/providers/TestingAuthenticationToken.java
  65. 81 0
      core/src/main/java/org/acegisecurity/providers/UsernamePasswordAuthenticationToken.java
  66. 124 0
      core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
  67. 43 0
      core/src/main/java/org/acegisecurity/providers/dao/UsernameNotFoundException.java
  68. 5 0
      core/src/main/java/org/acegisecurity/providers/dao/package.html
  69. 7 0
      core/src/main/java/org/acegisecurity/providers/package.html
  70. 77 0
      core/src/main/java/org/acegisecurity/runas/RunAsImplAuthenticationProvider.java
  71. 108 0
      core/src/main/java/org/acegisecurity/runas/RunAsManagerImpl.java
  72. 93 0
      core/src/main/java/org/acegisecurity/runas/RunAsUserToken.java
  73. 5 0
      core/src/main/java/org/acegisecurity/runas/package.html
  74. 71 0
      core/src/main/java/org/acegisecurity/userdetails/User.java
  75. 46 0
      core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java
  76. 139 0
      core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java
  77. 5 0
      core/src/main/java/org/acegisecurity/userdetails/jdbc/package.html
  78. 57 0
      core/src/main/java/org/acegisecurity/userdetails/memory/InMemoryDaoImpl.java
  79. 72 0
      core/src/main/java/org/acegisecurity/userdetails/memory/UserAttributeDefinition.java
  80. 57 0
      core/src/main/java/org/acegisecurity/userdetails/memory/UserAttributeEditor.java
  81. 50 0
      core/src/main/java/org/acegisecurity/userdetails/memory/UserMap.java
  82. 93 0
      core/src/main/java/org/acegisecurity/userdetails/memory/UserMapEditor.java
  83. 6 0
      core/src/main/java/org/acegisecurity/userdetails/memory/package.html
  84. 98 0
      core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java
  85. 96 0
      core/src/main/java/org/acegisecurity/vote/AccessDecisionVoter.java
  86. 92 0
      core/src/main/java/org/acegisecurity/vote/AffirmativeBased.java
  87. 127 0
      core/src/main/java/org/acegisecurity/vote/ConsensusBased.java
  88. 76 0
      core/src/main/java/org/acegisecurity/vote/RoleVoter.java
  89. 119 0
      core/src/main/java/org/acegisecurity/vote/UnanimousBased.java
  90. 6 0
      core/src/main/java/org/acegisecurity/vote/package.html
  91. 41 0
      core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml
  92. 41 0
      core/src/main/resources/org/acegisecurity/providers/dao/jdbc/acegisecurity-jdbc.xml
  93. 88 0
      core/src/test/java/org/acegisecurity/BankSecurityVoter.java
  94. 43 0
      core/src/test/java/org/acegisecurity/ExoticSecureContext.java
  95. 217 0
      core/src/test/java/org/acegisecurity/SecurityTests.java
  96. 76 0
      core/src/test/java/org/acegisecurity/adapters/AuthByAdapterTests.java
  97. 29 0
      core/src/test/java/org/acegisecurity/adapters/applicationContext.xml
  98. 84 0
      core/src/test/java/org/acegisecurity/applicationContext.xml
  99. 141 0
      core/src/test/java/org/acegisecurity/attribute/AttributesTests.java
  100. 67 0
      core/src/test/java/org/acegisecurity/attribute/MockAttributes.java

+ 22 - 0
.classpath

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+    <classpathentry kind="src" path="src"/>
+    <classpathentry kind="src" path="test"/>
+    <classpathentry kind="src" path="samples/contacts/src"/>
+    <classpathentry kind="src" path="samples/attributes/src"/>
+    <classpathentry kind="src" path="integration-test/src"/>
+    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+    <classpathentry kind="lib" path="lib/aop-alliance/aopalliance.jar"/>
+    <classpathentry kind="lib" path="lib/jakarta-commons/commons-logging.jar"/>
+    <classpathentry kind="lib" path="lib/j2ee/servlet.jar"/>
+    <classpathentry kind="lib" path="lib/junit/junit.jar"/>
+    <classpathentry kind="lib" path="lib/spring/spring.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/jboss/jboss-common-extracted.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/jboss/jbosssx-extracted.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/catalina/catalina-extracted.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/catalina/jmx-extracted.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/jetty/org.mortbay.jetty-extracted.jar"/>
+    <classpathentry kind="lib" path="lib/extracted/resin/resin-extracted.jar"/>
+    <classpathentry kind="lib" path="integration-test/lib/httpunit.jar"/>
+    <classpathentry kind="output" path="target/eclipseclasses"/>
+</classpath>

+ 5 - 0
.cvsignore

@@ -0,0 +1,5 @@
+dist
+target
+build.properties
+*.log
+

+ 17 - 0
.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>acegisecurity</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 216 - 0
adapters/catalina/src/main/java/org/acegisecurity/adapters/catalina/CatalinaAcegiUserRealm.java

@@ -0,0 +1,216 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.catalina;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.realm.RealmBase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.support.FileSystemXmlApplicationContext;
+
+import java.io.File;
+
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+
+import java.util.Map;
+
+
+/**
+ * Adapter to enable Catalina (Tomcat) to authenticate via the Acegi Security
+ * System for Spring.
+ * 
+ * <p>
+ * Returns a {@link PrincipalAcegiUserToken} to Catalina's authentication
+ * system, which is subsequently available via
+ * <code>HttpServletRequest.getUserPrincipal()</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class CatalinaAcegiUserRealm extends RealmBase {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(CatalinaAcegiUserRealm.class);
+
+    //~ Instance fields ========================================================
+
+    protected final String name = "CatalinaSpringUserRealm / $Id$";
+    private AuthenticationManager authenticationManager;
+    private Container container;
+    private String appContextLocation;
+    private String key;
+
+    //~ Methods ================================================================
+
+    public void setAppContextLocation(String appContextLocation) {
+        this.appContextLocation = appContextLocation;
+    }
+
+    public String getAppContextLocation() {
+        return appContextLocation;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public Principal authenticate(String username, String credentials) {
+        if (username == null) {
+            return null;
+        }
+
+        if (credentials == null) {
+            credentials = "";
+        }
+
+        Authentication request = new UsernamePasswordAuthenticationToken(username,
+                credentials);
+        Authentication response = null;
+
+        try {
+            response = authenticationManager.authenticate(request);
+        } catch (AuthenticationException failed) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication request for user: " + username
+                    + " failed: " + failed.toString());
+            }
+
+            return null;
+        }
+
+        return new PrincipalAcegiUserToken(this.key,
+            response.getPrincipal().toString(),
+            response.getCredentials().toString(), response.getAuthorities());
+    }
+
+    public Principal authenticate(String username, byte[] credentials) {
+        return authenticate(username, new String(credentials));
+    }
+
+    /**
+     * Not supported, returns null
+     *
+     * @param username DOCUMENT ME!
+     * @param digest DOCUMENT ME!
+     * @param nonce DOCUMENT ME!
+     * @param nc DOCUMENT ME!
+     * @param cnonce DOCUMENT ME!
+     * @param qop DOCUMENT ME!
+     * @param realm DOCUMENT ME!
+     * @param md5a2 DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     */
+    public java.security.Principal authenticate(java.lang.String username,
+        java.lang.String digest, java.lang.String nonce, java.lang.String nc,
+        java.lang.String cnonce, java.lang.String qop, java.lang.String realm,
+        java.lang.String md5a2) {
+        return null;
+    }
+
+    /**
+     * Not supported, returns null
+     *
+     * @param x509Certificates DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     */
+    public Principal authenticate(X509Certificate[] x509Certificates) {
+        return null;
+    }
+
+    public boolean hasRole(Principal principal, String role) {
+        if (!(principal instanceof PrincipalAcegiUserToken)) {
+            if (logger.isWarnEnabled()) {
+                logger.warn(
+                    "Expected passed principal to be of type PrincipalSpringUserToken but was "
+                    + principal.getClass().getName());
+            }
+
+            return false;
+        }
+
+        PrincipalAcegiUserToken test = (PrincipalAcegiUserToken) principal;
+
+        return test.isUserInRole(role);
+    }
+
+    public void start() throws LifecycleException {
+        super.start();
+
+        if (appContextLocation == null) {
+            throw new LifecycleException("appContextLocation must be defined");
+        }
+
+        if (key == null) {
+            throw new LifecycleException("key must be defined");
+        }
+
+        File xml = new File(System.getProperty("catalina.base"),
+                appContextLocation);
+
+        if (!xml.exists()) {
+            throw new LifecycleException(
+                "appContextLocation does not seem to exist - try specifying conf/springsecurity.xml");
+        }
+
+        FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext(xml
+                .getAbsolutePath());
+        Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
+
+        if (beans.size() == 0) {
+            throw new IllegalArgumentException(
+                "Bean context must contain at least one bean of type AuthenticationManager");
+        }
+
+        String beanName = (String) beans.keySet().iterator().next();
+        authenticationManager = (AuthenticationManager) beans.get(beanName);
+        logger.info("CatalinaSpringUserRealm Started");
+    }
+
+    protected String getName() {
+        return this.name;
+    }
+
+    /**
+     * Always returns null (we override authenticate methods)
+     *
+     * @param arg0 DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     */
+    protected String getPassword(String arg0) {
+        return null;
+    }
+
+    /**
+     * Always returns null (we override authenticate methods)
+     *
+     * @param arg0 DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     */
+    protected Principal getPrincipal(String arg0) {
+        return null;
+    }
+}

+ 7 - 0
adapters/catalina/src/main/java/org/acegisecurity/adapters/catalina/package.html

@@ -0,0 +1,7 @@
+<html>
+<body>
+Adapter to Catalina web container (Tomcat).
+<p>
+</body>
+</html>
+

+ 188 - 0
adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java

@@ -0,0 +1,188 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.jboss;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.jboss.security.SimpleGroup;
+import org.jboss.security.SimplePrincipal;
+import org.jboss.security.auth.spi.AbstractServerLoginModule;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.security.Principal;
+import java.security.acl.Group;
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+
+
+/**
+ * Adapter to enable JBoss to authenticate via the Acegi Security System for
+ * Spring.
+ * 
+ * <p>
+ * Returns a {@link PrincipalAcegiUserToken} to JBoss' authentication system,
+ * which is subsequently available from
+ * <code>java:comp/env/security/subject</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class JbossAcegiLoginModule extends AbstractServerLoginModule {
+    //~ Instance fields ========================================================
+
+    private AuthenticationManager authenticationManager;
+    private Principal identity;
+    private String key;
+    private char[] credential;
+
+    //~ Methods ================================================================
+
+    public void initialize(Subject subject, CallbackHandler callbackHandler,
+        Map sharedState, Map options) {
+        super.initialize(subject, callbackHandler, sharedState, options);
+
+        this.key = (String) options.get("key");
+
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext((String) options
+                .get("appContextLocation"));
+        Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
+
+        if (beans.size() == 0) {
+            throw new IllegalArgumentException(
+                "Bean context must contain at least one bean of type AuthenticationManager");
+        }
+
+        String beanName = (String) beans.keySet().iterator().next();
+        authenticationManager = (AuthenticationManager) beans.get(beanName);
+        super.log.info("Successfully started JbossSpringLoginModule");
+    }
+
+    public boolean login() throws LoginException {
+        super.loginOk = false;
+
+        String[] info = getUsernameAndPassword();
+        String username = info[0];
+        String password = info[1];
+
+        if ((username == null) && (password == null)) {
+            identity = null;
+            super.log.trace("Authenticating as unauthenticatedIdentity="
+                + identity);
+        }
+
+        if (identity == null) {
+            Authentication request = new UsernamePasswordAuthenticationToken(username,
+                    password);
+            Authentication response = null;
+
+            try {
+                response = authenticationManager.authenticate(request);
+            } catch (AuthenticationException failed) {
+                if (super.log.isDebugEnabled()) {
+                    super.log.debug("Bad password for username=" + username);
+                }
+
+                throw new FailedLoginException(
+                    "Password Incorrect/Password Required");
+            }
+
+            identity = new PrincipalAcegiUserToken(this.key,
+                    response.getPrincipal().toString(),
+                    response.getCredentials().toString(),
+                    response.getAuthorities());
+        }
+
+        if (getUseFirstPass() == true) {
+            // Add the username and password to the shared state map
+            sharedState.put("javax.security.auth.login.name", username);
+            sharedState.put("javax.security.auth.login.password", credential);
+        }
+
+        super.loginOk = true;
+        super.log.trace("User '" + identity + "' authenticated, loginOk="
+            + loginOk);
+
+        return true;
+    }
+
+    protected Principal getIdentity() {
+        return this.identity;
+    }
+
+    protected Group[] getRoleSets() throws LoginException {
+        SimpleGroup roles = new SimpleGroup("Roles");
+        Group[] roleSets = {roles};
+
+        if (this.identity instanceof Authentication) {
+            Authentication user = (Authentication) this.identity;
+
+            for (int i = 0; i < user.getAuthorities().length; i++) {
+                roles.addMember(new SimplePrincipal(
+                        user.getAuthorities()[i].getAuthority()));
+            }
+        }
+
+        return roleSets;
+    }
+
+    protected String[] getUsernameAndPassword() throws LoginException {
+        String[] info = {null, null};
+
+        // prompt for a username and password
+        if (callbackHandler == null) {
+            throw new LoginException("Error: no CallbackHandler available "
+                + "to collect authentication information");
+        }
+
+        NameCallback nc = new NameCallback("User name: ", "guest");
+        PasswordCallback pc = new PasswordCallback("Password: ", false);
+        Callback[] callbacks = {nc, pc};
+        String username = null;
+        String password = null;
+
+        try {
+            callbackHandler.handle(callbacks);
+            username = nc.getName();
+
+            char[] tmpPassword = pc.getPassword();
+
+            if (tmpPassword != null) {
+                credential = new char[tmpPassword.length];
+                System.arraycopy(tmpPassword, 0, credential, 0,
+                    tmpPassword.length);
+                pc.clearPassword();
+                password = new String(credential);
+            }
+        } catch (java.io.IOException ioe) {
+            throw new LoginException(ioe.toString());
+        } catch (UnsupportedCallbackException uce) {
+            throw new LoginException("CallbackHandler does not support: "
+                + uce.getCallback());
+        }
+
+        info[0] = username;
+        info[1] = password;
+
+        return info;
+    }
+}

+ 72 - 0
adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossIntegrationFilter.java

@@ -0,0 +1,72 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.jboss;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.adapters.AbstractIntegrationFilter;
+
+import java.security.Principal;
+
+import java.util.Iterator;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import javax.security.auth.Subject;
+
+import javax.servlet.ServletRequest;
+
+
+/**
+ * Populates a {@link net.sf.acegisecurity.context.SecureContext} from JBoss'
+ * <code>java:comp/env/security/subject</code>.
+ * 
+ * <p>
+ * See {@link AbstractIntegrationFilter} for further information.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class JbossIntegrationFilter extends AbstractIntegrationFilter {
+    //~ Methods ================================================================
+
+    public Object extractFromContainer(ServletRequest request) {
+        Subject subject = null;
+
+        try {
+            InitialContext ic = new InitialContext();
+            subject = (Subject) ic.lookup("java:comp/env/security/subject");
+        } catch (NamingException ne) {
+            if (super.logger.isDebugEnabled()) {
+                super.logger.warn("Lookup on Subject failed "
+                                  + ne.getLocalizedMessage());
+            }
+        }
+
+        if ((subject != null) && (subject.getPrincipals() != null)) {
+            Iterator principals = subject.getPrincipals().iterator();
+
+            while (principals.hasNext()) {
+                Principal p = (Principal) principals.next();
+
+                if (super.logger.isDebugEnabled()) {
+                    super.logger.debug("Found Principal in container ("
+                                       + p.getClass().getName() + ") : "
+                                       + p.getName());
+                }
+
+                if (p instanceof Authentication) {
+                    return p;
+                }
+            }
+        }
+
+        return null;
+    }
+}

+ 7 - 0
adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/package.html

@@ -0,0 +1,7 @@
+<html>
+<body>
+Adapter to JBoss.
+<p>
+</body>
+</html>
+

+ 147 - 0
adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/JettyAcegiUserRealm.java

@@ -0,0 +1,147 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.jetty;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.mortbay.http.HttpRequest;
+import org.mortbay.http.UserPrincipal;
+import org.mortbay.http.UserRealm;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.Map;
+
+
+/**
+ * Adapter to enable Jetty to authenticate via the Acegi Security System for
+ * Spring.
+ * 
+ * <p>
+ * Returns a {@link JettyAcegiUserToken} to Jetty's authentication system,
+ * which is subsequently available via
+ * <code>HttpServletRequest.getUserPrincipal()</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public final class JettyAcegiUserRealm implements UserRealm {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(JettyAcegiUserRealm.class);
+
+    //~ Instance fields ========================================================
+
+    private AuthenticationManager authenticationManager;
+    private String key;
+    private String realm;
+
+    //~ Constructors ===========================================================
+
+    /**
+     * Construct a <code>SpringUserRealm</code>.
+     *
+     * @param realm the name of the authentication realm (within Jetty)
+     * @param providerKey a password to sign all authentication objects
+     * @param appContextLocation the classpath location of the bean context XML
+     *        file
+     *
+     * @throws IllegalArgumentException DOCUMENT ME!
+     */
+    public JettyAcegiUserRealm(String realm, String providerKey,
+        String appContextLocation) {
+        this.realm = realm;
+        this.key = providerKey;
+
+        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(appContextLocation);
+        Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
+
+        if (beans.size() == 0) {
+            throw new IllegalArgumentException(
+                "Bean context must contain at least one bean of type AuthenticationManager");
+        }
+
+        String beanName = (String) beans.keySet().iterator().next();
+        authenticationManager = (AuthenticationManager) beans.get(beanName);
+    }
+
+    private JettyAcegiUserRealm() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public AuthenticationManager getAuthenticationManager() {
+        return authenticationManager;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return the name of the realm as defined when
+     *         <code>SpringUserRealm</code> was created
+     */
+    public String getName() {
+        return this.realm;
+    }
+
+    public UserPrincipal authenticate(String username, Object password,
+        HttpRequest httpRequest) {
+        if (username == null) {
+            return null;
+        }
+
+        if (password == null) {
+            password = "";
+        }
+
+        Authentication request = new UsernamePasswordAuthenticationToken(username
+                .toString(), password.toString());
+        Authentication response = null;
+
+        try {
+            response = authenticationManager.authenticate(request);
+        } catch (AuthenticationException failed) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication request for user: " + username
+                    + " failed: " + failed.toString());
+            }
+
+            return null;
+        }
+
+        return new JettyAcegiUserToken(this.key,
+            response.getPrincipal().toString(),
+            response.getCredentials().toString(), response.getAuthorities());
+    }
+
+    public void disassociate(UserPrincipal userPrincipal) {
+        // No action required
+    }
+
+    public void logout(UserPrincipal arg0) {
+        // Not supported
+    }
+
+    public UserPrincipal popRole(UserPrincipal userPrincipal) {
+        // Not supported
+        return userPrincipal;
+    }
+
+    public UserPrincipal pushRole(UserPrincipal userPrincipal, String role) {
+        // Not supported
+        return userPrincipal;
+    }
+}

+ 55 - 0
adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/JettyAcegiUserToken.java

@@ -0,0 +1,55 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.jetty;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.adapters.AbstractAdapterAuthenticationToken;
+
+import org.mortbay.http.UserPrincipal;
+
+
+/**
+ * A Jetty compatible {@link net.sf.acegisecurity.Authentication} object.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class JettyAcegiUserToken extends AbstractAdapterAuthenticationToken
+    implements UserPrincipal {
+    //~ Instance fields ========================================================
+
+    private String password;
+    private String username;
+
+    //~ Constructors ===========================================================
+
+    public JettyAcegiUserToken(String key, String username, String password,
+        GrantedAuthority[] authorities) {
+        super(key, authorities);
+        this.username = username;
+        this.password = password;
+    }
+
+    private JettyAcegiUserToken() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public Object getCredentials() {
+        return this.password;
+    }
+
+    public String getName() {
+        return this.username;
+    }
+
+    public Object getPrincipal() {
+        return this.username;
+    }
+}

+ 7 - 0
adapters/jetty/src/main/java/org/acegisecurity/adapters/jetty/package.html

@@ -0,0 +1,7 @@
+<html>
+<body>
+Adapter to Jetty web container.
+<p>
+</body>
+</html>
+

+ 163 - 0
adapters/resin/src/main/java/org/acegisecurity/adapters/resin/ResinAcegiAuthenticator.java

@@ -0,0 +1,163 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters.resin;
+
+import com.caucho.http.security.AbstractAuthenticator;
+
+import com.caucho.vfs.Path;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.support.FileSystemXmlApplicationContext;
+
+import java.io.File;
+
+import java.security.Principal;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Adapter to enable Resin to authenticate via the Acegi Security System for
+ * Spring.
+ * 
+ * <p>
+ * Returns a {@link PrincipalAcegiUserToken} to Resin's authentication system,
+ * which is subsequently available via
+ * <code>HttpServletRequest.getUserPrincipal()</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ResinAcegiAuthenticator extends AbstractAuthenticator {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(ResinAcegiAuthenticator.class);
+
+    //~ Instance fields ========================================================
+
+    private AuthenticationManager authenticationManager;
+    private Path appContextLocation;
+    private String key;
+
+    //~ Methods ================================================================
+
+    public void setAppContextLocation(Path appContextLocation) {
+        this.appContextLocation = appContextLocation;
+    }
+
+    public Path getAppContextLocation() {
+        return appContextLocation;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public boolean isUserInRole(HttpServletRequest request,
+        HttpServletResponse response, ServletContext application,
+        Principal principal, String role) {
+        if (!(principal instanceof PrincipalAcegiUserToken)) {
+            if (logger.isWarnEnabled()) {
+                logger.warn(
+                    "Expected passed principal to be of type PrincipalSpringUserToken but was "
+                    + principal.getClass().getName());
+            }
+
+            return false;
+        }
+
+        PrincipalAcegiUserToken test = (PrincipalAcegiUserToken) principal;
+
+        return test.isUserInRole(role);
+    }
+
+    public void init() throws ServletException {
+        super.init();
+
+        if (appContextLocation == null) {
+            throw new ServletException("appContextLocation must be defined");
+        }
+
+        if (key == null) {
+            throw new ServletException("key must be defined");
+        }
+
+        File xml = new File(appContextLocation.getPath());
+
+        if (!xml.exists()) {
+            throw new ServletException(
+                "appContextLocation does not seem to exist - try specifying WEB-INF/resin-springsecurity.xml");
+        }
+
+        FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext(xml
+                .getAbsolutePath());
+        Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
+
+        if (beans.size() == 0) {
+            throw new ServletException(
+                "Bean context must contain at least one bean of type AuthenticationManager");
+        }
+
+        String beanName = (String) beans.keySet().iterator().next();
+        authenticationManager = (AuthenticationManager) beans.get(beanName);
+        logger.info("ResinSpringAuthenticator Started");
+    }
+
+    protected Principal loginImpl(String username, String credentials) {
+        if (username == null) {
+            return null;
+        }
+
+        if (credentials == null) {
+            credentials = "";
+        }
+
+        Authentication request = new UsernamePasswordAuthenticationToken(username,
+                credentials);
+        Authentication response = null;
+
+        try {
+            response = authenticationManager.authenticate(request);
+        } catch (AuthenticationException failed) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication request for user: " + username
+                    + " failed: " + failed.toString());
+            }
+
+            return null;
+        }
+
+        return new PrincipalAcegiUserToken(this.key,
+            response.getPrincipal().toString(),
+            response.getCredentials().toString(), response.getAuthorities());
+    }
+
+    protected Principal loginImpl(HttpServletRequest request,
+        HttpServletResponse response, ServletContext application,
+        String userName, String password) throws ServletException {
+        return loginImpl(userName, password);
+    }
+}

+ 7 - 0
adapters/resin/src/main/java/org/acegisecurity/adapters/resin/package.html

@@ -0,0 +1,7 @@
+<html>
+<body>
+Adapter to Resin web container.
+<p>
+</body>
+</html>
+

+ 464 - 0
build.xml

@@ -0,0 +1,464 @@
+<?xml version="1.0"?>
+
+<!--
+    Build file for the Acegi Security System for Spring.
+
+    $Id$
+-->
+
+<project name="acegi-security-core" default="usage" basedir=".">
+
+	<property file="build.properties"/>
+	<property file="project.properties"/>
+
+	<path id="qa-portalpath">
+		<fileset dir="${lib.dir}">
+			<include name="**/*.jar"/>
+		</fileset>
+	</path>
+
+    <path id="jalopy-classpath">
+        <fileset dir="${lib.dir}/jalopy">
+            <include name="**/*.jar"/>
+        </fileset>
+    </path>
+
+	<target name="usage">
+		<echo message=""/>
+		<echo message="${name} build file"/>
+		<echo message="------------------------------------------------------"/>
+		<echo message=""/>
+		<echo message="Among the available targets are:"/>
+		<echo message=""/>
+		<echo message="build    --> build all; don't create JARs"/>
+		<echo message="alljars  --> create all JAR files"/>
+		<echo message="format   --> format/tidy all source code"/>
+		<echo message="tests    --> run tests"/>
+		<echo message="release  --> build a distribution ZIP file"/>
+		<echo message=""/>
+		<echo message="To build or test, your lib directory needs to be populated"/>
+	</target>
+
+	<target name="clean" description="Clean all output dirs (dist, javadocs, classes, test-classes, etc.)">
+
+		<delete dir="${dist.dir}"/>
+		<delete dir="${javadocs.dir}"/>
+
+		<delete dir="${target.classes.dir}"/>
+		<delete dir="${target.junit.reports.dir}"/>
+		<delete dir="${target.otherclasses.dir}"/>
+		<delete dir="${target.release.dir}"/>
+		<delete dir="${target.testclasses.dir}"/>
+
+	</target>
+
+
+	<!--
+		Compile the main source tree.
+	-->
+	<target name="build"
+		depends=""
+		description="Compile main source tree java files into class files (no-jarring)">
+
+		<mkdir dir="${target.classes.dir}"/>
+
+		<javac destdir="${target.classes.dir}" target="1.3" debug="${debug}"
+			deprecation="false" optimize="false" failonerror="true">
+			<src path="${src.dir}"/>
+			<classpath refid="qa-portalpath"/>
+		</javac>
+
+		<copy todir="${target.classes.dir}" preservelastmodified="true">
+			<fileset dir="${src.dir}">
+				<include name="**/*.xml"/>
+			</fileset>
+		</copy>
+
+	</target>
+
+
+    <target name="format" description="Formats all project source code">
+	    <taskdef name="jalopy" classname="de.hunsicker.jalopy.plugin.ant.AntPlugin">
+            <classpath refid="jalopy-classpath"/>
+        </taskdef>
+
+        <jalopy fileformat="unix"
+            convention="${jalopy.xml}"
+            history="file"
+            historymethod="adler32"
+            loglevel="error"
+            threads="2"
+            classpathref="qa-portalpath">
+            <fileset dir="${src.dir}">
+                <include name="**/*.java"/>
+            </fileset>
+            <fileset dir="${test.dir}">
+                <include name="**/*.java"/>
+            </fileset>
+        </jalopy>
+
+		<ant inheritall="no" antfile="build.xml" dir="samples/contacts" target="format"/>
+		<ant inheritall="no" antfile="build.xml" dir="samples/attributes" target="format"/>
+		<ant inheritall="no" antfile="build.xml" dir="integration-test" target="format"/>
+    </target>
+
+	<target name="initdist" description="Initialize the distribution directory">
+		<mkdir dir="${dist.dir}"/>
+	</target>
+
+
+	<target name="fulljar" depends="build,initdist" description="Create JAR file with all Acegi Security System for Spring classes">
+		<delete file="${dist.dir}/${acegi-security.jar}"/>
+		
+		<!-- An all classes JAR file, which is provided for compiling web apps
+		only (at runtime all classes should be from web container) -->
+		<jar jarfile="${dist.dir}/${acegi-security.jar}">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/**"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+		
+		<!-- The class that has catalina.jar dependencies and thus belongs in
+		Catalina's "Catalina" classloader ($CATALINA_HOME/server/lib directory) -->
+		<jar jarfile="${dist.dir}/acegi-security-catalina-server.jar">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/adapters/catalina/CatalinaAcegiUserRealm.class"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+		
+		<!-- All Acegi Security System for Spring classes that belong in Catalina's
+		"Common" classloader ($CATALINA_HOME/common/lib directory) -->
+		<jar jarfile="${dist.dir}/acegi-security-catalina-common.jar">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/*"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+				<include name="net/sf/acegisecurity/providers/**"/>
+				<include name="net/sf/acegisecurity/runas/**"/>
+				<include name="net/sf/acegisecurity/vote/**"/>
+				<include name="net/sf/acegisecurity/adapters/*"/>
+				<include name="net/sf/acegisecurity/adapters/catalina/*"/>
+				<exclude name="net/sf/acegisecurity/adapters/catalina/CatalinaAcegiUserRealm.class"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+		
+		<!-- All Acegi Security System for Spring classes that belong in Jetty's
+		"ext" directory -->
+		<jar jarfile="${dist.dir}/acegi-security-jetty-ext.jar">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/*"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+				<include name="net/sf/acegisecurity/providers/**"/>
+				<include name="net/sf/acegisecurity/runas/**"/>
+				<include name="net/sf/acegisecurity/vote/**"/>
+				<include name="net/sf/acegisecurity/adapters/*"/>
+				<include name="net/sf/acegisecurity/adapters/jetty/*"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+		
+		<!-- All Acegi Security System for Spring classes that belong in JBoss'
+		"server/your_config/lib" directory -->
+		<jar jarfile="${dist.dir}/acegi-security-jboss-lib.jar">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/*"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+				<include name="net/sf/acegisecurity/providers/**"/>
+				<include name="net/sf/acegisecurity/runas/**"/>
+				<include name="net/sf/acegisecurity/vote/**"/>
+				<include name="net/sf/acegisecurity/adapters/*"/>
+				<include name="net/sf/acegisecurity/adapters/jboss/*"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+		
+		<!-- All Acegi Security System for Spring classes that belong in
+		Resin's "lib" directory -->
+		<jar jarfile="${dist.dir}/acegi-security-resin-lib.jar">
+			<fileset dir="${target.classes.dir}">
+				<include name="net/sf/acegisecurity/*"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+				<include name="net/sf/acegisecurity/providers/**"/>
+				<include name="net/sf/acegisecurity/runas/**"/>
+				<include name="net/sf/acegisecurity/vote/**"/>
+				<include name="net/sf/acegisecurity/adapters/*"/>
+				<include name="net/sf/acegisecurity/adapters/resin/*"/>
+			</fileset>
+			<manifest>
+				<attribute name="Acegi-Security-System-version" value="${acegi-security-version}"/>
+			</manifest>
+		</jar>
+
+	</target>
+
+	<target name="srczip" depends="initdist" description="Create source ZIP (containing all Java sources)">
+		<delete file="${dist.dir}/${acegi-security-src.zip}"/>
+
+		<zip zipfile="${dist.dir}/${acegi-security-src.zip}">
+			<fileset dir="${src.dir}">
+				<include name="net/sf/acegisecurity/**"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+			</fileset>
+		</zip>
+	</target>
+
+
+	<target name="alljars" depends="fulljar,srczip" description="Create all JAR files"/>
+
+	<target name="javadoc" description="Generate Javadocs.">
+		<mkdir dir="${javadocs.dir}"/>
+
+		<javadoc sourcepath="src" destdir="${javadocs.dir}" windowtitle="Acegi Security System for Spring"
+			defaultexcludes="yes"	author="true" version="true" use="true">
+			<doctitle><![CDATA[<h1>Acegi Security System for Spring</h1>]]></doctitle>
+			<bottom><![CDATA[<A HREF="http://acegisecurity.sourceforge.net">Acegi Security System for Spring Project]]></bottom>
+			<classpath refid="qa-portalpath"/>
+			<packageset dir="${src.dir}">
+				<include name="net/sf/acegisecurity/**"/>
+				<include name="net/sf/acegisecurity/context/**"/>
+			</packageset>
+		</javadoc>
+	</target>
+
+	<target name="release" depends="clean,alljars,format,tests,javadoc,refdoc" description="Generate release zip file">
+		<ant inheritall="no" antfile="build.xml" dir="samples/contacts" target="release"/>
+		<ant inheritall="no" antfile="build.xml" dir="samples/attributes" target="release"/>
+
+		<delete dir="${target.release.dir}"/>
+		<mkdir dir="${target.release.dir}"/>
+
+		<fileset id="main" dir=".">
+			<include name="dist/*.jar"/>
+			<include name="docs/**"/>
+			<exclude name="docs/reference/lib/**"/>
+			<include name="extractor/*"/>
+			<include name="extractor/source/*"/>
+			<include name="integration-test/**"/>
+			<exclude name="integration-test/lib/**"/>
+			<exclude name="integration-test/build.properties"/>
+			<exclude name="integration-test/classes/**"/>
+			<exclude name="integration-test/containers/**"/>
+			<exclude name="integration-test/reports/**"/>
+			<exclude name="integration-test/temporary/**"/>
+			<include name="samples/**"/>
+			<exclude name="samples/contacts/classes/**"/>
+			<exclude name="samples/contacts/build.properties"/>
+			<exclude name="samples/attributes/classes/**"/>
+			<exclude name="samples/attributes/reports/**"/>
+			<exclude name="samples/attributes/generated/**"/>
+			<include name="src/**"/>
+			<include name="test/**"/>
+			<include name="hsqldb/**"/>
+			<include name="*.txt"/>
+			<include name="*.xml"/>
+			<exclude name="project.properties"/>
+		</fileset>
+
+		<zip zipfile="${target.release.dir}/${release.zip}">
+			<zipfileset refid="main" prefix="${release.path}"/>
+		</zip>
+
+		<zip zipfile="${target.release.dir}/${release-with-dependencies.zip}">
+			<zipfileset refid="main" prefix="${release.path}"/>
+			<zipfileset dir="." prefix="${release.path}">
+				<include name="lib/**"/>
+				<include name="integration-test/lib/**"/>
+			</zipfileset>
+		</zip>
+
+	</target>
+
+ 	<!--
+		Compile test cases
+	-->
+	<target name="buildtests" depends="" description="Compile test source tree Java files into class files">
+
+		<mkdir dir="${target.testclasses.dir}"/>
+
+		<javac destdir="${target.testclasses.dir}" target="1.3" debug="${debug}"
+			deprecation="false" optimize="false" failonerror="true">
+			<src path="${test.dir}"/>
+			<classpath refid="qa-portalpath"/>
+			<classpath location="${target.classes.dir}"/>
+		</javac>
+
+		<!-- Pick up logging config from test directory -->
+		<copy todir="${target.testclasses.dir}" preservelastmodified="true">
+			<fileset dir="${test.dir}">
+				<include name="**/*.properties"/>
+			</fileset>
+		</copy>
+
+	</target>
+
+
+	<!--
+		Run tests. 
+	-->
+	<target name="tests" depends="buildtests" description="Run tests.">
+
+		<property name="reports.dir" value="${target.junit.reports.dir}"/>
+
+		<delete dir="${reports.dir}"/>
+		<mkdir dir="${reports.dir}"/>
+
+		<junit printsummary="yes" haltonfailure="yes">
+
+			<!-- Must go first to ensure any jndi.properties files etc take precedence  -->
+			<classpath location="${target.testclasses.dir}"/>
+			<classpath location="${target.classes.dir}"/>
+
+			<!-- Need files loaded as resources -->
+			<classpath location="${test.dir}"/>
+
+			<classpath refid="qa-portalpath"/>
+
+			<formatter type="plain"/>
+
+			<batchtest fork="yes" todir="${reports.dir}">
+				<fileset dir="${target.testclasses.dir}" includes="${test.includes}" excludes="${test.excludes}"/>
+			</batchtest>
+
+		</junit>
+
+	</target>
+
+	<target name="docclean" description="Delete temporary and distribution directories for docs">
+
+		<delete quiet="true" dir="${basedir}/${dist.ref.dir}/pdf"/>
+		<delete quiet="true" dir="${basedir}/${dist.ref.dir}/html_single"/>
+		<delete quiet="true" dir="${basedir}/${dist.ref.dir}/html"/>
+
+	</target>
+
+
+	<target name="preparedocs" description="Extra preparation for the documentation">
+		<!-- For now, no dynamic inclusion of the DTD since it looks ugly because of
+		     all the extra newlines the replace is mysteriously adding.
+		     I'll figure something out for that later on
+		<delete file="${basedir}/${doc.ref.dir}/src/dtd.xml"/>
+		<loadfile
+			property="doc.beansdtd"
+				srcFile="${src.dir}/org/springframework/beans/factory/xml/spring-beans.dtd"/>
+		<copy
+			file="${basedir}/${doc.ref.dir}/src/dtd-template.xml"
+			tofile="${basedir}/${doc.ref.dir}/src/dtd.xml"/>
+		<replace
+			file="${basedir}/${doc.ref.dir}/src/dtd.xml"
+			token="@dtd-include@"
+			value="${doc.beansdtd}">
+		</replace>
+		<replace
+			file="${basedir}/${doc.ref.dir}/src/dtd.xml"
+			token="\\n"
+			value=""/>
+		-->
+	</target>
+
+
+	<target name="docpdf" depends="preparedocs" description="Compile reference documentation to pdf">
+
+		<mkdir dir="${basedir}/${dist.ref.dir}/pdf/images"/>
+
+		<copy todir="${basedir}/${dist.ref.dir}/pdf/images">
+			<fileset dir="${basedir}/${doc.ref.dir}/src/images">
+				<include name="*.gif"/>
+				<include name="*.svg"/>
+				<include name="*.jpg"/>
+			</fileset>
+		</copy>
+
+		<java classname="com.icl.saxon.StyleSheet" fork="true" dir="${doc.ref.dir}">
+			<classpath>
+				<fileset dir="${basedir}/${doc.ref.dir}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+			</classpath>
+			<arg value="-o"/>
+			<arg value="${basedir}/${dist.ref.dir}/pdf/docbook_fop.tmp"/>
+			<arg value="${basedir}/${doc.ref.dir}/src/index.xml"/>
+			<arg value="${basedir}/${doc.ref.dir}/styles/fopdf.xsl"/>
+		</java>
+
+		<java classname="org.apache.fop.apps.Fop" fork="true" dir="${doc.ref.dir}">
+			<classpath>
+				<fileset dir="${basedir}/${doc.ref.dir}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+			</classpath>
+			<arg value="${basedir}/${dist.ref.dir}/pdf/docbook_fop.tmp"/>
+			<arg value="${basedir}/${dist.ref.dir}/pdf/acegi-security-reference.pdf"/>
+		</java>
+
+		<delete file="${dist.ref.dir}/pdf/docbook_fop.tmp"/>
+
+	</target>
+
+
+	<target name="dochtml" depends="preparedocs" description="Compile reference documentation to chunked html">
+
+		<mkdir dir="${dist.ref.dir}/html/images"/>
+
+		<copy todir="${basedir}/${dist.ref.dir}/html/images">
+			<fileset dir="${basedir}/${doc.ref.dir}/src/images">
+				<include name="*.gif"/>
+				<include name="*.svg"/>
+				<include name="*.jpg"/>
+			</fileset>
+		</copy>
+
+		<java classname="com.icl.saxon.StyleSheet" fork="true" dir="${dist.ref.dir}/html/">
+			<classpath>
+				<fileset dir="${basedir}/${doc.ref.dir}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+			</classpath>
+			<arg value="${basedir}/${doc.ref.dir}/src/index.xml"/>
+			<arg value="${basedir}/${doc.ref.dir}/styles/html_chunk.xsl"/>
+		</java>
+
+	</target>
+
+
+	<target name="dochtmlsingle" description="Compile reference documentation to single html">
+
+		<mkdir dir="${dist.ref.dir}/html_single/images"/>
+
+		<copy todir="${basedir}/${dist.ref.dir}/html_single/images">
+			<fileset dir="${basedir}/${doc.ref.dir}/src/images">
+				<include name="*.gif"/>
+				<include name="*.svg"/>
+				<include name="*.jpg"/>
+			</fileset>
+		</copy>
+
+		<java classname="com.icl.saxon.StyleSheet" fork="true" dir="${doc.ref.dir}">
+			<classpath>
+				<fileset dir="${basedir}/${doc.dir}/reference/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+			</classpath>
+			<arg value="-o"/>
+			<arg value="${basedir}/${dist.ref.dir}/html_single/index.html"/>
+			<arg value="${basedir}/${doc.ref.dir}/src/index.xml"/>
+			<arg value="${basedir}/${doc.ref.dir}/styles/html.xsl"/>
+		</java>
+
+	</target>
+
+
+	<target name="refdoc" depends="dochtml,dochtmlsingle,docpdf" description="Generate and copy reference documentation"/>
+
+</project>

+ 39 - 0
changelog.txt

@@ -0,0 +1,39 @@
+Changes in version 0.3 (2004-03-16)
+-----------------------------------
+
+* Added "in container" unit test system for container adapters and sample app
+* Added library extractor tool to reduce the "with deps" ZIP release sizes
+* Added unit test to the attributes sample
+* Added Jalopy source formatting
+* Modified all files to use net.sf.acegisecurity namespace
+* Renamed springsecurity.xml to acegisecurity.xml for consistency
+* Reduced length of ZIP and JAR filenames
+* Clarified licenses and sources for all included libraries
+* Updated documentation to reflect new file and package names
+* Setup Sourceforge.net project and added to CVS etc
+
+Changes in version 0.2 (2004-03-10)
+-----------------------------------
+
+* Added Commons Attributes support and sample (thanks to Cameron Braid)
+* Added JBoss container adapter
+* Added Resin container adapter
+* Added JDBC DAO authentication provider
+* Added several filter implementations for container adapter integration
+* Added SecurityInterceptor startup time validation of ConfigAttributes
+* Added more unit tests
+* Refactored ConfigAttribute to interface and added concrete implementation
+* Enhanced diagnostics information provided by sample application debug.jsp
+* Modified sample application for wider container portability (Resin, JBoss)
+* Fixed switch block in voting decision manager implementations
+* Removed Spring MVC interceptor for container adapter integration
+* Documentation improvements
+
+Changes in version 0.1 (2004-03-03)
+-----------------------------------
+
+* Initial public release
+
+
+
+$Id$

+ 15 - 0
contributors.txt

@@ -0,0 +1,15 @@
+===============================================================================
+              ACEGI SECURITY SYSTEM FOR SPRING - CONTRIBUTORS
+===============================================================================
+
+The following people and organisations are gratefully acknowledged for their
+contributions to the Acegi Security System for Spring project:
+
+* Ben Alex of Acegi Technology Pty Limited (http://www.acegi.com.au) donated
+  the original code and currently maintains the project.
+
+* Cameron Braid added support for configuration using Commons Attributes.
+
+                                 
+
+$Id$

+ 55 - 0
core/src/main/java/org/acegisecurity/AccessDecisionManager.java

@@ -0,0 +1,55 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+
+/**
+ * Makes a final access control (authorization) decision.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AccessDecisionManager {
+    //~ Methods ================================================================
+
+    /**
+     * Resolves an access control decision for the passed parameters.
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @throws AccessDeniedException if access is denied
+     */
+    public void decide(Authentication authentication,
+                       MethodInvocation invocation,
+                       ConfigAttributeDefinition config)
+                throws AccessDeniedException;
+
+    /**
+     * Indicates whether this <code>AccessDecisionManager</code> is able to
+     * process authorization requests presented with the passed
+     * <code>ConfigAttribute</code>.
+     * 
+     * <p>
+     * This allows the <code>SecurityInterceptor</code> to check every
+     * configuration attribute can be consumed by the configured
+     * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
+     * </p>
+     *
+     * @param attribute a configuration attribute that has been configured
+     *        against the <code>SecurityInterceptor</code>
+     *
+     * @return true if this <code>AccessDecisionManager</code> can support the
+     *         passed configuration attribute
+     */
+    public boolean supports(ConfigAttribute attribute);
+}

+ 40 - 0
core/src/main/java/org/acegisecurity/AccessDeniedException.java

@@ -0,0 +1,40 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an {@link Authentication} object does not hold a required
+ * authority.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AccessDeniedException extends AcegiSecurityException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs an <code>AccessDeniedException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message
+     */
+    public AccessDeniedException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs an <code>AccessDeniedException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public AccessDeniedException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 48 - 0
core/src/main/java/org/acegisecurity/AcegiSecurityException.java

@@ -0,0 +1,48 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.springframework.core.NestedRuntimeException;
+
+
+/**
+ * Abstract superclass for all exceptions thrown in the security package and
+ * subpackages.
+ * 
+ * <p>
+ * Note that this is a runtime (unchecked) exception. Security exceptions are
+ * usually fatal; there is no reason for them to be checked.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AcegiSecurityException extends NestedRuntimeException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs an <code>AcegiSecurityException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t the root cause
+     */
+    public AcegiSecurityException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+    /**
+     * Constructs an <code>AcegiSecurityException</code> with the specified
+     * message and no root cause.
+     *
+     * @param msg the detail message
+     */
+    public AcegiSecurityException(String msg) {
+        super(msg);
+    }
+}

+ 69 - 0
core/src/main/java/org/acegisecurity/Authentication.java

@@ -0,0 +1,69 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Represents an authentication request.
+ * 
+ * <p>
+ * An <code>Authentication</code> object is not considered authenticated until
+ * it is processed by an {@link AuthenticationManager}.
+ * </p>
+ * 
+ * <p>
+ * Stored in a request {@link net.sf.acegisecurity.context.SecureContext}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface Authentication {
+    //~ Methods ================================================================
+
+    public void setAuthenticated(boolean isAuthenticated);
+
+    /**
+     * Indicates whether or not authentication was attempted by the {@link
+     * net.sf.acegisecurity.SecurityInterceptor}. Note that  classes should
+     * not rely on this value as being valid unless it has been set by a
+     * trusted <code>SecurityInterceptor</code>.
+     *
+     * @return true if authenticated by the <code>SecurityInterceptor</code>
+     */
+    public boolean isAuthenticated();
+
+    /**
+     * Set by an <code>AuthenticationManager</code> to indicate the authorities
+     * that the principal has been  granted. Note that classes should not rely
+     * on this value as being valid  unless it has been set by a trusted
+     * <code>AuthenticationManager</code>.
+     *
+     * @return the authorities granted to the principal, or <code>null</code>
+     *         if authentication has not been completed
+     */
+    public GrantedAuthority[] getAuthorities();
+
+    /**
+     * The credentials that prove the principal is correct. This is usually a
+     * password, but could be anything relevant to the
+     * <code>AuthenticationManager</code>. Callers are expected to populate
+     * the credentials.
+     *
+     * @return the credentials that prove the identity of the
+     *         <code>Principal</code>
+     */
+    public Object getCredentials();
+
+    /**
+     * The identity of the principal being authenticated. This is usually a
+     * username. Callers are expected to populate the principal.
+     *
+     * @return the <code>Principal</code> being authenticated
+     */
+    public Object getPrincipal();
+}

+ 42 - 0
core/src/main/java/org/acegisecurity/AuthenticationCredentialsNotFoundException.java

@@ -0,0 +1,42 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an authentication request is rejected because there is no {@link
+ * Authentication} object in the  {@link
+ * net.sf.acegisecurity.context.SecureContext}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationCredentialsNotFoundException
+    extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs an <code>AuthenticationCredentialsNotFoundException</code>
+     * with the specified message.
+     *
+     * @param msg the detail message
+     */
+    public AuthenticationCredentialsNotFoundException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs an <code>AuthenticationCredentialsNotFoundException</code>
+     * with the specified message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public AuthenticationCredentialsNotFoundException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 41 - 0
core/src/main/java/org/acegisecurity/AuthenticationException.java

@@ -0,0 +1,41 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Abstract superclass for all exceptions related to the {@link
+ * AuthenticationManager} being unable to authenticate an {@link
+ * Authentication} object.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AuthenticationException extends AcegiSecurityException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs an <code>AuthenticationException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t the root cause
+     */
+    public AuthenticationException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+    /**
+     * Constructs an <code>AuthenticationException</code> with the specified
+     * message and no root cause.
+     *
+     * @param msg the detail message
+     */
+    public AuthenticationException(String msg) {
+        super(msg);
+    }
+}

+ 61 - 0
core/src/main/java/org/acegisecurity/AuthenticationManager.java

@@ -0,0 +1,61 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Processes an {@link Authentication} request.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AuthenticationManager {
+    //~ Methods ================================================================
+
+    /**
+     * Attempts to authenticate the passed {@link Authentication} object,
+     * returning a fully populated <code>Authentication</code> object
+     * (including granted authorities) if successful.
+     * 
+     * <p>
+     * An <code>AuthenticationManager</code> must honour the following contract
+     * concerning exceptions:
+     * </p>
+     * 
+     * <p>
+     * A {@link DisabledException} must be thrown if an account is disabled and
+     * the <code>AuthenticationManager</code> can test for this state.
+     * </p>
+     * 
+     * <p>
+     * A {@link LockedException} must be thrown if an account is locked and the
+     * <code>AuthenticationManager</code> can test for account locking.
+     * </p>
+     * 
+     * <p>
+     * A {@link BadCredentialsException} must be thrown if incorrect
+     * credentials are presented. Whilst the above exceptions are optional, an
+     * <code>AuthenticationManager</code> must <B>always</B> test credentials.
+     * </p>
+     * 
+     * <p>
+     * Exceptions should be tested for and if applicable thrown in the order
+     * expressed above (ie if an account is disabled or locked, the
+     * authentication request is immediately rejected and the credentials
+     * testing process is not performed). This prevents credentials being
+     * tested against  disabled or locked accounts.
+     * </p>
+     *
+     * @param authentication the authentication request object
+     *
+     * @return a fully authenticated object including credentials
+     *
+     * @throws AuthenticationException if authentication fails
+     */
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException;
+}

+ 44 - 0
core/src/main/java/org/acegisecurity/AuthenticationServiceException.java

@@ -0,0 +1,44 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an authentication request could not be processed due to a system
+ * problem.
+ * 
+ * <p>
+ * This might be thrown if a backend authentication repository is  unavailable.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationServiceException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs an <code>AuthenticationServiceException</code> with the
+     * specified message.
+     *
+     * @param msg the detail message
+     */
+    public AuthenticationServiceException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs an <code>AuthenticationServiceException</code> with the
+     * specified message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public AuthenticationServiceException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 41 - 0
core/src/main/java/org/acegisecurity/BadCredentialsException.java

@@ -0,0 +1,41 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an authentication request is rejected because the credentials are
+ * invalid. For this exception to be thrown, it means the account is neither
+ * locked nor disabled.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class BadCredentialsException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>BadCredentialsException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message
+     */
+    public BadCredentialsException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>BadCredentialsException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public BadCredentialsException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 53 - 0
core/src/main/java/org/acegisecurity/ConfigAttribute.java

@@ -0,0 +1,53 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Stores a security system related configuration attribute.
+ * 
+ * <p>
+ * When the {@link SecurityInterceptor} is setup, a list of configuration
+ * attributes is defined for secure method patterns. These configuration
+ * attributes have special meaning to a {@link RunAsManager}, {@link
+ * AccessDecisionManager} or <code>AccessDecisionManager</code> delegate.
+ * </p>
+ * 
+ * <P>
+ * Stored at runtime with other <code>ConfigAttribute</code>s for the same
+ * method within a {@link ConfigAttributeDefinition}.
+ * </p>
+ *
+ * @author <A HREF="mailto:ben.alex@fremerx.com">Ben Alex</A>
+ * @version $Id$
+ */
+public interface ConfigAttribute {
+    //~ Methods ================================================================
+
+    /**
+     * If the <code>ConfigAttribute</code> can be represented as a
+     * <code>String</code> and that <code>String</code> is sufficient in
+     * precision to be relied upon as a configuration parameter by a {@link
+     * RunAsManager}, {@link AccessDecisionManager} or
+     * <code>AccessDecisionManager</code> delegate, this method should  return
+     * such a <code>String</code>.
+     * 
+     * <p>
+     * If the <code>ConfigAttribute</code> cannot be expressed with sufficient
+     * precision as a <code>String</code>,  <code>null</code> should be
+     * returned. Returning <code>null</code> will require an relying classes
+     * to specifically support the  <code>ConfigAttribute</code>
+     * implementation, so returning  <code>null</code> should be avoided
+     * unless actually  required.
+     * </p>
+     *
+     * @return a representation of the configuration attribute (or
+     *         <code>null</code> if the configuration attribute cannot be
+     *         expressed as a <code>String</code> with sufficient precision).
+     */
+    public String getAttribute();
+}

+ 62 - 0
core/src/main/java/org/acegisecurity/ConfigAttributeDefinition.java

@@ -0,0 +1,62 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * Holds a group of {@link ConfigAttribute}s that are associated with a given
+ * method.
+ * 
+ * <p>
+ * All the <code>ConfigAttributeDefinition</code>s associated with a given
+ * <code>SecurityInterceptor</code> are stored in a  {@link
+ * MethodDefinitionMap}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ConfigAttributeDefinition {
+    //~ Instance fields ========================================================
+
+    private Set configAttributes = new HashSet();
+
+    //~ Constructors ===========================================================
+
+    public ConfigAttributeDefinition() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return all the configuration attributes related to the method.
+     */
+    public Iterator getConfigAttributes() {
+        return this.configAttributes.iterator();
+    }
+
+    /**
+     * Adds a <code>ConfigAttribute</code> that is related to the method.
+     *
+     * @param newConfigAttribute DOCUMENT ME!
+     */
+    public void addConfigAttribute(ConfigAttribute newConfigAttribute) {
+        this.configAttributes.add(newConfigAttribute);
+    }
+
+    public String toString() {
+        return this.configAttributes.toString();
+    }
+}

+ 39 - 0
core/src/main/java/org/acegisecurity/ConfigAttributeEditor.java

@@ -0,0 +1,39 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+
+
+/**
+ * A property editor that can create a populated  {@link
+ * ConfigAttributeDefinition} from a comma separated list of values.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ConfigAttributeEditor extends PropertyEditorSupport {
+    //~ Methods ================================================================
+
+    public void setAsText(String s) throws IllegalArgumentException {
+        if ((s == null) || "".equals(s)) {
+            setValue(null);
+        } else {
+            String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
+            ConfigAttributeDefinition configDefinition = new ConfigAttributeDefinition();
+
+            for (int i = 0; i < tokens.length; i++) {
+                configDefinition.addConfigAttribute(new SecurityConfig(tokens[i]));
+            }
+
+            setValue(configDefinition);
+        }
+    }
+}

+ 40 - 0
core/src/main/java/org/acegisecurity/DisabledException.java

@@ -0,0 +1,40 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an authentication request is rejected because the account is
+ * disabled. Makes no assertion as to whether or not the credentials were
+ * valid.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class DisabledException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>DisabledException</code> with the specified message.
+     *
+     * @param msg the detail message
+     */
+    public DisabledException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>DisabledException</code> with the specified message
+     * and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public DisabledException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 46 - 0
core/src/main/java/org/acegisecurity/GrantedAuthority.java

@@ -0,0 +1,46 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Represents an authority granted to an {@link Authentication} object.
+ * 
+ * <p>
+ * A <code>GrantedAuthority</code> must either represent itself as a
+ * <code>String</code> or be specifically supported by an  {@link
+ * AccessDecisionManager}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface GrantedAuthority {
+    //~ Methods ================================================================
+
+    /**
+     * If the <code>GrantedAuthority</code> can be represented as a
+     * <code>String</code> and that <code>String</code> is sufficient in
+     * precision to be relied upon for an access control decision by an {@link
+     * AccessDecisionManager} (or delegate), this method should return such a
+     * <code>String</code>.
+     * 
+     * <p>
+     * If the <code>GrantedAuthority</code> cannot be expressed with sufficient
+     * precision as a <code>String</code>,  <code>null</code> should be
+     * returned. Returning <code>null</code> will require an
+     * <code>AccessDecisionManager</code> (or delegate) to  specifically
+     * support the <code>GrantedAuthority</code> implementation,  so returning
+     * <code>null</code> should be avoided unless actually  required.
+     * </p>
+     *
+     * @return a representation of the granted authority (or <code>null</code>
+     *         if the granted authority cannot be expressed as a
+     *         <code>String</code> with sufficient precision).
+     */
+    public String getAuthority();
+}

+ 64 - 0
core/src/main/java/org/acegisecurity/GrantedAuthorityImpl.java

@@ -0,0 +1,64 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Basic concrete implementation of a {@link GrantedAuthority}.
+ * 
+ * <p>
+ * Stores a <code>String</code> representation of an authority granted to  the
+ * {@link Authentication} object.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class GrantedAuthorityImpl implements GrantedAuthority {
+    //~ Instance fields ========================================================
+
+    private String role;
+
+    //~ Constructors ===========================================================
+
+    public GrantedAuthorityImpl(String role) {
+        super();
+        this.role = role;
+    }
+
+    private GrantedAuthorityImpl() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public String getAuthority() {
+        return this.role;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj instanceof String) {
+            return obj.equals(this.role);
+        }
+
+        if (obj instanceof GrantedAuthority) {
+            GrantedAuthority attr = (GrantedAuthority) obj;
+
+            return this.role.equals(attr.getAuthority());
+        }
+
+        return false;
+    }
+
+    public int hashCode() {
+        return this.role.hashCode();
+    }
+
+    public String toString() {
+        return this.role;
+    }
+}

+ 39 - 0
core/src/main/java/org/acegisecurity/LockedException.java

@@ -0,0 +1,39 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Thrown if an authentication request is rejected because the account is
+ * locked. Makes no assertion as to whether or not the credentials were valid.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class LockedException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>LockedException</code> with the specified message.
+     *
+     * @param msg the detail message.
+     */
+    public LockedException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>LockedException</code> with the specified message and
+     * root cause.
+     *
+     * @param msg the detail message.
+     * @param t root cause
+     */
+    public LockedException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 116 - 0
core/src/main/java/org/acegisecurity/MethodDefinitionAttributes.java

@@ -0,0 +1,116 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.metadata.Attributes;
+
+import java.lang.reflect.Method;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+
+/**
+ * Stores a {@link ConfigAttributeDefinition} for each method signature defined
+ * by Commons Attributes.
+ *
+ * @author Cameron Braid
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodDefinitionAttributes implements MethodDefinitionSource {
+    //~ Instance fields ========================================================
+
+    private Attributes attributes;
+
+    //~ Methods ================================================================
+
+    public void setAttributes(Attributes attributes) {
+        this.attributes = attributes;
+    }
+
+    public ConfigAttributeDefinition getAttributes(MethodInvocation invocation) {
+        ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
+
+        Class interceptedClass = invocation.getMethod().getDeclaringClass();
+
+        // add the class level attributes for the implementing class
+        addClassAttributes(definition, interceptedClass);
+
+        // add the class level attributes for the implemented interfaces
+        addClassAttributes(definition, interceptedClass.getInterfaces());
+
+        // add the method level attributes for the implemented method
+        addMethodAttributes(definition, invocation.getMethod());
+
+        // add the method level attributes for the implemented intreface methods
+        addInterfaceMethodAttributes(definition, invocation.getMethod());
+
+        return definition;
+    }
+
+    public Iterator getConfigAttributeDefinitions() {
+        return null;
+    }
+
+    private void add(ConfigAttributeDefinition definition, Collection attribs) {
+        for (Iterator iter = attribs.iterator(); iter.hasNext();) {
+            Object o = (Object) iter.next();
+
+            if (o instanceof ConfigAttribute) {
+                definition.addConfigAttribute((ConfigAttribute) o);
+            }
+        }
+    }
+
+    private void addClassAttributes(ConfigAttributeDefinition definition,
+                                    Class clazz) {
+        addClassAttributes(definition, new Class[] {clazz});
+    }
+
+    private void addClassAttributes(ConfigAttributeDefinition definition,
+                                    Class[] clazz) {
+        for (int i = 0; i < clazz.length; i++) {
+            Collection classAttributes = attributes.getAttributes(clazz[i]);
+
+            if (classAttributes != null) {
+                add(definition, classAttributes);
+            }
+        }
+    }
+
+    private void addInterfaceMethodAttributes(ConfigAttributeDefinition definition,
+                                              Method method) {
+        Class[] interfaces = method.getDeclaringClass().getInterfaces();
+
+        for (int i = 0; i < interfaces.length; i++) {
+            Class clazz = interfaces[i];
+
+            try {
+                Method m = clazz.getDeclaredMethod(method.getName(),
+                                                   method.getParameterTypes());
+                addMethodAttributes(definition, m);
+            } catch (Exception e) {
+                // this won't happen since we are getting a method from an interface that 
+                // the declaring class implements
+            }
+        }
+    }
+
+    private void addMethodAttributes(ConfigAttributeDefinition definition,
+                                     Method method) {
+        // add the method level attributes
+        Collection methodAttributes = attributes.getAttributes(method);
+
+        if (methodAttributes != null) {
+            add(definition, methodAttributes);
+        }
+    }
+}

+ 181 - 0
core/src/main/java/org/acegisecurity/MethodDefinitionMap.java

@@ -0,0 +1,181 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.lang.reflect.Method;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Stores a {@link ConfigAttributeDefinition} for each method signature defined
+ * in a bean context.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodDefinitionMap implements MethodDefinitionSource {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
+
+    //~ Instance fields ========================================================
+
+    /** Map from Method to ApplicationDefinition */
+    protected Map methodMap = new HashMap();
+
+    /** Map from Method to name pattern used for registration */
+    private Map nameMap = new HashMap();
+
+    //~ Methods ================================================================
+
+    public ConfigAttributeDefinition getAttributes(MethodInvocation invocation) {
+        return (ConfigAttributeDefinition) this.methodMap.get(invocation
+                                                              .getMethod());
+    }
+
+    public Iterator getConfigAttributeDefinitions() {
+        return methodMap.values().iterator();
+    }
+
+    /**
+     * Add required authorities for a secure method. Method names can end or
+     * start with "" for matching multiple methods.
+     *
+     * @param method the method to be secured
+     * @param attr required authorities associated with the method
+     */
+    public void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
+        logger.info("Adding secure method [" + method + "] with attributes ["
+                    + attr + "]");
+        this.methodMap.put(method, attr);
+    }
+
+    /**
+     * Add required authorities for a secure method. Method names can end or
+     * start with "" for matching multiple methods.
+     *
+     * @param name class and method name, separated by a dot
+     * @param attr required authorities associated with the method
+     *
+     * @throws IllegalArgumentException DOCUMENT ME!
+     */
+    public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
+        int lastDotIndex = name.lastIndexOf(".");
+
+        if (lastDotIndex == -1) {
+            throw new IllegalArgumentException("'" + name
+                                               + "' is not a valid method name: format is FQN.methodName");
+        }
+
+        String className = name.substring(0, lastDotIndex);
+        String methodName = name.substring(lastDotIndex + 1);
+
+        try {
+            Class clazz = Class.forName(className, true,
+                                        Thread.currentThread()
+                                              .getContextClassLoader());
+            addSecureMethod(clazz, methodName, attr);
+        } catch (ClassNotFoundException ex) {
+            throw new IllegalArgumentException("Class '" + className
+                                               + "' not found");
+        }
+    }
+
+    /**
+     * Add required authorities for a secure method. Method names can end or
+     * start with "" for matching multiple methods.
+     *
+     * @param clazz target interface or class
+     * @param mappedName mapped method name
+     * @param attr required authorities associated with the method
+     *
+     * @throws IllegalArgumentException DOCUMENT ME!
+     */
+    public void addSecureMethod(Class clazz, String mappedName,
+                                ConfigAttributeDefinition attr) {
+        String name = clazz.getName() + '.' + mappedName;
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Adding secure method [" + name
+                         + "] with attributes [" + attr + "]");
+        }
+
+        Method[] methods = clazz.getDeclaredMethods();
+        List matchingMethods = new ArrayList();
+
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].getName().equals(mappedName)
+                    || isMatch(methods[i].getName(), mappedName)) {
+                matchingMethods.add(methods[i]);
+            }
+        }
+
+        if (matchingMethods.isEmpty()) {
+            throw new IllegalArgumentException("Couldn't find method '"
+                                               + mappedName + "' on " + clazz);
+        }
+
+        // register all matching methods
+        for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
+            Method method = (Method) it.next();
+            String regMethodName = (String) this.nameMap.get(method);
+
+            if ((regMethodName == null)
+                    || (!regMethodName.equals(name)
+                    && (regMethodName.length() <= name.length()))) {
+                // no already registered method name, or more specific
+                // method name specification now -> (re-)register method
+                if (logger.isDebugEnabled() && (regMethodName != null)) {
+                    logger.debug("Replacing attributes for secure method ["
+                                 + method + "]: current name [" + name
+                                 + "] is more specific than [" + regMethodName
+                                 + "]");
+                }
+
+                this.nameMap.put(method, name);
+                addSecureMethod(method, attr);
+            } else {
+                if (logger.isDebugEnabled() && (regMethodName != null)) {
+                    logger.debug("Keeping attributes for secure method ["
+                                 + method + "]: current name [" + name
+                                 + "] is not more specific than ["
+                                 + regMethodName + "]");
+                }
+            }
+        }
+    }
+
+    /**
+     * Return if the given method name matches the mapped name. The default
+     * implementation checks for "xxx" and "xxx" matches.
+     *
+     * @param methodName the method name of the class
+     * @param mappedName the name in the descriptor
+     *
+     * @return if the names match
+     */
+    private boolean isMatch(String methodName, String mappedName) {
+        return (mappedName.endsWith("*")
+               && methodName.startsWith(mappedName.substring(0,
+                                                             mappedName.length()
+                                                             - 1)))
+               || (mappedName.startsWith("*")
+               && methodName.endsWith(mappedName.substring(1,
+                                                           mappedName.length())));
+    }
+}

+ 44 - 0
core/src/main/java/org/acegisecurity/MethodDefinitionSource.java

@@ -0,0 +1,44 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.Iterator;
+
+
+/**
+ * Implemented by classes that store {@link ConfigAttributeDefinition}s and can
+ * identify the appropriate <code>ConfigAttributeDefinition</code> that
+ * applies for the current method call.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface MethodDefinitionSource {
+    //~ Methods ================================================================
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param invocation the method being called
+     *
+     * @return the <code>ConfigAttributeDefinition</code> that applies to the
+     *         passed method call
+     */
+    public ConfigAttributeDefinition getAttributes(MethodInvocation invocation);
+
+    /**
+     * If available, all of the <code>ConfigAttributeDefinition</code>s defined
+     * by the implementing class.
+     *
+     * @return an iterator over all the <code>ConfigAttributeDefinition</code>s
+     *         or <code>null</code> if unsupported
+     */
+    public Iterator getConfigAttributeDefinitions();
+}

+ 70 - 0
core/src/main/java/org/acegisecurity/MethodDefinitionSourceEditor.java

@@ -0,0 +1,70 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.propertyeditors.PropertiesEditor;
+
+import java.beans.PropertyEditorSupport;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+
+/**
+ * Property editor to assist with the setup of  {@link MethodDefinitionSource}.
+ * 
+ * <p>
+ * The class creates and populates an {@link MethodDefinitionMap}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodDefinitionSourceEditor extends PropertyEditorSupport {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(MethodDefinitionSourceEditor.class);
+
+    //~ Methods ================================================================
+
+    public void setAsText(String s) throws IllegalArgumentException {
+        MethodDefinitionMap source = new MethodDefinitionMap();
+
+        if ((s == null) || "".equals(s)) {
+            // Leave value in property editor null
+        } else {
+            // Use properties editor to tokenize the string
+            PropertiesEditor propertiesEditor = new PropertiesEditor();
+            propertiesEditor.setAsText(s);
+
+            Properties props = (Properties) propertiesEditor.getValue();
+
+            // Now we have properties, process each one individually
+            ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
+
+            for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
+                String name = (String) iter.next();
+                String value = props.getProperty(name);
+
+                // Convert value to series of security configuration attributes
+                configAttribEd.setAsText(value);
+
+                ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd
+                                                 .getValue();
+
+                // Register name and attribute
+                source.addSecureMethod(name, attr);
+            }
+        }
+
+        setValue(source);
+    }
+}

+ 90 - 0
core/src/main/java/org/acegisecurity/RunAsManager.java

@@ -0,0 +1,90 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+
+/**
+ * Creates a new temporary {@link Authentication} object for the current method
+ * invocation only.
+ * 
+ * <P>
+ * This interface permits implementations to replace the
+ * <code>Authentication</code> object that applies to the current method
+ * invocation only. The {@link SecurityInterceptor} will replace the
+ * <code>Authentication</code> object held in the  {@link
+ * net.sf.acegisecurity.context.SecureContext} for the duration of the method
+ * invocation only, returning it to the original  <code>Authentication</code>
+ * object when the method invocation completes.
+ * </p>
+ * 
+ * <P>
+ * This is provided so that systems with two layers of objects can be
+ * established. One layer is public facing and has normal secure methods with
+ * the granted authorities expected to be held by external callers. The other
+ * layer is private, and is only expected to be called by objects within the
+ * public facing layer. The objects in this private layer still need security
+ * (otherwise they would be public methods) and they also need security in
+ * such a manner that prevents them being called directly by external callers.
+ * The objects in the private layer would be configured to require granted
+ * authorities never granted to external callers. The
+ * <code>RunAsManager</code> interface provides a mechanism to elevate
+ * security in this manner.
+ * </p>
+ * 
+ * <p>
+ * It is expected implementations will provide a corresponding concrete
+ * <code>Authentication</code> and <code>AuthenticationProvider</code> so that
+ * the replacement <code>Authentication</code> object can be  authenticated.
+ * Some form of security will need to be implemented to prevent to ensure the
+ * <code>AuthenticationProvider</code> only accepts
+ * <code>Authentication</code> objects created by an authorized concrete
+ * implementation of <code>RunAsManager</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface RunAsManager {
+    //~ Methods ================================================================
+
+    /**
+     * Returns a replacement <code>Authentication</code> object for the current
+     * method invocation, or <code>null</code> if replacement not required.
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @return a replacement object to be used for duration of the method
+     *         invocation
+     */
+    public Authentication buildRunAs(Authentication authentication,
+                                     MethodInvocation invocation,
+                                     ConfigAttributeDefinition config);
+
+    /**
+     * Indicates whether this <code>RunAsManager</code> is able to process the
+     * passed <code>ConfigAttribute</code>.
+     * 
+     * <p>
+     * This allows the <code>SecurityInterceptor</code> to check every
+     * configuration attribute can be consumed by the configured
+     * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
+     * </p>
+     *
+     * @param attribute a configuration attribute that has been configured
+     *        against the <code>SecurityInterceptor</code>
+     *
+     * @return true if this <code>RunAsManager</code> can support the passed
+     *         configuration attribute
+     */
+    public boolean supports(ConfigAttribute attribute);
+}

+ 58 - 0
core/src/main/java/org/acegisecurity/SecurityConfig.java

@@ -0,0 +1,58 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+/**
+ * Stores a {@link ConfigAttribute} as a <code>String</code>.
+ *
+ * @author <A HREF="mailto:ben.alex@fremerx.com">Ben Alex</A>
+ * @version $Id$
+ */
+public class SecurityConfig implements ConfigAttribute {
+    //~ Instance fields ========================================================
+
+    private String attrib;
+
+    //~ Constructors ===========================================================
+
+    public SecurityConfig(String config) {
+        this.attrib = config;
+    }
+
+    private SecurityConfig() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public String getAttribute() {
+        return this.attrib;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj instanceof String) {
+            return obj.equals(this.attrib);
+        }
+
+        if (obj instanceof ConfigAttribute) {
+            ConfigAttribute attr = (ConfigAttribute) obj;
+
+            return this.attrib.equals(attr.getAttribute());
+        }
+
+        return false;
+    }
+
+    public int hashCode() {
+        return this.attrib.hashCode();
+    }
+
+    public String toString() {
+        return this.attrib;
+    }
+}

+ 303 - 0
core/src/main/java/org/acegisecurity/SecurityInterceptor.java

@@ -0,0 +1,303 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import net.sf.acegisecurity.context.Context;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * Intercepts calls to an object and applies security.
+ * 
+ * <p>
+ * A method is treated as public unless it has one or more configuration
+ * attributes defined via  {@link
+ * #setMethodDefinitionSource(MethodDefinitionSource)}. If public, no
+ * authentication will be attempted, which means an unauthenticated {@link
+ * Authentication} object may be present in the {@link ContextHolder} (if any
+ * such an unauthenticated <code>Authentication</code> object exists, its
+ * {@link Authentication#isAuthenticated()} method will  return
+ * <code>false</code> once the <code>SecurityInterceptor</code> has
+ * intercepted the public method).
+ * </p>
+ * 
+ * <p>
+ * For those methods to be secured by the interceptor, one or more
+ * configuration attributes must be defined. These attributes are stored as
+ * {@link ConfigAttribute} objects.
+ * </p>
+ * 
+ * <p>
+ * The presence of a configuration attribute for a given method will force
+ * authentication to be attempted via the {@link AuthenticationManager}
+ * configured against the interceptor. If successfully authenticated, the
+ * configured {@link AccessDecisionManager} will be passed the  {@link
+ * ConfigAttributeDefinition} applicable for the method invocation,  the
+ * method invocation itself, and the <code>Authentication</code> object. The
+ * <code>AccessDecisionManager</code> which will then make the  authorization
+ * decision.
+ * </p>
+ * 
+ * <p>
+ * There shouldn't be any requirement to customise the behaviour of the
+ * <code>SecurityInterceptor</code>, as all security decisions are made by the
+ * <code>AuthenticationProvider</code> and <code>AccessDecisionManager</code>
+ * interfaces, which can of course be replaced with different concrete
+ * implementations.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecurityInterceptor implements MethodInterceptor, InitializingBean {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(SecurityInterceptor.class);
+
+    //~ Instance fields ========================================================
+
+    private AccessDecisionManager accessDecisionManager;
+    private AuthenticationManager authenticationManager;
+    private MethodDefinitionSource methodDefinitionSource;
+    private RunAsManager runAsManager;
+    private boolean validateConfigAttributes = true;
+
+    //~ Methods ================================================================
+
+    public void setAccessDecisionManager(
+        AccessDecisionManager accessDecisionManager) {
+        this.accessDecisionManager = accessDecisionManager;
+    }
+
+    public AccessDecisionManager getAccessDecisionManager() {
+        return accessDecisionManager;
+    }
+
+    public void setAuthenticationManager(AuthenticationManager newManager) {
+        this.authenticationManager = newManager;
+    }
+
+    public AuthenticationManager getAuthenticationManager() {
+        return this.authenticationManager;
+    }
+
+    public void setMethodDefinitionSource(MethodDefinitionSource newSource) {
+        this.methodDefinitionSource = newSource;
+    }
+
+    public MethodDefinitionSource getMethodDefinitionSource() {
+        return this.methodDefinitionSource;
+    }
+
+    public void setRunAsManager(RunAsManager runAsManager) {
+        this.runAsManager = runAsManager;
+    }
+
+    public RunAsManager getRunAsManager() {
+        return runAsManager;
+    }
+
+    public void setValidateConfigAttributes(boolean validateConfigAttributes) {
+        this.validateConfigAttributes = validateConfigAttributes;
+    }
+
+    public boolean isValidateConfigAttributes() {
+        return validateConfigAttributes;
+    }
+
+    public void afterPropertiesSet() {
+        if (this.authenticationManager == null) {
+            throw new IllegalArgumentException(
+                "An AuthenticationManager is required");
+        }
+
+        if (this.accessDecisionManager == null) {
+            throw new IllegalArgumentException(
+                "An AccessDecisionManager is required");
+        }
+
+        if (this.runAsManager == null) {
+            throw new IllegalArgumentException("A RunAsManager is required");
+        }
+
+        if (this.methodDefinitionSource == null) {
+            throw new IllegalArgumentException(
+                "A MethodDefinitionSource is required");
+        }
+
+        if (this.validateConfigAttributes) {
+            Iterator iter = this.methodDefinitionSource
+                .getConfigAttributeDefinitions();
+
+            if (iter == null) {
+                if (logger.isWarnEnabled()) {
+                    logger.warn(
+                        "Could not validate configuration attributes as the MethodDefinitionSource did not return a ConfigAttributeDefinition Iterator");
+                }
+
+                return;
+            }
+
+            Set set = new HashSet();
+
+            while (iter.hasNext()) {
+                ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
+                    .next();
+                Iterator attributes = def.getConfigAttributes();
+
+                while (attributes.hasNext()) {
+                    ConfigAttribute attr = (ConfigAttribute) attributes.next();
+
+                    if (!this.runAsManager.supports(attr)
+                        && !this.accessDecisionManager.supports(attr)) {
+                        set.add(attr);
+                    }
+                }
+            }
+
+            if (set.size() == 0) {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Validated configuration attributes");
+                }
+            } else {
+                throw new IllegalArgumentException(
+                    "Unsupported configuration attributes: " + set.toString());
+            }
+        }
+    }
+
+    /**
+     * Does the work of authenticating and authorizing the request. Throws
+     * {@link AcegiSecurityException} and its subclasses.
+     *
+     * @param mi The method being invoked which requires a security decision
+     *
+     * @return The returned value from the method invocation
+     *
+     * @throws Throwable if any error occurs
+     * @throws AuthenticationCredentialsNotFoundException if the
+     *         <code>ContextHolder</code> does not contain a valid
+     *         <code>SecureContext</code> which in turn contains an
+     *         <code>Authentication</code> object
+     */
+    public Object invoke(MethodInvocation mi) throws Throwable {
+        ConfigAttributeDefinition attr = this.methodDefinitionSource
+            .getAttributes(mi);
+
+        if (attr != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Secure method configuration "
+                    + attr.getConfigAttributes().toString());
+            }
+
+            // Ensure ContextHolder presents a populated SecureContext
+            if ((ContextHolder.getContext() == null)
+                || !(ContextHolder.getContext() instanceof SecureContext)) {
+                throw new AuthenticationCredentialsNotFoundException(
+                    "A valid SecureContext was not provided in the RequestContext");
+            }
+
+            SecureContext context = (SecureContext) ContextHolder.getContext();
+
+            if (context.getAuthentication() == null) {
+                throw new AuthenticationCredentialsNotFoundException(
+                    "Authentication credentials were not found in the SecureContext");
+            }
+
+            // Attempt authentication
+            Authentication authenticated = this.authenticationManager
+                .authenticate(context.getAuthentication());
+            authenticated.setAuthenticated(true);
+            logger.debug("Authenticated: " + authenticated.toString());
+            context.setAuthentication(authenticated);
+            ContextHolder.setContext((Context) context);
+
+            // Attempt authorization
+            this.accessDecisionManager.decide(authenticated, mi, attr);
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authorization successful");
+            }
+
+            // Attempt to run as a different user
+            Authentication runAs = this.runAsManager.buildRunAs(authenticated,
+                    mi, attr);
+
+            if (runAs == null) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "RunAsManager did not change Authentication object");
+                }
+
+                Object ret = mi.proceed();
+
+                return ret;
+            } else {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Switching to RunAs Authentication: "
+                        + runAs.toString());
+                }
+
+                context.setAuthentication(runAs);
+                ContextHolder.setContext((Context) context);
+
+                Object ret = mi.proceed();
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Reverting to original Authentication: "
+                        + authenticated.toString());
+                }
+
+                context.setAuthentication(authenticated);
+                ContextHolder.setContext((Context) context);
+
+                return ret;
+            }
+        } else {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Public method - authentication not attempted");
+            }
+
+            // Set Authentication object (if it exists) to be unauthenticated
+            if ((ContextHolder.getContext() != null)
+                && ContextHolder.getContext() instanceof SecureContext) {
+                SecureContext context = (SecureContext) ContextHolder
+                    .getContext();
+
+                if (context.getAuthentication() != null) {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(
+                            "Authentication object detected and tagged as unauthenticated");
+                    }
+
+                    Authentication authenticated = context.getAuthentication();
+                    authenticated.setAuthenticated(false);
+                    context.setAuthentication(authenticated);
+                    ContextHolder.setContext((Context) context);
+                }
+            }
+
+            Object ret = mi.proceed();
+
+            return ret;
+        }
+    }
+}

+ 100 - 0
core/src/main/java/org/acegisecurity/adapters/AbstractAdapterAuthenticationToken.java

@@ -0,0 +1,100 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
+
+
+/**
+ * Convenience superclass for {@link AuthByAdapter} implementations.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AbstractAdapterAuthenticationToken
+    extends AbstractAuthenticationToken implements AuthByAdapter {
+    //~ Instance fields ========================================================
+
+    private GrantedAuthority[] authorities;
+    private int keyHash;
+
+    //~ Constructors ===========================================================
+
+    protected AbstractAdapterAuthenticationToken() {
+        super();
+    }
+
+    /**
+     * The only way an <code>AbstractAdapterAuthentication</code> should be
+     * constructed.
+     *
+     * @param key the key that is hashed and made available via  {@link
+     *        #getKeyHash()}
+     * @param authorities the authorities granted to this principal
+     */
+    protected AbstractAdapterAuthenticationToken(String key,
+                                                 GrantedAuthority[] authorities) {
+        super();
+        this.keyHash = key.hashCode();
+        this.authorities = authorities;
+    }
+
+    //~ Methods ================================================================
+
+    /**
+     * Setting is ignored. Always considered authenticated.
+     *
+     * @param ignored DOCUMENT ME!
+     */
+    public void setAuthenticated(boolean ignored) {
+        // ignored
+    }
+
+    /**
+     * Always returns <code>true</code>.
+     *
+     * @return DOCUMENT ME!
+     */
+    public boolean isAuthenticated() {
+        return true;
+    }
+
+    public GrantedAuthority[] getAuthorities() {
+        return authorities;
+    }
+
+    public int getKeyHash() {
+        return this.keyHash;
+    }
+
+    /**
+     * Iterates the granted authorities and indicates whether or not the
+     * specified role is held.
+     * 
+     * <p>
+     * Comparison is based on the <code>String</code> returned by {@link
+     * GrantedAuthority#getAuthority}.
+     * </p>
+     *
+     * @param role the role being searched for in this object's granted
+     *        authorities list
+     *
+     * @return <code>true</code> if the granted authority is held, or
+     *         <code>false</code> otherwise
+     */
+    public boolean isUserInRole(String role) {
+        for (int i = 0; i < this.authorities.length; i++) {
+            if (role.equals(this.authorities[i].getAuthority())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 146 - 0
core/src/main/java/org/acegisecurity/adapters/AbstractIntegrationFilter.java

@@ -0,0 +1,146 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.context.Context;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Automatically populates a {@link net.sf.acegisecurity.context.SecureContext}
+ * from a subclass-provided container source.
+ * 
+ * <p>
+ * The container is expected to expose an {@link Authentication} object in a
+ * well-known location. The <code>Authentication</code> object will have been
+ * created by the container-specific Acegi Security System for Spring adapter.
+ * </p>
+ * 
+ * <P>
+ * Once the <code>Authentication</code> object has been extracted from the
+ * well-known location, the interceptor handles putting it into the {@link
+ * ContextHolder}. It then removes it once the filter chain has  completed.
+ * </p>
+ * 
+ * <p>
+ * This interceptor will not operate if the container does not provide an
+ * <code>Authentication</code> object from its well-known location.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AbstractIntegrationFilter implements Filter {
+    //~ Static fields/initializers =============================================
+
+    protected static final Log logger = LogFactory.getLog(AbstractIntegrationFilter.class);
+
+    //~ Methods ================================================================
+
+    public void destroy() {}
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain)
+                  throws IOException, ServletException {
+        // Populate authentication information
+        Object extracted = this.extractFromContainer(request);
+
+        if (extracted instanceof Authentication) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication added to ContextHolder from container");
+            }
+
+            Authentication auth = (Authentication) extracted;
+
+            // Get or create existing SecureContext
+            SecureContext secureContext = null;
+
+            if ((ContextHolder.getContext() == null)
+                    || !(ContextHolder.getContext() instanceof SecureContext)) {
+                secureContext = new SecureContextImpl();
+            } else {
+                secureContext = (SecureContext) ContextHolder.getContext();
+            }
+
+            // Add Authentication to SecureContext, and save
+            secureContext.setAuthentication(auth);
+            ContextHolder.setContext((Context) secureContext);
+        } else {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Authentication not added to ContextHolder (could not extract an authentication object from the container which is an instance of Authentication)");
+            }
+        }
+
+        // Proceed with chain
+        chain.doFilter(request, response);
+
+        // Remove authentication information
+        if ((ContextHolder.getContext() != null)
+                && ContextHolder.getContext() instanceof SecureContext) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Removing Authentication from ContextHolder");
+            }
+
+            // Get context holder and remove authentication information
+            SecureContext secureContext = (SecureContext) ContextHolder
+                                          .getContext();
+            secureContext.setAuthentication(null);
+            ContextHolder.setContext((Context) secureContext);
+        } else {
+            if (logger.isDebugEnabled()) {
+                logger.debug("ContextHolder does not contain any authentication information");
+            }
+        }
+    }
+
+    /**
+     * Subclasses must override this method to provide the <code>Object</code>
+     * that contains the <code>Authentication</code> interface.
+     * 
+     * <p>
+     * For convenience we have allowed any <code>Object</code> to be  returned
+     * by subclasses, as the abstract class will ensure class casting safety
+     * and ignore objects that do not implement  <code>Authentication</code>.
+     * </p>
+     * 
+     * <p>
+     * If no authentication object is available, subclasses should return
+     * <code>null</code>.
+     * </p>
+     * 
+     * <p>
+     * If the container can locate multiple authentication objects,  subclasses
+     * should return the object that was created by the Acegi Security System
+     * for Spring adapter (ie that implements <code>Authentication</code>).
+     * </p>
+     *
+     * @param request the request, which may be of use in extracting the
+     *        authentication object
+     *
+     * @return <code>null</code> or an object that implements
+     *         <code>Authentication</code>
+     */
+    public abstract Object extractFromContainer(ServletRequest request);
+
+    public void init(FilterConfig filterConfig) throws ServletException {}
+}

+ 35 - 0
core/src/main/java/org/acegisecurity/adapters/AuthByAdapter.java

@@ -0,0 +1,35 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.Authentication;
+
+
+/**
+ * Indicates a specialized, immutable, server-side only {@link Authentication}
+ * class.
+ * 
+ * <P>
+ * Automatically considered valid by the {@link AuthByAdapterProvider},
+ * provided the hash code presented by the implementation objects matches that
+ * expected by the <code>AuthByAdapterProvider</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AuthByAdapter extends Authentication {
+    //~ Methods ================================================================
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return the hash code of the key used when the object was created.
+     */
+    public int getKeyHash();
+}

+ 77 - 0
core/src/main/java/org/acegisecurity/adapters/AuthByAdapterProvider.java

@@ -0,0 +1,77 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.providers.AuthenticationProvider;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * An {@link AuthenticationProvider} implementation that can authenticate an
+ * {@link AuthByAdapter}.
+ * 
+ * <P>
+ * Configured in the bean context with a key that should match the key used by
+ * adapters to generate <code>AuthByAdapter</code> instances. It treats as
+ * valid any such instance presenting a hash code that matches the
+ * <code>AuthByAdapterProvider</code>-configured key.
+ * </p>
+ * 
+ * <P>
+ * If the key does not match, a <code>BadCredentialsException</code> is
+ * thrown.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthByAdapterProvider implements InitializingBean,
+                                              AuthenticationProvider {
+    //~ Instance fields ========================================================
+
+    private String key;
+
+    //~ Methods ================================================================
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (key == null) {
+            throw new IllegalArgumentException("A Key is required and should match that configured for the adapters");
+        }
+    }
+
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException {
+        AuthByAdapter token = (AuthByAdapter) authentication;
+
+        if (token.getKeyHash() == key.hashCode()) {
+            return authentication;
+        } else {
+            throw new BadCredentialsException("The presented AuthByAdapter implementation does not contain the expected key");
+        }
+    }
+
+    public boolean supports(Class authentication) {
+        if (AuthByAdapter.class.isAssignableFrom(authentication)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 54 - 0
core/src/main/java/org/acegisecurity/adapters/AutoIntegrationFilter.java

@@ -0,0 +1,54 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.adapters.jboss.JbossIntegrationFilter;
+
+import org.jboss.security.SimplePrincipal;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Detects the container and delegates to the appropriate {@link
+ * AbstractIntegrationFilter}.
+ * 
+ * <p>
+ * This eases the creation of portable secured Spring applications, as the
+ * <code>web.xml</code> will not need to refer to a specific container
+ * integration filter.
+ * </p>
+ * 
+ * <p>
+ * See {@link AbstractIntegrationFilter} for further information.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AutoIntegrationFilter extends AbstractIntegrationFilter {
+    //~ Methods ================================================================
+
+    public Object extractFromContainer(ServletRequest request) {
+        if (request instanceof HttpServletRequest) {
+            HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+            if (httpRequest.getUserPrincipal() instanceof Authentication) {
+                return new HttpRequestIntegrationFilter().extractFromContainer(request);
+            }
+
+            if (httpRequest.getUserPrincipal() instanceof SimplePrincipal) {
+                return new JbossIntegrationFilter().extractFromContainer(request);
+            }
+        }
+
+        return null;
+    }
+}

+ 42 - 0
core/src/main/java/org/acegisecurity/adapters/HttpRequestIntegrationFilter.java

@@ -0,0 +1,42 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Populates a {@link net.sf.acegisecurity.context.SecureContext} from the
+ * container's <code>HttpServletRequest.getUserPrincipal()</code>.
+ * 
+ * <p>
+ * See {@link AbstractIntegrationFilter} for further information.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class HttpRequestIntegrationFilter extends AbstractIntegrationFilter {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(HttpRequestIntegrationFilter.class);
+
+    //~ Methods ================================================================
+
+    public Object extractFromContainer(ServletRequest request) {
+        if (request instanceof HttpServletRequest) {
+            return ((HttpServletRequest) request).getUserPrincipal();
+        } else {
+            return null;
+        }
+    }
+}

+ 55 - 0
core/src/main/java/org/acegisecurity/adapters/PrincipalAcegiUserToken.java

@@ -0,0 +1,55 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import net.sf.acegisecurity.GrantedAuthority;
+
+import java.security.Principal;
+
+
+/**
+ * A {@link Principal} compatible  {@link net.sf.acegisecurity.Authentication}
+ * object.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class PrincipalAcegiUserToken extends AbstractAdapterAuthenticationToken
+    implements Principal {
+    //~ Instance fields ========================================================
+
+    private String password;
+    private String username;
+
+    //~ Constructors ===========================================================
+
+    public PrincipalAcegiUserToken(String key, String username,
+        String password, GrantedAuthority[] authorities) {
+        super(key, authorities);
+        this.username = username;
+        this.password = password;
+    }
+
+    private PrincipalAcegiUserToken() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public Object getCredentials() {
+        return this.password;
+    }
+
+    public String getName() {
+        return this.username;
+    }
+
+    public Object getPrincipal() {
+        return this.username;
+    }
+}

+ 8 - 0
core/src/main/java/org/acegisecurity/adapters/package.html

@@ -0,0 +1,8 @@
+<html>
+<body>
+Provides "adapters" so that containers can authenticate with the 
+Acegi Security System for Spring.
+<p>
+</body>
+</html>
+

+ 38 - 0
core/src/main/java/org/acegisecurity/context/Context.java

@@ -0,0 +1,38 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+import java.io.Serializable;
+
+
+/**
+ * Holds objects that are needed on every request.
+ * 
+ * <P>
+ * A <code>Context</code> will be sent between application tiers  via a  {@link
+ * ContextHolder}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface Context extends Serializable {
+    //~ Methods ================================================================
+
+    /**
+     * Check the <code>Context</code> is properly configured.
+     * 
+     * <P>
+     * This allows implementations to confirm they are valid, as this method
+     * is automatically called by the {@link ContextInterceptor}.
+     * </p>
+     *
+     * @throws ContextInvalidException if the <code>Context</code> is invalid.
+     */
+    public void validate() throws ContextInvalidException;
+}

+ 47 - 0
core/src/main/java/org/acegisecurity/context/ContextException.java

@@ -0,0 +1,47 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+import org.springframework.core.NestedRuntimeException;
+
+
+/**
+ * Abstract superclass for all exceptions thrown in the context package and
+ * subpackages.
+ *
+ * <p>
+ * Note that this is a runtime (unchecked) exception.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class ContextException extends NestedRuntimeException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>ContextException</code> with the specified message
+     * and root cause.
+     *
+     * @param msg the detail message
+     * @param t the root cause
+     */
+    public ContextException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+    /**
+     * Constructs a <code>ContextException</code> with the specified message
+     * and no root cause.
+     *
+     * @param msg the detail message
+     */
+    public ContextException(String msg) {
+        super(msg);
+    }
+}

+ 30 - 0
core/src/main/java/org/acegisecurity/context/ContextHolder.java

@@ -0,0 +1,30 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+/**
+ * Associates a given {@link Context} with the current execution thread.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ContextHolder {
+    //~ Static fields/initializers =============================================
+
+    private static ThreadLocal contextHolder = new ThreadLocal();
+
+    //~ Methods ================================================================
+
+    public static void setContext(Context context) {
+        contextHolder.set(context);
+    }
+
+    public static Context getContext() {
+        return (Context) contextHolder.get();
+    }
+}

+ 40 - 0
core/src/main/java/org/acegisecurity/context/ContextHolderEmptyException.java

@@ -0,0 +1,40 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+/**
+ * Thrown if a {@link ContextHolder} object does not contain a valid  {@link
+ * Context}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ContextHolderEmptyException extends ContextException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>ContextHolderEmptyException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message
+     */
+    public ContextHolderEmptyException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>ContextHolderEmptyException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public ContextHolderEmptyException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 22 - 0
core/src/main/java/org/acegisecurity/context/ContextImpl.java

@@ -0,0 +1,22 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+/**
+ * Basic concrete implementation of a {@link Context}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ContextImpl implements Context {
+    //~ Methods ================================================================
+
+    public void validate() throws ContextInvalidException {
+        // Nothing to validate.
+    }
+}

+ 50 - 0
core/src/main/java/org/acegisecurity/context/ContextInterceptor.java

@@ -0,0 +1,50 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Ensures the {@link ContextHolder} contains a valid {@link Context}.
+ * 
+ * <p>
+ * This interceptor works by calling {@link Context#validate()} before
+ * proceeding with method invocations. It is configured in the bean context
+ * with a <code>ProxyFactoryBean</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ *
+ * @see Context#validate()
+ */
+public class ContextInterceptor implements MethodInterceptor {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(ContextInterceptor.class);
+
+    //~ Methods ================================================================
+
+    public Object invoke(MethodInvocation mi) throws Throwable {
+        if (ContextHolder.getContext() == null) {
+            throw new ContextHolderEmptyException("ContextHolder does not contain a Context",
+                                                  null);
+        }
+
+        ContextHolder.getContext().validate();
+
+        Object ret = mi.proceed();
+
+        return ret;
+    }
+}

+ 42 - 0
core/src/main/java/org/acegisecurity/context/ContextInvalidException.java

@@ -0,0 +1,42 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+/**
+ * Thrown if a {@link Context} is not valid, according to  {@link
+ * Context#validate()}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ *
+ * @see Context#validate()
+ */
+public class ContextInvalidException extends ContextException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>ContextInvalidException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message.
+     */
+    public ContextInvalidException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>ContextInvalidException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message.
+     * @param t root cause
+     */
+    public ContextInvalidException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 31 - 0
core/src/main/java/org/acegisecurity/context/SecureContext.java

@@ -0,0 +1,31 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+import net.sf.acegisecurity.Authentication;
+
+
+/**
+ * A {@link Context} that also stores {@link Authentication} information.
+ * 
+ * <p>
+ * This interface must be implemented on contexts that will be presented to the
+ * Acegi Security System for Spring, as it is required by the  {@link
+ * net.sf.acegisecurity.SecurityInterceptor}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface SecureContext {
+    //~ Methods ================================================================
+
+    public void setAuthentication(Authentication newAuthentication);
+
+    public Authentication getAuthentication();
+}

+ 41 - 0
core/src/main/java/org/acegisecurity/context/SecureContextImpl.java

@@ -0,0 +1,41 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.context;
+
+import net.sf.acegisecurity.Authentication;
+
+
+/**
+ * Basic concrete implementation of a {@link SecureContext}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecureContextImpl extends ContextImpl implements SecureContext {
+    //~ Instance fields ========================================================
+
+    private Authentication authentication;
+
+    //~ Methods ================================================================
+
+    public void setAuthentication(Authentication newAuthentication) {
+        this.authentication = newAuthentication;
+    }
+
+    public Authentication getAuthentication() {
+        return this.authentication;
+    }
+
+    public void validate() throws ContextInvalidException {
+        super.validate();
+
+        if (authentication == null) {
+            throw new ContextInvalidException("Authentication not set");
+        }
+    }
+}

+ 10 - 0
core/src/main/java/org/acegisecurity/context/package.html

@@ -0,0 +1,10 @@
+<html>
+<body>
+Provides a "request context".
+<p>
+A request context is associated with the current execution thread. It holds
+objects that would otherwise need to be included in many method signatures, 
+such as for authentication.
+</body>
+</html>
+

+ 21 - 0
core/src/main/java/org/acegisecurity/package.html

@@ -0,0 +1,21 @@
+<html>
+<body>
+Provides core security classes of the Acegi Security System for Spring.
+<p>
+The {@link net.sf.acegisecurity.SecurityInterceptor} is the main class.
+It delegates to two interfaces, 
+{@link net.sf.acegisecurity.AuthenticationManager} and 
+{@link net.sf.acegisecurity.AccessDecisionManager} for authentication and
+authorization respectively.
+<p>
+When configuring <code>SecurityInterceptor</code> in the bean context, each 
+method to be secured is provided a comma separated list of configuration
+attributes ({@link net.sf.acegisecurity.ConfigAttribute}). 
+These configuration attributes are relevant only to 
+<Code>AccessDecisionManager</code>s.
+<p>
+Read the JavaDocs of the key classes listed above to learn more about how 
+the security classes operate.
+</body>
+</html>
+

+ 45 - 0
core/src/main/java/org/acegisecurity/providers/AbstractAuthenticationToken.java

@@ -0,0 +1,45 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.Authentication;
+
+
+/**
+ * Provides a <code>String</code> representation of the Authentication token.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AbstractAuthenticationToken implements Authentication {
+    //~ Methods ================================================================
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(super.toString() + ": ");
+        sb.append("Username: " + this.getPrincipal() + "; ");
+        sb.append("Password: [PROTECTED]; ");
+        sb.append("Authenticated: " + this.isAuthenticated() + "; ");
+
+        if (this.getAuthorities() != null) {
+            sb.append("Granted Authorities: ");
+
+            for (int i = 0; i < this.getAuthorities().length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+
+                sb.append(this.getAuthorities()[i].toString());
+            }
+        } else {
+            sb.append("Not granted any authorities");
+        }
+
+        return sb.toString();
+    }
+}

+ 50 - 0
core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java

@@ -0,0 +1,50 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+
+
+/**
+ * Indicates a class can process a specific  {@link
+ * net.sf.acegisecurity.Authentication}  implementation.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AuthenticationProvider {
+    //~ Methods ================================================================
+
+    /**
+     * Performs authentication with the same contract as  {@link
+     * net.sf.acegisecurity.AuthenticationManager#authenticate(Authentication)}.
+     *
+     * @param authentication the authentication request object.
+     *
+     * @return a fully authenticated object including credentials.
+     *
+     * @throws AuthenticationException if authentication fails.
+     */
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException;
+
+    /**
+     * Returns true if this <Code>AuthenticationProvider</code> supports the
+     * indicated <Code>Authentication</code> object.
+     * 
+     * <P>
+     * Selection of an <code>AuthenticationProvider</code> capable of
+     * performing authentication is conducted at runtime the
+     * <code>ProviderManager</code>.
+     * </p>
+     *
+     * @return DOCUMENT ME!
+     */
+    public boolean supports(Class authentication);
+}

+ 132 - 0
core/src/main/java/org/acegisecurity/providers/ProviderManager.java

@@ -0,0 +1,132 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Iterates an {@link Authentication} request through a list of  {@link
+ * AuthenticationProvider}s.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ProviderManager implements InitializingBean, AuthenticationManager {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(ProviderManager.class);
+
+    //~ Instance fields ========================================================
+
+    private List providers;
+
+    //~ Methods ================================================================
+
+    /**
+     * Sets the {@link AuthenticationProvider} objects to be used for
+     * authentication.
+     *
+     * @param newList
+     *
+     * @throws IllegalArgumentException DOCUMENT ME!
+     */
+    public void setProviders(List newList) {
+        checkIfValidList(newList);
+
+        Iterator iter = newList.iterator();
+
+        while (iter.hasNext()) {
+            Object currentObject = null;
+
+            try {
+                currentObject = iter.next();
+
+                AuthenticationProvider attemptToCast = (AuthenticationProvider) currentObject;
+            } catch (ClassCastException cce) {
+                throw new IllegalArgumentException("AuthenticationProvider "
+                                                   + currentObject.getClass()
+                                                                  .getName()
+                                                   + " must implement AuthenticationProvider");
+            }
+        }
+
+        this.providers = newList;
+    }
+
+    public List getProviders() {
+        return this.providers;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        checkIfValidList(this.providers);
+    }
+
+    /**
+     * Attempts to authenticate the passed {@link Authentication} object.
+     * 
+     * <p>
+     * The list of {@link AuthenticationProvider}s will be successively tried
+     * until an <code>AuthenticationProvider</code> indicates it is  capable
+     * of authenticating the type of <code>Authentication</code> object
+     * passed. Authentication will then be attempted with that
+     * <code>AuthenticationProvider</code>.
+     * </p>
+     * 
+     * <p>
+     * If more than one <code>AuthenticationProvider</code> supports the passed
+     * <code>Authentication</code> object, only the first
+     * <code>AuthenticationProvider</code> tried will determine the result. No
+     * subsequent <code>AuthenticationProvider</code>s will be tried.
+     * </p>
+     *
+     * @param authentication the authentication request object.
+     *
+     * @return a fully authenticated object including credentials.
+     *
+     * @throws AuthenticationException if authentication fails.
+     * @throws ProviderNotFoundException DOCUMENT ME!
+     */
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException {
+        Iterator iter = providers.iterator();
+
+        Class toTest = authentication.getClass();
+
+        while (iter.hasNext()) {
+            AuthenticationProvider provider = (AuthenticationProvider) iter
+                                              .next();
+
+            if (provider.supports(toTest)) {
+                logger.debug("Authentication attempt using "
+                             + provider.getClass().getName());
+
+                return provider.authenticate(authentication);
+            }
+        }
+
+        throw new ProviderNotFoundException("No authentication provider for "
+                                            + toTest.getName());
+    }
+
+    private void checkIfValidList(List listToCheck) {
+        if ((listToCheck == null) || (listToCheck.size() == 0)) {
+            throw new IllegalArgumentException("A list of AuthenticationManagers is required");
+        }
+    }
+}

+ 44 - 0
core/src/main/java/org/acegisecurity/providers/ProviderNotFoundException.java

@@ -0,0 +1,44 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.AuthenticationException;
+
+
+/**
+ * Thrown by {@link ProviderManager} if no  {@link AuthenticationProvider}
+ * could be found that supports the presented {@link
+ * net.sf.acegisecurity.Authentication} object.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ProviderNotFoundException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>ProviderNotFoundException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message
+     */
+    public ProviderNotFoundException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>ProviderNotFoundException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message
+     * @param t root cause
+     */
+    public ProviderNotFoundException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

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

@@ -0,0 +1,46 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+
+
+/**
+ * An {@link AuthenticationProvider} implementation for the  {@link
+ * TestingAuthenticationToken}.
+ * 
+ * <p>
+ * It simply accepts as valid whatever is contained within the
+ * <code>TestingAuthenticationToken</code>.
+ * </p>
+ * 
+ * <p>
+ * The purpose of this implementation is to facilitate unit testing. This
+ * provider should <B>never be enabled on a production system</b>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class TestingAuthenticationProvider implements AuthenticationProvider {
+    //~ Methods ================================================================
+
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException {
+        return authentication;
+    }
+
+    public boolean supports(Class authentication) {
+        if (TestingAuthenticationToken.class.isAssignableFrom(authentication)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 67 - 0
core/src/main/java/org/acegisecurity/providers/TestingAuthenticationToken.java

@@ -0,0 +1,67 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.GrantedAuthority;
+
+
+/**
+ * An {@link net.sf.acegisecurity.Authentication} implementation that is
+ * designed for use whilst unit testing.
+ * 
+ * <p>
+ * The corresponding authentication provider is  {@link
+ * TestingAuthenticationProvider}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class TestingAuthenticationToken extends AbstractAuthenticationToken {
+    //~ Instance fields ========================================================
+
+    private Object credentials;
+    private Object principal;
+    private GrantedAuthority[] authorities;
+    private boolean authenticated = false;
+
+    //~ Constructors ===========================================================
+
+    public TestingAuthenticationToken(Object principal, Object credentials,
+                                      GrantedAuthority[] authorities) {
+        this.principal = principal;
+        this.credentials = credentials;
+        this.authorities = authorities;
+    }
+
+    private TestingAuthenticationToken() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.authenticated = isAuthenticated;
+    }
+
+    public boolean isAuthenticated() {
+        return this.authenticated;
+    }
+
+    public GrantedAuthority[] getAuthorities() {
+        return this.authorities;
+    }
+
+    public Object getCredentials() {
+        return this.credentials;
+    }
+
+    public Object getPrincipal() {
+        return this.principal;
+    }
+}

+ 81 - 0
core/src/main/java/org/acegisecurity/providers/UsernamePasswordAuthenticationToken.java

@@ -0,0 +1,81 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers;
+
+import net.sf.acegisecurity.GrantedAuthority;
+
+
+/**
+ * An {@link net.sf.acegisecurity.Authentication} implementation that is
+ * designed for simple presentation of a username and password.
+ * 
+ * <p>
+ * The <code>principal</code> and <code>credentials</code> should be set with
+ * an <code>Object</code> that provides the respective property via its
+ * <code>Object.toString()</code> method. The simplest such
+ * <code>Object</code> to use is <code>String</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UsernamePasswordAuthenticationToken
+    extends AbstractAuthenticationToken {
+    //~ Instance fields ========================================================
+
+    private Object credentials;
+    private Object principal;
+    private GrantedAuthority[] authorities;
+    private boolean authenticated = false;
+
+    //~ Constructors ===========================================================
+
+    public UsernamePasswordAuthenticationToken(Object principal,
+                                               Object credentials) {
+        this.principal = principal;
+        this.credentials = credentials;
+    }
+
+    public UsernamePasswordAuthenticationToken(Object principal,
+                                               Object credentials,
+                                               GrantedAuthority[] authorities) {
+        this.principal = principal;
+        this.credentials = credentials;
+        this.authorities = authorities;
+    }
+
+    private UsernamePasswordAuthenticationToken() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.authenticated = isAuthenticated;
+    }
+
+    public boolean isAuthenticated() {
+        return this.authenticated;
+    }
+
+    public void setAuthorities(GrantedAuthority[] authorities) {
+        this.authorities = authorities;
+    }
+
+    public GrantedAuthority[] getAuthorities() {
+        return this.authorities;
+    }
+
+    public Object getCredentials() {
+        return this.credentials;
+    }
+
+    public Object getPrincipal() {
+        return this.principal;
+    }
+}

+ 124 - 0
core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java

@@ -0,0 +1,124 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.AuthenticationServiceException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.DisabledException;
+import net.sf.acegisecurity.providers.AuthenticationProvider;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import org.springframework.dao.DataAccessException;
+
+
+/**
+ * An {@link AuthenticationProvider} implementation that retrieves user details
+ * from an {@link AuthenticationDao}.
+ * 
+ * <p>
+ * This <code>AuthenticationProvider</code> is capable of validating  {@link
+ * UsernamePasswordAuthenticationToken} requests contain the correct username,
+ * password and the user is not disabled.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class DaoAuthenticationProvider implements AuthenticationProvider,
+                                                  InitializingBean {
+    //~ Instance fields ========================================================
+
+    private AuthenticationDao authenticationDao;
+    private boolean ignorePasswordCase = false;
+    private boolean ignoreUsernameCase = true;
+
+    //~ Methods ================================================================
+
+    public void setAuthenticationDao(AuthenticationDao authenticationDao) {
+        this.authenticationDao = authenticationDao;
+    }
+
+    public AuthenticationDao getAuthenticationDao() {
+        return authenticationDao;
+    }
+
+    /**
+     * Indicates whether the password comparison is case sensitive. Defaults to
+     * <code>false</code>, meaning an exact case match is required.
+     *
+     * @param ignorePasswordCase set to <code>true</code> for less stringent
+     *        comparison
+     */
+    public void setIgnorePasswordCase(boolean ignorePasswordCase) {
+        this.ignorePasswordCase = ignorePasswordCase;
+    }
+
+    public boolean isIgnorePasswordCase() {
+        return ignorePasswordCase;
+    }
+
+    /**
+     * Indicates whether the username search is case sensitive. Default to
+     * <code>true</code>, meaning an exact case match is not  required.
+     *
+     * @param ignoreUsernameCase set to <code>false</code> for more stringent
+     *        comparison
+     */
+    public void setIgnoreUsernameCase(boolean ignoreUsernameCase) {
+        this.ignoreUsernameCase = ignoreUsernameCase;
+    }
+
+    public boolean isIgnoreUsernameCase() {
+        return ignoreUsernameCase;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (this.authenticationDao == null) {
+            throw new IllegalArgumentException("An Authentication DAO must be set");
+        }
+    }
+
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException {
+        User user = null;
+
+        try {
+            user = this.authenticationDao.loadUserByUsername(authentication.getPrincipal()
+                                                                           .toString());
+        } catch (UsernameNotFoundException notFound) {
+            throw new BadCredentialsException("Bad credentials presented");
+        } catch (DataAccessException repositoryProblem) {
+            throw new AuthenticationServiceException(repositoryProblem
+                                                     .getMessage());
+        }
+
+        if (!user.isEnabled()) {
+            throw new DisabledException("User is disabled");
+        }
+
+        if (!user.getPassword().equals(authentication.getCredentials().toString())) {
+            throw new BadCredentialsException("Bad credentials presented");
+        }
+
+        return new UsernamePasswordAuthenticationToken(user.getUsername(),
+                                                       user.getPassword(),
+                                                       user.getAuthorities());
+    }
+
+    public boolean supports(Class authentication) {
+        if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 43 - 0
core/src/main/java/org/acegisecurity/providers/dao/UsernameNotFoundException.java

@@ -0,0 +1,43 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import net.sf.acegisecurity.AuthenticationException;
+
+
+/**
+ * Thrown if an {@link AuthenticationDao} implementation cannot locate a {@link
+ * User} by its username.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UsernameNotFoundException extends AuthenticationException {
+    //~ Constructors ===========================================================
+
+    /**
+     * Constructs a <code>UsernameNotFoundException</code> with the specified
+     * message.
+     *
+     * @param msg the detail message.
+     */
+    public UsernameNotFoundException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a <code>UsernameNotFoundException</code> with the specified
+     * message and root cause.
+     *
+     * @param msg the detail message.
+     * @param t root cause
+     */
+    public UsernameNotFoundException(String msg, Throwable t) {
+        super(msg, t);
+    }
+}

+ 5 - 0
core/src/main/java/org/acegisecurity/providers/dao/package.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+An authentication provider that relies upon a data access object.
+</body>
+</html>

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

@@ -0,0 +1,7 @@
+<html>
+<body>
+Implements a provider-based approach to authorization decisions.
+<p>
+</body>
+</html>
+

+ 77 - 0
core/src/main/java/org/acegisecurity/runas/RunAsImplAuthenticationProvider.java

@@ -0,0 +1,77 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.runas;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.providers.AuthenticationProvider;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * An {@link AuthenticationProvider} implementation that can authenticate a
+ * {@link RunAsUserToken}.
+ * 
+ * <P>
+ * Configured in the bean context with a key that should match the key used by
+ * adapters to generate the <code>RunAsUserToken</code>. It treats as valid
+ * any <code>RunAsUserToken</code> instance presenting a hash code that
+ * matches the <code>RunAsImplAuthenticationProvider</code>-configured key.
+ * </p>
+ * 
+ * <P>
+ * If the key does not match, a <code>BadCredentialsException</code> is
+ * thrown.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RunAsImplAuthenticationProvider implements InitializingBean,
+                                                        AuthenticationProvider {
+    //~ Instance fields ========================================================
+
+    private String key;
+
+    //~ Methods ================================================================
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (key == null) {
+            throw new IllegalArgumentException("A Key is required and should match that configured for the RunAsManagerImpl");
+        }
+    }
+
+    public Authentication authenticate(Authentication authentication)
+                                throws AuthenticationException {
+        RunAsUserToken token = (RunAsUserToken) authentication;
+
+        if (token.getKeyHash() == key.hashCode()) {
+            return authentication;
+        } else {
+            throw new BadCredentialsException("The presented RunAsUserToken does not contain the expected key");
+        }
+    }
+
+    public boolean supports(Class authentication) {
+        if (RunAsUserToken.class.isAssignableFrom(authentication)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 108 - 0
core/src/main/java/org/acegisecurity/runas/RunAsManagerImpl.java

@@ -0,0 +1,108 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.runas;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.RunAsManager;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * Basic concrete implementation of a {@link RunAsManager}.
+ * 
+ * <p>
+ * Is activated if any {@link ConfigAttribute#getAttribute()} is prefixed  with
+ * <Code>RUN_AS_</code>. If found, it generates a new  {@link RunAsUserToken}
+ * containing the same principal, credentials and granted authorities as the
+ * original {@link Authentication} object, along with {@link
+ * GrantedAuthorityImpl}s for each <code>RUN_AS_</code> indicated. The created
+ * <code>GrantedAuthorityImpl</code>s will be prefixed with <code>ROLE_</code>
+ * and then the remainder of the <code>RUN_AS_</code> keyword. For example,
+ * <code>RUN_AS_FOO</code> will result in the creation of a granted authority
+ * of <code>ROLE_RUN_AS_FOO</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RunAsManagerImpl implements RunAsManager, InitializingBean {
+    //~ Instance fields ========================================================
+
+    private String key;
+
+    //~ Methods ================================================================
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (key == null) {
+            throw new IllegalArgumentException("A Key is required and should match that configured for the RunAsImplAuthenticationProvider");
+        }
+    }
+
+    public Authentication buildRunAs(Authentication authentication,
+                                     MethodInvocation invocation,
+                                     ConfigAttributeDefinition config) {
+        Set newAuthorities = new HashSet();
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+
+            if (this.supports(attribute)) {
+                GrantedAuthorityImpl extraAuthority = new GrantedAuthorityImpl("ROLE_"
+                                                                               + attribute
+                                                                                 .getAttribute());
+                newAuthorities.add(extraAuthority);
+            }
+        }
+
+        if (newAuthorities.size() == 0) {
+            return null;
+        } else {
+            for (int i = 0; i < authentication.getAuthorities().length; i++) {
+                newAuthorities.add(authentication.getAuthorities()[i]);
+            }
+
+            GrantedAuthority[] resultType = {new GrantedAuthorityImpl("holder")};
+            GrantedAuthority[] newAuthoritiesAsArray = (GrantedAuthority[]) newAuthorities
+                                                         .toArray(resultType);
+
+            return new RunAsUserToken(this.key, authentication.getPrincipal(),
+                                      authentication.getCredentials(),
+                                      newAuthoritiesAsArray,
+                                      authentication.getClass());
+        }
+    }
+
+    public boolean supports(ConfigAttribute attribute) {
+        if ((attribute.getAttribute() != null)
+                && attribute.getAttribute().startsWith("RUN_AS_")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 93 - 0
core/src/main/java/org/acegisecurity/runas/RunAsUserToken.java

@@ -0,0 +1,93 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.runas;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
+
+
+/**
+ * An immutable {@link net.sf.acegisecurity.Authentication}  implementation
+ * that supports {@link RunAsManagerImpl}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RunAsUserToken extends AbstractAuthenticationToken {
+    //~ Instance fields ========================================================
+
+    private Class originalAuthentication;
+    private Object credentials;
+    private Object principal;
+    private GrantedAuthority[] authorities;
+    private int keyHash;
+
+    //~ Constructors ===========================================================
+
+    public RunAsUserToken(String key, Object principal, Object credentials,
+                          GrantedAuthority[] authorities,
+                          Class originalAuthentication) {
+        super();
+        this.keyHash = key.hashCode();
+        this.authorities = authorities;
+        this.principal = principal;
+        this.credentials = credentials;
+        this.originalAuthentication = originalAuthentication;
+    }
+
+    private RunAsUserToken() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    /**
+     * Setting is ignored. Always considered authenticated.
+     *
+     * @param ignored DOCUMENT ME!
+     */
+    public void setAuthenticated(boolean ignored) {
+        // ignored
+    }
+
+    /**
+     * Always returns <code>true</code>.
+     *
+     * @return DOCUMENT ME!
+     */
+    public boolean isAuthenticated() {
+        return true;
+    }
+
+    public GrantedAuthority[] getAuthorities() {
+        return this.authorities;
+    }
+
+    public Object getCredentials() {
+        return this.credentials;
+    }
+
+    public int getKeyHash() {
+        return this.keyHash;
+    }
+
+    public Class getOriginalAuthentication() {
+        return this.originalAuthentication;
+    }
+
+    public Object getPrincipal() {
+        return this.principal;
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer(super.toString());
+        sb.append("; Original Class: " + this.originalAuthentication.getName());
+
+        return sb.toString();
+    }
+}

+ 5 - 0
core/src/main/java/org/acegisecurity/runas/package.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+Allows intercepted methods to be run under a different authentication identity.
+</body>
+</html>

+ 71 - 0
core/src/main/java/org/acegisecurity/userdetails/User.java

@@ -0,0 +1,71 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import net.sf.acegisecurity.GrantedAuthority;
+
+
+/**
+ * Models core user information retieved by an {@link AuthenticationDao}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class User {
+    //~ Instance fields ========================================================
+
+    private String password;
+    private String username;
+    private GrantedAuthority[] authorities;
+    private boolean enabled;
+
+    //~ Constructors ===========================================================
+
+    /**
+     * Construct the <code>User</code> with the details required by  {@link
+     * DaoAuthenticationProvider}.
+     *
+     * @param username the username presented to the
+     *        <code>DaoAuthenticationProvider</code>
+     * @param password the password that should be presented to the
+     *        <code>DaoAuthenticationProvider</code>
+     * @param enabled set to <code>true</code> if the user is enabled
+     * @param authorities the authorities that should be granted to the caller
+     *        if they presented the correct username and password and the user
+     *        is enabled
+     */
+    public User(String username, String password, boolean enabled,
+                GrantedAuthority[] authorities) {
+        this.username = username;
+        this.password = password;
+        this.enabled = enabled;
+        this.authorities = authorities;
+    }
+
+    private User() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public GrantedAuthority[] getAuthorities() {
+        return authorities;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+}

+ 46 - 0
core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java

@@ -0,0 +1,46 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao;
+
+import org.springframework.dao.DataAccessException;
+
+
+/**
+ * Defines an interface for implementations that wish to provide data access
+ * services to the {@link DaoAuthenticationProvider}.
+ * 
+ * <p>
+ * The interface requires only one read-only method, which simplifies support
+ * of new data access strategies.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AuthenticationDao {
+    //~ Methods ================================================================
+
+    /**
+     * Locates the user based on the username. The search is case insensitive,
+     * meaning the implementation must return any matching object irrespective
+     * of the mixture of uppercase and lowercase characters in the username.
+     *
+     * @param username the username presented to the {@link
+     *        DaoAuthenticationProvider}
+     *
+     * @return a fully populated user record
+     *
+     * @throws UsernameNotFoundException if the user could not be found or the
+     *         user has no GrantedAuthority
+     * @throws DataAccessException if user could not be found for a
+     *         repository-specific reason
+     */
+    public User loadUserByUsername(String username)
+                            throws UsernameNotFoundException, 
+                                   DataAccessException;
+}

+ 139 - 0
core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java

@@ -0,0 +1,139 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.jdbc;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.providers.dao.AuthenticationDao;
+import net.sf.acegisecurity.providers.dao.User;
+import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.context.ApplicationContextException;
+
+import org.springframework.dao.DataAccessException;
+
+import org.springframework.jdbc.core.SqlParameter;
+import org.springframework.jdbc.core.support.JdbcDaoSupport;
+import org.springframework.jdbc.object.MappingSqlQuery;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import java.util.List;
+
+import javax.sql.DataSource;
+
+
+/**
+ * Retrieves user details from a JDBC location provided by the bean context.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(JdbcDaoSupport.class);
+
+    //~ Instance fields ========================================================
+
+    private AuthoritiesByUsernameQuery authoritiesByUsernameQuery;
+    private UsersByUsernameQuery usersByUsernameQuery;
+
+    //~ Methods ================================================================
+
+    public User loadUserByUsername(String username)
+                            throws UsernameNotFoundException, 
+                                   DataAccessException {
+        List users = usersByUsernameQuery.execute(username);
+
+        if (users.size() == 0) {
+            throw new UsernameNotFoundException("User not found");
+        }
+
+        User user = (User) users.get(0); // contains no GrantedAuthority[]
+
+        List dbAuths = authoritiesByUsernameQuery.execute(user.getUsername());
+
+        if (dbAuths.size() == 0) {
+            throw new UsernameNotFoundException("User has no GrantedAuthority");
+        }
+
+        GrantedAuthority[] arrayAuths = {new GrantedAuthorityImpl("demo")};
+        arrayAuths = (GrantedAuthority[]) dbAuths.toArray(arrayAuths);
+
+        return new User(user.getUsername(), user.getPassword(),
+                        user.isEnabled(), arrayAuths);
+    }
+
+    protected void setAuthoritiesByUsernameQuery(AuthoritiesByUsernameQuery authoritiesByUsernameQuery) {
+        this.authoritiesByUsernameQuery = authoritiesByUsernameQuery;
+    }
+
+    protected void setUsersByUsernameQuery(UsersByUsernameQuery usersByUsernameQuery) {
+        this.usersByUsernameQuery = usersByUsernameQuery;
+    }
+
+    protected void initDao() throws ApplicationContextException {
+        if (usersByUsernameQuery == null) {
+            usersByUsernameQuery = new UsersByUsernameQuery(getDataSource());
+        }
+
+        if (authoritiesByUsernameQuery == null) {
+            authoritiesByUsernameQuery = new AuthoritiesByUsernameQuery(getDataSource());
+        }
+    }
+
+    //~ Inner Classes ==========================================================
+
+    /**
+     * Query object to look up a user's authorities.
+     */
+    protected static class AuthoritiesByUsernameQuery extends MappingSqlQuery {
+        protected AuthoritiesByUsernameQuery(DataSource ds) {
+            super(ds,
+                  "SELECT username,authority FROM authorities WHERE username = ?");
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+
+        protected Object mapRow(ResultSet rs, int rownum)
+                         throws SQLException {
+            GrantedAuthorityImpl authority = new GrantedAuthorityImpl(rs
+                                                                      .getString("authority"));
+
+            return authority;
+        }
+    }
+
+    /**
+     * Query object to look up a user.
+     */
+    protected static class UsersByUsernameQuery extends MappingSqlQuery {
+        protected UsersByUsernameQuery(DataSource ds) {
+            super(ds,
+                  "SELECT username,password,enabled FROM users WHERE username = ?");
+            declareParameter(new SqlParameter(Types.VARCHAR));
+            compile();
+        }
+
+        protected Object mapRow(ResultSet rs, int rownum)
+                         throws SQLException {
+            String username = rs.getString("username");
+            String password = rs.getString("password");
+            boolean enabled = rs.getBoolean("enabled");
+            User user = new User(username, password, enabled, null);
+
+            return user;
+        }
+    }
+}

+ 5 - 0
core/src/main/java/org/acegisecurity/userdetails/jdbc/package.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+Exposes a JDBC-based authentication repository.
+</body>
+</html>

+ 57 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/InMemoryDaoImpl.java

@@ -0,0 +1,57 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.memory;
+
+import net.sf.acegisecurity.providers.dao.AuthenticationDao;
+import net.sf.acegisecurity.providers.dao.User;
+import net.sf.acegisecurity.providers.dao.UsernameNotFoundException;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import org.springframework.dao.DataAccessException;
+
+
+/**
+ * Retrieves user details from an in-memory list created by the bean context.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class InMemoryDaoImpl implements AuthenticationDao, InitializingBean {
+    //~ Instance fields ========================================================
+
+    private UserMap userMap;
+
+    //~ Methods ================================================================
+
+    public void setUserMap(UserMap userMap) {
+        this.userMap = userMap;
+    }
+
+    public UserMap getUserMap() {
+        return userMap;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (this.userMap == null) {
+            throw new IllegalArgumentException("A list of users, passwords, enabled/disabled status and their granted authorities must be set");
+        }
+    }
+
+    public User loadUserByUsername(String username)
+                            throws UsernameNotFoundException, 
+                                   DataAccessException {
+        User result = userMap.getUser(username);
+
+        if (result == null) {
+            throw new UsernameNotFoundException("User could not be found");
+        }
+
+        return result;
+    }
+}

+ 72 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/UserAttributeDefinition.java

@@ -0,0 +1,72 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.memory;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Used by {@link InMemoryDaoImpl} to temporarily store the attributes
+ * associated with a user.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UserAttributeDefinition {
+    //~ Instance fields ========================================================
+
+    private Set authorities = new HashSet();
+    private String password;
+    private boolean enabled = true;
+
+    //~ Constructors ===========================================================
+
+    public UserAttributeDefinition() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public GrantedAuthority[] getAuthorities() {
+        GrantedAuthority[] toReturn = {new GrantedAuthorityImpl("demo")};
+
+        return (GrantedAuthority[]) this.authorities.toArray(toReturn);
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public boolean isValid() {
+        if ((this.password != null) && (authorities.size() > 0)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void addAuthority(GrantedAuthority newAuthority) {
+        this.authorities.add(newAuthority);
+    }
+}

+ 57 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/UserAttributeEditor.java

@@ -0,0 +1,57 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.memory;
+
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+
+
+/**
+ * Property editor that creates a {@link UserAttributeDefinition} from a  comma
+ * separated list of values.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UserAttributeEditor extends PropertyEditorSupport {
+    //~ Methods ================================================================
+
+    public void setAsText(String s) throws IllegalArgumentException {
+        if ((s == null) || "".equals(s)) {
+            setValue(null);
+        } else {
+            String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
+            UserAttributeDefinition userAttrib = new UserAttributeDefinition();
+
+            for (int i = 0; i < tokens.length; i++) {
+                String currentToken = tokens[i];
+
+                if (i == 0) {
+                    userAttrib.setPassword(currentToken);
+                } else {
+                    if (currentToken.toLowerCase().equals("enabled")) {
+                        userAttrib.setEnabled(true);
+                    } else if (currentToken.toLowerCase().equals("disabled")) {
+                        userAttrib.setEnabled(false);
+                    } else {
+                        userAttrib.addAuthority(new GrantedAuthorityImpl(currentToken));
+                    }
+                }
+            }
+
+            if (userAttrib.isValid()) {
+                setValue(userAttrib);
+            } else {
+                setValue(null);
+            }
+        }
+    }
+}

+ 50 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/UserMap.java

@@ -0,0 +1,50 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.memory;
+
+import net.sf.acegisecurity.providers.dao.User;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Used by {@link InMemoryDaoImpl} to store a list of users and their
+ * corresponding granted authorities.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UserMap {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(UserMap.class);
+
+    //~ Instance fields ========================================================
+
+    private Map userMap = new HashMap();
+
+    //~ Methods ================================================================
+
+    public User getUser(String username) {
+        return (User) this.userMap.get(username.toLowerCase());
+    }
+
+    /**
+     * Adds a user to the in-memory map.
+     *
+     * @param user the user to be stored
+     */
+    public void addUser(User user) {
+        logger.info("Adding user [" + user + "]");
+        this.userMap.put(user.getUsername().toLowerCase(), user);
+    }
+}

+ 93 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/UserMapEditor.java

@@ -0,0 +1,93 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.providers.dao.memory;
+
+import net.sf.acegisecurity.providers.dao.User;
+
+import org.springframework.beans.propertyeditors.PropertiesEditor;
+
+import java.beans.PropertyEditorSupport;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+
+/**
+ * Property editor to assist with the setup of a {@link UserMap}.
+ * 
+ * <p>
+ * The format of entries should be:
+ * </p>
+ * 
+ * <p>
+ * <code>
+ * username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
+ * </code>
+ * </p>
+ * 
+ * <p>
+ * The <code>password</code> must always be the first entry after the equals.
+ * The <code>enabled</code> or <code>disabled</code> keyword can appear
+ * anywhere (apart from the first entry reserved for the password). If neither
+ * <code>enabled</code> or <code>disabled</code> appear, the default is
+ * <code>enabled</code>. At least one granted authority must be listed.
+ * </p>
+ * 
+ * <p>
+ * The <code>username</code> represents the key and duplicates are handled the
+ * same was as duplicates would be in Java <code>Properties</code> files.
+ * </p>
+ * 
+ * <p>
+ * If the above requirements are not met, the invalid entry will be silently
+ * ignored.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UserMapEditor extends PropertyEditorSupport {
+    //~ Methods ================================================================
+
+    public void setAsText(String s) throws IllegalArgumentException {
+        UserMap userMap = new UserMap();
+
+        if ((s == null) || "".equals(s)) {
+            // Leave value in property editor null
+        } else {
+            // Use properties editor to tokenize the string
+            PropertiesEditor propertiesEditor = new PropertiesEditor();
+            propertiesEditor.setAsText(s);
+
+            Properties props = (Properties) propertiesEditor.getValue();
+
+            // Now we have properties, process each one individually
+            UserAttributeEditor configAttribEd = new UserAttributeEditor();
+
+            for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
+                String username = (String) iter.next();
+                String value = props.getProperty(username);
+
+                // Convert value to a password, enabled setting, and list of granted authorities
+                configAttribEd.setAsText(value);
+
+                UserAttributeDefinition attr = (UserAttributeDefinition) configAttribEd
+                                               .getValue();
+
+                // Make a user object, assuming the properties were properly provided
+                if (attr != null) {
+                    User user = new User(username, attr.getPassword(),
+                                         attr.isEnabled(), attr.getAuthorities());
+                    userMap.addUser(user);
+                }
+            }
+        }
+
+        setValue(userMap);
+    }
+}

+ 6 - 0
core/src/main/java/org/acegisecurity/userdetails/memory/package.html

@@ -0,0 +1,6 @@
+<html>
+<body>
+Exposes an in-memory authentication repository.
+</body>
+</html>
+

+ 98 - 0
core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java

@@ -0,0 +1,98 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.AccessDecisionManager;
+import net.sf.acegisecurity.ConfigAttribute;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Abstract implementation of {@link AccessDecisionManager}.
+ * 
+ * <p>
+ * Handles configuration of a bean context defined list of  {@link
+ * AccessDecisionVoter}s and the access control behaviour if all  voters
+ * abstain from voting (defaults to deny access).
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public abstract class AbstractAccessDecisionManager
+    implements AccessDecisionManager, InitializingBean {
+    //~ Instance fields ========================================================
+
+    private List decisionVoters;
+    private boolean allowIfAllAbstainDecisions = false;
+
+    //~ Methods ================================================================
+
+    public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {
+        this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions;
+    }
+
+    public boolean isAllowIfAllAbstainDecisions() {
+        return allowIfAllAbstainDecisions;
+    }
+
+    public void setDecisionVoters(List newList) {
+        checkIfValidList(newList);
+
+        Iterator iter = newList.iterator();
+
+        while (iter.hasNext()) {
+            Object currentObject = null;
+
+            try {
+                currentObject = iter.next();
+
+                AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject;
+            } catch (ClassCastException cce) {
+                throw new IllegalArgumentException("AccessDecisionVoter "
+                                                   + currentObject.getClass()
+                                                                  .getName()
+                                                   + " must implement AccessDecisionVoter");
+            }
+        }
+
+        this.decisionVoters = newList;
+    }
+
+    public List getDecisionVoters() {
+        return this.decisionVoters;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        checkIfValidList(this.decisionVoters);
+    }
+
+    public boolean supports(ConfigAttribute attribute) {
+        Iterator iter = this.decisionVoters.iterator();
+
+        while (iter.hasNext()) {
+            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
+
+            if (voter.supports(attribute)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void checkIfValidList(List listToCheck) {
+        if ((listToCheck == null) || (listToCheck.size() == 0)) {
+            throw new IllegalArgumentException("A list of AccessDecisionVoters is required");
+        }
+    }
+}

+ 96 - 0
core/src/main/java/org/acegisecurity/vote/AccessDecisionVoter.java

@@ -0,0 +1,96 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+
+/**
+ * Indicates a class is responsible for voting on authorization decisions.
+ * 
+ * <p>
+ * The coordination of voting (ie polling <code>AccessDecisionVoter</code>s,
+ * tallying their responses, and making the final authorization decision) is
+ * performed by an {@link net.sf.acegisecurity.AccessDecisionManager}.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AccessDecisionVoter {
+    //~ Static fields/initializers =============================================
+
+    public static final int ACCESS_GRANTED = 1;
+    public static final int ACCESS_ABSTAIN = 0;
+    public static final int ACCESS_DENIED = -1;
+
+    //~ Methods ================================================================
+
+    /**
+     * Indicates whether this <code>AccessDecisionVoter</code> is able to vote
+     * on the passed <code>ConfigAttribute</code>.
+     * 
+     * <p>
+     * This allows the <code>SecurityInterceptor</code> to check every
+     * configuration attribute can be consumed by the configured
+     * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code>.
+     * </p>
+     *
+     * @param attribute a configuration attribute that has been configured
+     *        against the <code>SecurityInterceptor</code>
+     *
+     * @return true if this <code>AccessDecisionVoter</code> can support the
+     *         passed configuration attribute
+     */
+    public boolean supports(ConfigAttribute attribute);
+
+    /**
+     * Indicates whether or not access is granted.
+     * 
+     * <p>
+     * The decision must be affirmative (<code>ACCESS_GRANTED</code>),
+     * negative (<code>ACCESS_DENIED</code>) or the
+     * <code>AccessDecisionVoter</code> can abstain
+     * (<code>ACCESS_ABSTAIN</code>) from voting. Under no circumstances
+     * should implementing classes return any other value. If a weighting of
+     * results is desired, this should be handled in a custom  {@link
+     * net.sf.acegisecurity.AccessDecisionManager} instead.
+     * </p>
+     * 
+     * <P>
+     * Unless an <code>AccessDecisionVoter</code> is specifically intended to
+     * vote on an access control decision due to a passed method invocation or
+     * configuration attribute parameter, it must return
+     * <code>ACCESS_ABSTAIN</code>. This prevents the coordinating
+     * <code>AccessDecisionManager</code> from counting votes from those
+     * <code>AccessDecisionVoter</code>s without a legitimate interest in the
+     * access control decision.
+     * </p>
+     * 
+     * <p>
+     * Whilst the method invocation is passed as a parameter to maximise
+     * flexibility in making access control decisions, implementing classes
+     * must never modify the behaviour of the method invocation (such as
+     * calling <Code>MethodInvocation.proceed()</code>).
+     * </p>
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @return either {@link #ACCESS_GRANTED}, {@link #ACCESS_ABSTAIN} or
+     *         {@link #ACCESS_DENIED}
+     */
+    public int vote(Authentication authentication, MethodInvocation invocation,
+                    ConfigAttributeDefinition config);
+}

+ 92 - 0
core/src/main/java/org/acegisecurity/vote/AffirmativeBased.java

@@ -0,0 +1,92 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Iterator;
+
+
+/**
+ * Simple concrete implementation of  {@link
+ * net.sf.acegisecurity.AccessDecisionManager} that grants access if any
+ * <code>AccessDecisionVoter</code> returns an affirmative response.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AffirmativeBased extends AbstractAccessDecisionManager {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(AffirmativeBased.class);
+
+    //~ Methods ================================================================
+
+    /**
+     * This concrete implementation simply polls all configured  {@link
+     * AccessDecisionVoter}s and grants access if any
+     * <code>AccessDecisionVoter</code> voted affirmatively. Denies access
+     * only if there was a deny vote AND no affirmative votes.
+     * 
+     * <p>
+     * If every <code>AccessDecisionVoter</code> abstained from voting, the
+     * decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
+     * property (defaults to false).
+     * </p>
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @throws AccessDeniedException if access is denied
+     */
+    public void decide(Authentication authentication,
+                       MethodInvocation invocation,
+                       ConfigAttributeDefinition config)
+                throws AccessDeniedException {
+        Iterator iter = this.getDecisionVoters().iterator();
+        int deny = 0;
+
+        while (iter.hasNext()) {
+            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
+            int result = voter.vote(authentication, invocation, config);
+
+            switch (result) {
+            case AccessDecisionVoter.ACCESS_GRANTED:
+                return;
+
+            case AccessDecisionVoter.ACCESS_DENIED:
+                deny++;
+
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        if (deny > 0) {
+            throw new AccessDeniedException("Access is denied.");
+        }
+
+        // To get this far, every AccessDecisionVoter abstained
+        if (this.isAllowIfAllAbstainDecisions()) {
+            return;
+        } else {
+            throw new AccessDeniedException("Access is denied.");
+        }
+    }
+}

+ 127 - 0
core/src/main/java/org/acegisecurity/vote/ConsensusBased.java

@@ -0,0 +1,127 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Iterator;
+
+
+/**
+ * Simple concrete implementation of  {@link
+ * net.sf.acegisecurity.AccessDecisionManager} that uses a  consensus-based
+ * approach.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ConsensusBased extends AbstractAccessDecisionManager {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(ConsensusBased.class);
+
+    //~ Instance fields ========================================================
+
+    private boolean allowIfEqualGrantedDeniedDecisions = true;
+
+    //~ Methods ================================================================
+
+    public void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {
+        this.allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions;
+    }
+
+    public boolean isAllowIfEqualGrantedDeniedDecisions() {
+        return allowIfEqualGrantedDeniedDecisions;
+    }
+
+    /**
+     * This concrete implementation simply polls all configured  {@link
+     * AccessDecisionVoter}s and upon completion determines the consensus of
+     * granted vs denied responses.
+     * 
+     * <p>
+     * If there were an equal number of grant and deny votes, the decision will
+     * be based on the {@link #isAllowIfEqualGrantedDeniedDecisions()}
+     * property (defaults to true).
+     * </p>
+     * 
+     * <p>
+     * If every <code>AccessDecisionVoter</code> abstained from voting, the
+     * decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
+     * property (defaults to false).
+     * </p>
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @throws AccessDeniedException if access is denied
+     */
+    public void decide(Authentication authentication,
+                       MethodInvocation invocation,
+                       ConfigAttributeDefinition config)
+                throws AccessDeniedException {
+        Iterator iter = this.getDecisionVoters().iterator();
+        int grant = 0;
+        int deny = 0;
+        int abstain = 0;
+
+        while (iter.hasNext()) {
+            AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
+            int result = voter.vote(authentication, invocation, config);
+
+            switch (result) {
+            case AccessDecisionVoter.ACCESS_GRANTED:
+                grant++;
+
+                break;
+
+            case AccessDecisionVoter.ACCESS_DENIED:
+                deny++;
+
+                break;
+
+            default:
+                abstain++;
+
+                break;
+            }
+        }
+
+        if (grant > deny) {
+            return;
+        }
+
+        if (deny > grant) {
+            throw new AccessDeniedException("Access is denied.");
+        }
+
+        if ((grant == deny) && (grant != 0)) {
+            if (this.allowIfEqualGrantedDeniedDecisions) {
+                return;
+            } else {
+                throw new AccessDeniedException("Access is denied.");
+            }
+        }
+
+        // To get this far, every AccessDecisionVoter abstained
+        if (this.isAllowIfAllAbstainDecisions()) {
+            return;
+        } else {
+            throw new AccessDeniedException("Access is denied.");
+        }
+    }
+}

+ 76 - 0
core/src/main/java/org/acegisecurity/vote/RoleVoter.java

@@ -0,0 +1,76 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.Iterator;
+
+
+/**
+ * Votes if any {@link ConfigAttribute#getAttribute()} is prefixed with
+ * <Code>ROLE_</code>.
+ * 
+ * <p>
+ * Abstains from voting if no configuration attribute commences with
+ * <code>ROLE_</code>. Votes to grant access if there is an exact matching
+ * {@link net.sf.acegisecurity.GrantedAuthority} to a
+ * <code>ConfigAttribute</code> starting with <code>ROLE_</code>. Votes to
+ * deny access if there is no exact matching <code>GrantedAuthority</code>  to
+ * a <code>ConfigAttribute</code> starting with <code>ROLE_</code>.
+ * </p>
+ * 
+ * <p>
+ * All comparisons and prefixes are case sensitive.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RoleVoter implements AccessDecisionVoter {
+    //~ Methods ================================================================
+
+    public boolean supports(ConfigAttribute attribute) {
+        if ((attribute.getAttribute() != null)
+                && attribute.getAttribute().startsWith("ROLE_")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public int vote(Authentication authentication, MethodInvocation invocation,
+                    ConfigAttributeDefinition config) {
+        int result = ACCESS_ABSTAIN;
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+
+            if (this.supports(attribute)) {
+                result = ACCESS_DENIED;
+
+                // Attempt to find a matching granted authority
+                for (int i = 0; i < authentication.getAuthorities().length;
+                         i++) {
+                    if (attribute.getAttribute().equals(authentication
+                                                            .getAuthorities()[i]
+                                                            .getAuthority())) {
+                        return ACCESS_GRANTED;
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+}

+ 119 - 0
core/src/main/java/org/acegisecurity/vote/UnanimousBased.java

@@ -0,0 +1,119 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Iterator;
+
+
+/**
+ * Simple concrete implementation of  {@link
+ * net.sf.acegisecurity.AccessDecisionManager} that  requires all voters to
+ * abstain or grant access.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class UnanimousBased extends AbstractAccessDecisionManager {
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(UnanimousBased.class);
+
+    //~ Methods ================================================================
+
+    /**
+     * This concrete implementation polls all configured  {@link
+     * AccessDecisionVoter}s for each {@link ConfigAttribute} and grants
+     * access if <b>only</b> grant votes were received.
+     * 
+     * <p>
+     * Other voting implementations usually pass the entire list of {@link
+     * ConfigAttributeDefinition}s to the  <code>AccessDecisionVoter</code>.
+     * This implementation differs in that each
+     * <code>AccessDecisionVoter</code> knows only about a single
+     * <code>ConfigAttribute</code> at a time.
+     * </p>
+     * 
+     * <p>
+     * If every <code>AccessDecisionVoter</code> abstained from voting, the
+     * decision will be based on the {@link #isAllowIfAllAbstainDecisions()}
+     * property (defaults to false).
+     * </p>
+     *
+     * @param authentication the caller invoking the method
+     * @param invocation the method being called
+     * @param config the configuration attributes associated with the method
+     *        being invoked
+     *
+     * @throws AccessDeniedException if access is denied
+     */
+    public void decide(Authentication authentication,
+                       MethodInvocation invocation,
+                       ConfigAttributeDefinition config)
+                throws AccessDeniedException {
+        int grant = 0;
+        int deny = 0;
+        int abstain = 0;
+
+        Iterator configIter = config.getConfigAttributes();
+
+        while (configIter.hasNext()) {
+            ConfigAttributeDefinition thisDef = new ConfigAttributeDefinition();
+            thisDef.addConfigAttribute((ConfigAttribute) configIter.next());
+
+            Iterator voters = this.getDecisionVoters().iterator();
+
+            while (voters.hasNext()) {
+                AccessDecisionVoter voter = (AccessDecisionVoter) voters.next();
+                int result = voter.vote(authentication, invocation, thisDef);
+
+                switch (result) {
+                case AccessDecisionVoter.ACCESS_GRANTED:
+                    grant++;
+
+                    break;
+
+                case AccessDecisionVoter.ACCESS_DENIED:
+                    deny++;
+
+                    break;
+
+                default:
+                    abstain++;
+
+                    break;
+                }
+            }
+        }
+
+        if (deny > 0) {
+            throw new AccessDeniedException("Access is denied.");
+        }
+
+        // To get this far, there were no deny votes
+        if (grant > 0) {
+            return;
+        }
+
+        // To get this far, every AccessDecisionVoter abstained
+        if (this.isAllowIfAllAbstainDecisions()) {
+            return;
+        } else {
+            throw new AccessDeniedException("Access is denied.");
+        }
+    }
+}

+ 6 - 0
core/src/main/java/org/acegisecurity/vote/package.html

@@ -0,0 +1,6 @@
+<html>
+<body>
+Implements a vote-based approach to authorization decisions.
+<p>
+</body>
+</html>

+ 41 - 0
core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ * $Id$
+-->
+
+<beans>
+
+	<!-- ================= CONTAINER ADAPTER CONFIGURATION ================ -->
+	
+	<!-- Data access object which stores authentication information -->
+	<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
+  		<property name="userMap">
+			<value>
+				marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
+				dianne=emu,ROLE_TELLER
+				scott=wombat,ROLE_TELLER
+				peter=opal,disabled,ROLE_TELLER
+			</value>
+		</property>
+	</bean>
+	
+	<!-- Authentication provider that queries our data access object  -->
+	<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
+     	<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
+ 		<property name="ignorePasswordCase"><value>false</value></property>
+ 		<property name="ignoreUsernameCase"><value>true</value></property>
+	</bean>
+
+	<!-- The authentication manager that iterates through our only authentication provider -->
+	<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="daoAuthenticationProvider"/>
+		  </list>
+		</property>
+	</bean>
+
+</beans>

+ 41 - 0
core/src/main/resources/org/acegisecurity/providers/dao/jdbc/acegisecurity-jdbc.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ * $Id$
+-->
+
+<beans>
+
+	<!-- ================= CONTAINER ADAPTER CONFIGURATION ================ -->
+	
+	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+		<property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property>
+		<property name="url"><value>jdbc:hsqldb:hsql://localhost:9001</value></property>
+		<property name="username"><value>sa</value></property>
+		<property name="password"><value></value></property>
+	</bean>
+
+	<!-- Data access object which stores authentication information -->
+	<bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
+  		<property name="dataSource"><ref bean="dataSource"/></property>
+	</bean>
+	
+	<!-- Authentication provider that queries our data access object  -->
+	<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
+     	<property name="authenticationDao"><ref bean="jdbcDaoImpl"/></property>
+ 		<property name="ignorePasswordCase"><value>false</value></property>
+ 		<property name="ignoreUsernameCase"><value>true</value></property>
+	</bean>
+
+	<!-- The authentication manager that iterates through our only authentication provider -->
+	<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="daoAuthenticationProvider"/>
+		  </list>
+		</property>
+	</bean>
+
+</beans>

+ 88 - 0
core/src/test/java/org/acegisecurity/BankSecurityVoter.java

@@ -0,0 +1,88 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import net.sf.acegisecurity.vote.AccessDecisionVoter;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.Iterator;
+
+
+/**
+ * Implementation of an {@link AccessDecisionVoter} that provides  a token
+ * example of application-specific security.
+ * 
+ * <p>
+ * If the {@link ConfigAttribute#getAttribute()} has a value of
+ * <code>BANKSECURITY_CUSTOMER</code>, the account number subject of the
+ * method call to be compared with any granted authority prefixed with
+ * <code>ACCOUNT_</code> and followed by that account number. For example, if
+ * account number 12 was subject of the call, a search would be conducted for
+ * a granted authority named <code>ACCOUNT_12</code>.
+ * </p>
+ * 
+ * <p>
+ * All comparisons are case sensitive.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class BankSecurityVoter implements AccessDecisionVoter {
+    //~ Methods ================================================================
+
+    public boolean supports(ConfigAttribute attribute) {
+        if ("BANKSECURITY_CUSTOMER".equals(attribute.getAttribute())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public int vote(Authentication authentication, MethodInvocation invocation,
+        ConfigAttributeDefinition config) {
+        int result = ACCESS_ABSTAIN;
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+
+            if (this.supports(attribute)) {
+                result = ACCESS_DENIED;
+
+                // Lookup the account number being passed
+                Integer accountNumber = null;
+
+                for (int i = 0; i < invocation.getArgumentCount(); i++) {
+                    Class argClass = invocation.getArgument(i).getClass();
+
+                    if (Integer.class.isAssignableFrom(argClass)) {
+                        accountNumber = (Integer) invocation.getArgument(i);
+                    }
+                }
+
+                if (accountNumber != null) {
+                    // Attempt to find a matching granted authority
+                    String targetAttribute = "ACCOUNT_"
+                        + accountNumber.toString();
+
+                    for (int i = 0; i < authentication.getAuthorities().length;
+                        i++) {
+                        if (targetAttribute.equals(
+                                authentication.getAuthorities()[i].getAuthority())) {
+                            return ACCESS_GRANTED;
+                        }
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+}

+ 43 - 0
core/src/test/java/org/acegisecurity/ExoticSecureContext.java

@@ -0,0 +1,43 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import net.sf.acegisecurity.context.ContextInvalidException;
+import net.sf.acegisecurity.context.SecureContextImpl;
+
+
+/**
+ * Demonstrates subclassing the {@link SecureContextImpl} with
+ * application-specific requirements.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ExoticSecureContext extends SecureContextImpl {
+    //~ Instance fields ========================================================
+
+    private int magicNumber;
+
+    //~ Methods ================================================================
+
+    public void setMagicNumber(int magicNumber) {
+        this.magicNumber = magicNumber;
+    }
+
+    public int getMagicNumber() {
+        return magicNumber;
+    }
+
+    public void validate() throws ContextInvalidException {
+        super.validate();
+
+        if (magicNumber != 7) {
+            throw new ContextInvalidException("Magic number is not 7");
+        }
+    }
+}

+ 217 - 0
core/src/test/java/org/acegisecurity/SecurityTests.java

@@ -0,0 +1,217 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.context.Account;
+import net.sf.acegisecurity.context.BankManager;
+import net.sf.acegisecurity.context.Context;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.ContextImpl;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+
+import org.springframework.beans.factory.BeanCreationException;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+
+/**
+ * Tests security objects.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecurityTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    private ClassPathXmlApplicationContext ctx;
+
+    //~ Constructors ===========================================================
+
+    public SecurityTests() {
+        super();
+    }
+
+    public SecurityTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+        ctx = new ClassPathXmlApplicationContext(
+                "/net/sf/acegisecurity/applicationContext.xml");
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(SecurityTests.class);
+    }
+
+    public void testDetectsInvalidConfigAttribute() throws Exception {
+        try {
+            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
+                    "/net/sf/acegisecurity/badContext.xml");
+            fail("Should have thrown BeanCreationException");
+        } catch (BeanCreationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testSecurityInterceptorCustomVoter() throws Exception {
+        Account marissa = new Account(2, "marissa");
+        BankManager bank = (BankManager) ctx.getBean("bankManager");
+
+        // Indicate the authenticated user holds an account number of 65
+        GrantedAuthority[] useless = {new GrantedAuthorityImpl("ACCOUNT_65")};
+        TestingAuthenticationToken auth = new TestingAuthenticationToken("Peter",
+                "emu", useless);
+        SecureContext secureContext = new SecureContextImpl();
+        secureContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        // Confirm the absence of holding a valid account number rejects access
+        try {
+            bank.saveAccount(marissa);
+            fail("Should have thrown an AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+
+        // Now setup a user with the correct account number
+        GrantedAuthority[] account2 = {new GrantedAuthorityImpl("ACCOUNT_2")};
+        auth = new TestingAuthenticationToken("Kristy", "opal", account2);
+        secureContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        // Check the user can perform operations related to their account number
+        bank.loadAccount(marissa.getId());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testSecurityInterceptorDetectsInvalidContexts()
+        throws Exception {
+        // Normally the security interceptor does not need to detect these conditions,
+        // because the context interceptor should with its validate method. However,
+        // the security interceptor still checks it is passed the correct objects.
+        Account ben = new Account(1, "ben");
+        BankManager bank = (BankManager) ctx.getBean("bankManager");
+
+        // First try with a totally empty ContextHolder
+        try {
+            bank.saveAccount(ben);
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+
+        // Now try with a ContextHolder but of the wrong type (not a SecureContext)
+        Context context = new ContextImpl();
+        ContextHolder.setContext(context);
+
+        try {
+            bank.saveAccount(ben);
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+
+        // Next try with a SecureContext but without an authentication object in it
+        SecureContext secureContext = new SecureContextImpl();
+        ContextHolder.setContext((Context) secureContext);
+
+        try {
+            bank.saveAccount(ben);
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+
+        // Now try with a SecureContext, correctly setup, which should work
+        GrantedAuthority[] granted = {new GrantedAuthorityImpl(
+                "ROLE_SUPERVISOR")};
+        TestingAuthenticationToken auth = new TestingAuthenticationToken("Jeni",
+                "kangaroo", granted);
+        secureContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        Account marissa = new Account(2, "marissa");
+        marissa.deposit(2000);
+        bank.saveAccount(marissa);
+        assertTrue(2000 == bank.getBalance(marissa.getId()));
+
+        // Now confirm if we subclass SecureContextImpl it still works.
+        // Note the validate method in our ExoticSecureContext will not be
+        // called, as we do not have the context interceptor defined.
+        ExoticSecureContext exoticContext = new ExoticSecureContext();
+        exoticContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        Account scott = new Account(3, "scott");
+        scott.deposit(50);
+        bank.saveAccount(scott);
+        assertTrue(50 == bank.getBalance(scott.getId()));
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testSecurityInterceptorEnforcesRoles()
+        throws Exception {
+        Account ben = new Account(1, "ben");
+        ben.deposit(25);
+
+        BankManager bank = (BankManager) ctx.getBean("bankManager");
+
+        // Indicate the authenticated user holds a role that is not useful
+        GrantedAuthority[] useless = {new GrantedAuthorityImpl(
+                "ROLE_NOTHING_USEFUL")};
+        TestingAuthenticationToken auth = new TestingAuthenticationToken("George",
+                "koala", useless);
+        SecureContext secureContext = new SecureContextImpl();
+        secureContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        // Confirm the absence of holding a valid role rejects access
+        try {
+            bank.saveAccount(ben);
+            fail("Should have thrown an AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+
+        // Now try to call a public method (getBankFundsUnderControl)
+        bank.getBankFundsUnderControl();
+
+        // Now setup a user with only a teller role
+        GrantedAuthority[] teller = {new GrantedAuthorityImpl("ROLE_TELLER")};
+        auth = new TestingAuthenticationToken("Michelle", "wombat", teller);
+        secureContext.setAuthentication(auth);
+        ContextHolder.setContext((Context) secureContext);
+
+        // Confirm the absence of ROLE_SUPERVISOR prevents calling deleteAccount
+        try {
+            bank.deleteAccount(ben.getId());
+            fail("Should have thrown an AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+
+        // Check the teller can perform ROLE_TELLER secured operations
+        bank.saveAccount(ben);
+        assertTrue(25 == bank.getBalance(ben.getId()));
+
+        ContextHolder.setContext(null);
+    }
+}

+ 76 - 0
core/src/test/java/org/acegisecurity/adapters/AuthByAdapterTests.java

@@ -0,0 +1,76 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.adapters;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationManager;
+import net.sf.acegisecurity.adapters.jetty.JettyAcegiUserToken;
+import net.sf.acegisecurity.providers.ProviderNotFoundException;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+
+/**
+ * Tests {@link AuthByAdapterProvider}
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthByAdapterTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    private ClassPathXmlApplicationContext ctx;
+
+    //~ Constructors ===========================================================
+
+    public AuthByAdapterTests() {
+        super();
+    }
+
+    public AuthByAdapterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+        ctx = new ClassPathXmlApplicationContext(
+                "/net/sf/acegisecurity/adapters/applicationContext.xml");
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthByAdapterTests.class);
+    }
+
+    public void testAdapterProvider() throws Exception {
+        AuthenticationManager authMgr = (AuthenticationManager) ctx.getBean(
+                "providerManager");
+
+        // Should authenticate as JettySpringUser is interface of AuthByAdapter
+        JettyAcegiUserToken jetty = new JettyAcegiUserToken("my_password",
+                "Test", "Password", null);
+        Authentication response = authMgr.authenticate(jetty);
+        jetty = null;
+        assertTrue(true);
+
+        // Should fail as UsernamePassword is not interface of AuthByAdapter
+        UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken("Test",
+                "Password");
+
+        try {
+            Authentication response2 = authMgr.authenticate(user);
+            fail("Should have thrown ProviderNotFoundException");
+        } catch (ProviderNotFoundException expected) {
+            assertTrue(true);
+        }
+    }
+}

+ 29 - 0
core/src/test/java/org/acegisecurity/adapters/applicationContext.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ * $Id$
+-->
+
+<beans>
+
+	<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
+	
+	<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
+
+	<!-- Authentication provider that accepts as valid any adapter-created Authentication token  -->
+	<bean id="authByAdapterProvider" class="net.sf.acegisecurity.adapters.AuthByAdapterProvider">
+  		<property name="key"><value>my_password</value></property>
+ 	</bean>
+
+	<!-- The authentication manager that iterates through our authentication providers -->
+	<bean id="providerManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="authByAdapterProvider"/>
+		  </list>
+		</property>
+	</bean>
+
+</beans>

+ 84 - 0
core/src/test/java/org/acegisecurity/applicationContext.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ * $Id$
+-->
+
+<beans>
+
+	<!-- =================== SECURITY SYSTEM DEFINITIONS ================== -->
+	
+	<!-- RunAsManager -->
+	<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
+     	<property name="key"><value>my_run_as_password</value></property>
+ 	</bean>
+
+	<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHENTICATION DEFINITIONS ~~~~~~~~~~~~~~~~~~ -->
+
+	<!-- This authentication provider accepts any presented TestingAuthenticationToken -->
+	<bean id="testingAuthenticationProvider" class="net.sf.acegisecurity.providers.TestingAuthenticationProvider"/>
+
+	<!-- The authentication manager that iterates through our only authentication provider -->
+	<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="testingAuthenticationProvider"/>
+		  </list>
+		</property>
+	</bean>
+
+	<!-- ~~~~~~~~~~~~~~~~~~~~ AUTHORIZATION DEFINITIONS ~~~~~~~~~~~~~~~~~~~ -->
+
+	<!-- An access decision voter that reads ROLE_* configuaration settings -->
+	<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
+
+	<!-- An access decision voter that reads BANKSECURITY_CUSTOMER configuaration settings -->
+	<bean id="bankSecurityVoter" class="net.sf.acegisecurity.BankSecurityVoter"/>
+
+	<!-- An affirmative access decision manager -->
+	<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
+   		<property name="allowIfAllAbstainDecisions"><value>false</value></property>
+		<property name="decisionVoters">
+		  <list>
+		    <ref bean="roleVoter"/>
+		    <ref bean="bankSecurityVoter"/>
+		  </list>
+		</property>
+	</bean>
+
+	<!-- ===================== SECURITY DEFINITIONS ======================= -->
+	
+	<!-- No declaration for BankManager.getBankFundsUnderControl() makes it public -->
+	<bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor">
+    	<property name="authenticationManager"><ref bean="authenticationManager"/></property>
+    	<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
+    	<property name="runAsManager"><ref bean="runAsManager"/></property>
+ 		<property name="methodDefinitionSource">
+			<value>
+				net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR
+				net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
+				net.sf.acegisecurity.context.BankManager.loadAccount=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER
+				net.sf.acegisecurity.context.BankManager.saveAccount=ROLE_TELLER,ROLE_SUPERVISOR
+				net.sf.acegisecurity.context.BankManager.transferFunds=ROLE_SUPERVISOR
+			</value>
+		</property>
+	</bean>
+
+	<!-- ======================= BUSINESS DEFINITIONS ===================== -->
+
+	<bean id="bankManagerTarget" class="net.sf.acegisecurity.context.BankManagerImpl"/>
+
+	<!-- We don't include any context interceptor, although we should do so prior to the security interceptor -->
+	<bean id="bankManager" class="org.springframework.aop.framework.ProxyFactoryBean">
+    	<property name="proxyInterfaces"><value>net.sf.acegisecurity.context.BankManager</value></property>
+	    <property name="interceptorNames">
+      	<list>
+        	<value>bankManagerSecurity</value>
+ 	        <value>bankManagerTarget</value>
+    	</list>
+	    </property>
+  	</bean>
+
+</beans>

+ 141 - 0
core/src/test/java/org/acegisecurity/attribute/AttributesTests.java

@@ -0,0 +1,141 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.attribute;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.MethodDefinitionAttributes;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.lang.reflect.Method;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author CameronBraid
+ */
+public class AttributesTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    ClassPathXmlApplicationContext applicationContext;
+
+    //~ Constructors ===========================================================
+
+    /**
+     *
+     */
+    public AttributesTests(String a) {
+        super(a);
+    }
+
+    //~ Methods ================================================================
+
+    public void testAttributesForImpl() throws Exception {
+        ConfigAttributeDefinition def = getConfigAttributeDefinition(TestServiceImpl.class);
+        Set set = toSet(def);
+        assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE")));
+        assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE_METHOD")));
+
+        assertTrue(set.contains(new SecurityConfig("ROLE_CLASS")));
+        assertTrue(set.contains(new SecurityConfig("ROLE_CLASS_METHOD")));
+    }
+
+    public void testAttributesForInterface() throws Exception {
+        ConfigAttributeDefinition def = getConfigAttributeDefinition(TestService.class);
+        Set set = toSet(def);
+        System.out.println(set.toString());
+        assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE")));
+        assertTrue(set.contains(new SecurityConfig("ROLE_INTERFACE_METHOD")));
+    }
+
+    public void testInterceptionWithMockAttributesAndSecureContext()
+        throws Exception {
+        applicationContext = new ClassPathXmlApplicationContext(
+                "/net/sf/acegisecurity/attribute/applicationContext.xml");
+
+        TestService service = (TestService) applicationContext.getBean(
+                "testService");
+
+        SecureContextImpl context = new SecureContextImpl();
+        ContextHolder.setContext(context);
+
+        Authentication auth;
+
+        auth = new TestingAuthenticationToken("test", "test",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_CLASS"), new GrantedAuthorityImpl(
+                        "ROLE_INTERFACE"), new GrantedAuthorityImpl(
+                        "ROLE_CLASS_METHOD"), new GrantedAuthorityImpl(
+                        "ROLE_INTERFACE_METHOD")});
+
+        context.setAuthentication(auth);
+        service.myMethod();
+
+        auth = new TestingAuthenticationToken("test", "test",
+                new GrantedAuthority[] {});
+        context.setAuthentication(auth);
+
+        try {
+            service.myMethod();
+            fail(
+                "security interceptor should have detected insufficient permissions");
+        } catch (Exception e) {}
+
+        applicationContext.close();
+        ContextHolder.setContext(null);
+    }
+
+    private ConfigAttributeDefinition getConfigAttributeDefinition(Class clazz)
+        throws Exception {
+        final Method method = clazz.getMethod("myMethod", null);
+        MethodDefinitionAttributes source = new MethodDefinitionAttributes();
+        source.setAttributes(new TestAttributes());
+
+        ConfigAttributeDefinition config = source.getAttributes(new MockMethodInvocation() {
+                    public Method getMethod() {
+                        return method;
+                    }
+                });
+
+        return config;
+    }
+
+    /**
+     * convert a ConfigAttributeDefinition into a set of
+     * <code>ConfigAttribute</code>(s)
+     *
+     * @param def DOCUMENT ME!
+     *
+     * @return
+     */
+    private Set toSet(ConfigAttributeDefinition def) {
+        Set set = new HashSet();
+        Iterator i = def.getConfigAttributes();
+
+        while (i.hasNext()) {
+            ConfigAttribute a = (ConfigAttribute) i.next();
+            set.add(a);
+        }
+
+        return set;
+    }
+}

+ 67 - 0
core/src/test/java/org/acegisecurity/attribute/MockAttributes.java

@@ -0,0 +1,67 @@
+/*
+ * The Acegi Security System for Spring is published under the terms
+ * of the Apache Software License.
+ *
+ * Visit http://acegisecurity.sourceforge.net for further details.
+ */
+
+package net.sf.acegisecurity.attribute;
+
+import org.springframework.metadata.Attributes;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import java.util.Collection;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author CameronBraid
+ */
+public class MockAttributes implements Attributes {
+    //~ Methods ================================================================
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class, java.lang.Class)
+     */
+    public Collection getAttributes(Class arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class)
+     */
+    public Collection getAttributes(Class arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field, java.lang.Class)
+     */
+    public Collection getAttributes(Field arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field)
+     */
+    public Collection getAttributes(Field arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method, java.lang.Class)
+     */
+    public Collection getAttributes(Method arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method)
+     */
+    public Collection getAttributes(Method arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است