Sfoglia il codice sorgente

Refactor Authentication.isAuthenticated() handling to be more performance (as per developer list discussion).

Ben Alex 20 anni fa
parent
commit
5f75e9bf9a
16 ha cambiato i file con 209 aggiunte e 152 eliminazioni
  1. 46 7
      core/src/main/java/org/acegisecurity/Authentication.java
  2. 15 0
      core/src/main/java/org/acegisecurity/context/rmi/ContextPropagatingRemoteInvocation.java
  3. 29 17
      core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java
  4. 30 15
      core/src/main/java/org/acegisecurity/providers/UsernamePasswordAuthenticationToken.java
  5. 9 17
      core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousAuthenticationToken.java
  6. 10 18
      core/src/main/java/org/acegisecurity/providers/cas/CasAuthenticationToken.java
  7. 10 8
      core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java
  8. 4 12
      core/src/main/java/org/acegisecurity/providers/rememberme/RememberMeAuthenticationToken.java
  9. 5 13
      core/src/main/java/org/acegisecurity/runas/RunAsUserToken.java
  10. 7 7
      core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodSecurityInterceptorTests.java
  11. 24 16
      core/src/test/java/org/acegisecurity/providers/UsernamePasswordAuthenticationTokenTests.java
  12. 2 3
      core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousAuthenticationTokenTests.java
  13. 3 4
      core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java
  14. 2 3
      core/src/test/java/org/acegisecurity/providers/rememberme/RememberMeAuthenticationTokenTests.java
  15. 3 3
      core/src/test/java/org/acegisecurity/runas/RunAsUserTokenTests.java
  16. 10 9
      doc/xdocs/changes.xml

+ 46 - 7
core/src/main/java/org/acegisecurity/Authentication.java

@@ -39,16 +39,55 @@ import java.security.Principal;
 public interface Authentication extends Principal, Serializable {
     //~ Methods ================================================================
 
-    public void setAuthenticated(boolean isAuthenticated);
+    /**
+     * See {@link #isAuthenticated()} for a full description.
+     * 
+     * <p>
+     * Implementations should <b>always</b> allow this method to be called with
+     * a <code>false</code> parameter, as this is used by various classes to
+     * specify the authentication token should not be trusted. If an
+     * implementation wishes to reject an invocation with a <code>true</code>
+     * parameter (which would indicate the authentication token is trusted - a
+     * potential security risk) the implementation should throw an {@link
+     * IllegalArgumentException}.
+     * </p>
+     *
+     * @param isAuthenticated <code>true</code> if the token should be trusted
+     *        (which may result in an exception) or <code>false</code> if the
+     *        token should not be trusted
+     *
+     * @throws IllegalArgumentException if an attempt to make the
+     *         authentication token trusted (by passing <code>true</code> as
+     *         the argument) is rejected due to the implementation being
+     *         immutable or implementing its own alternative approach to
+     *         {@link #isAuthenticated()}
+     */
+    public void setAuthenticated(boolean isAuthenticated)
+        throws IllegalArgumentException;
 
     /**
-     * Indicates whether or not authentication was attempted by the {@link
-     * net.sf.acegisecurity.intercept.AbstractSecurityInterceptor}. Note that
-     * classes should not rely on this value as being valid unless it has been
-     * set by a trusted <code>AbstractSecurityInterceptor</code>.
+     * Used to indicate to <code>AbstractSecurityInterceptor</code> whether it
+     * should present the authentication token to the
+     * <code>AuthenticationManager</code>. Typically an
+     * <code>AuthenticationManager</code> (or, more often, one of its
+     * <code>AuthenticationProvider</code>s) will return an immutable
+     * authentication token after successful authentication, in which case
+     * that token can safely return <code>true</code> to this method.
+     * Returning <code>true</code> will improve performance, as calling the
+     * <code>AuthenticationManager</code> for every request will no longer be
+     * necessary.
+     * 
+     * <p>
+     * For security reasons, implementations of this interface should be very
+     * careful about returning <code>true</code> to this method unless they
+     * are either immutable, or have some way of ensuring the properties have
+     * not been changed since original creation.
+     * </p>
      *
-     * @return true if authenticated by the
-     *         <code>AbstractSecurityInterceptor</code>
+     * @return true if the token has been authenticated and the
+     *         <code>AbstractSecurityInterceptor</code> does not need to
+     *         represent the token for re-authentication to the
+     *         <code>AuthenticationManager</code>
      */
     public boolean isAuthenticated();
 

+ 15 - 0
core/src/main/java/org/acegisecurity/context/rmi/ContextPropagatingRemoteInvocation.java

@@ -82,6 +82,15 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
 
     /**
      * Invoked on the server-side as described in the class JavaDocs.
+     * 
+     * <p>
+     * Invocations will always have their {@link
+     * net.sf.acegisecurity.Authentication#setAuthenticated(boolean)} set to
+     * <code>false</code>, which is guaranteed to always be accepted by
+     * <code>Authentication</code> implementations. This ensures that even
+     * remotely authenticated <code>Authentication</code>s will be untrusted
+     * by the server-side, which is an appropriate security measure.
+     * </p>
      *
      * @param targetObject the target object to apply the invocation to
      *
@@ -97,6 +106,12 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
             InvocationTargetException {
         SecurityContextHolder.setContext(securityContext);
 
+        if ((SecurityContextHolder.getContext() != null)
+            && (SecurityContextHolder.getContext().getAuthentication() != null)) {
+            SecurityContextHolder.getContext().getAuthentication()
+                                 .setAuthenticated(false);
+        }
+
         if (logger.isDebugEnabled()) {
             logger.debug("Set SecurityContextHolder to contain: "
                 + securityContext);

+ 29 - 17
core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java

@@ -364,30 +364,42 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
                     object, attr);
             }
 
-            // Attempt authentication
+            // Attempt authentication if not already authenticated
             Authentication authenticated;
 
-            try {
-                authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
-                                                                                             .getAuthentication());
-            } catch (AuthenticationException authenticationException) {
-                AuthenticationFailureEvent event = new AuthenticationFailureEvent(object,
-                        attr,
-                        SecurityContextHolder.getContext().getAuthentication(),
-                        authenticationException);
-                this.context.publishEvent(event);
+            if (!SecurityContextHolder.getContext().getAuthentication()
+                                      .isAuthenticated()) {
+                try {
+                    authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
+                                                                                                 .getAuthentication());
+                } catch (AuthenticationException authenticationException) {
+                    AuthenticationFailureEvent event = new AuthenticationFailureEvent(object,
+                            attr,
+                            SecurityContextHolder.getContext()
+                                                 .getAuthentication(),
+                            authenticationException);
+                    this.context.publishEvent(event);
+
+                    throw authenticationException;
+                }
 
-                throw authenticationException;
-            }
+                // We don't authenticated.setAuthentication(true), because each provider should do that
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Successfully Authenticated: "
+                        + authenticated.toString());
+                }
 
-            authenticated.setAuthenticated(true);
+                SecurityContextHolder.getContext().setAuthentication(authenticated);
+            } else {
+                authenticated = SecurityContextHolder.getContext()
+                                                     .getAuthentication();
 
-            if (logger.isDebugEnabled()) {
-                logger.debug("Authenticated: " + authenticated.toString());
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Previously Authenticated: "
+                        + authenticated.toString());
+                }
             }
 
-            SecurityContextHolder.getContext().setAuthentication(authenticated);
-
             // Attempt authorization
             try {
                 this.accessDecisionManager.decide(authenticated, object, attr);

+ 30 - 15
core/src/main/java/org/acegisecurity/providers/UsernamePasswordAuthenticationToken.java

@@ -1,4 +1,4 @@
-/* Copyright 2004 Acegi Technology Pty Limited
+/* Copyright 2004, 2005 Acegi Technology Pty Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,21 +40,42 @@ public class UsernamePasswordAuthenticationToken
     private Object details = null;
     private Object principal;
     private GrantedAuthority[] authorities;
-    private boolean authenticated = false;
+    private boolean authenticated;
 
     //~ Constructors ===========================================================
 
+    /**
+     * This constructor can be safely used by any code that wishes to create a
+     * <code>UsernamePasswordAuthenticationToken</code>, as the {@link
+     * #isAuthenticated()} will return <code>false</code>.
+     *
+     * @param principal DOCUMENT ME!
+     * @param credentials DOCUMENT ME!
+     */
     public UsernamePasswordAuthenticationToken(Object principal,
         Object credentials) {
         this.principal = principal;
         this.credentials = credentials;
+        this.authenticated = false;
     }
 
+    /**
+     * This constructor should only be used by
+     * <code>AuthenticationManager</code> or
+     * <code>AuthenticationProvider</code> implementations that are satisfied
+     * with producing a trusted (ie {@link #isAuthenticated()} =
+     * <code>true</code>) authentication token.
+     *
+     * @param principal
+     * @param credentials
+     * @param authorities
+     */
     public UsernamePasswordAuthenticationToken(Object principal,
         Object credentials, GrantedAuthority[] authorities) {
         this.principal = principal;
         this.credentials = credentials;
         this.authorities = authorities;
+        this.authenticated = true;
     }
 
     protected UsernamePasswordAuthenticationToken() {
@@ -63,7 +84,13 @@ public class UsernamePasswordAuthenticationToken
 
     //~ Methods ================================================================
 
-    public void setAuthenticated(boolean isAuthenticated) {
+    public void setAuthenticated(boolean isAuthenticated)
+        throws IllegalArgumentException {
+        if (isAuthenticated) {
+            throw new IllegalArgumentException(
+                "Cannot set this token to trusted - use constructor containing GrantedAuthority[]s instead");
+        }
+
         this.authenticated = isAuthenticated;
     }
 
@@ -71,18 +98,6 @@ public class UsernamePasswordAuthenticationToken
         return this.authenticated;
     }
 
-    /**
-     * Generally you should not call this method, because on subsequent
-     * requests the <code>Authentication</code> will be recreated by the
-     * relevant <code>AuthenticationManager</code>. This method is mostly of
-     * interest to <code>AuthenticationManager</code>s and unit tests.
-     *
-     * @param authorities the new authorities to apply
-     */
-    public void setAuthorities(GrantedAuthority[] authorities) {
-        this.authorities = authorities;
-    }
-
     public GrantedAuthority[] getAuthorities() {
         return this.authorities;
     }

+ 9 - 17
core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousAuthenticationToken.java

@@ -18,10 +18,10 @@ package net.sf.acegisecurity.providers.anonymous;
 import net.sf.acegisecurity.GrantedAuthority;
 import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
 
-import java.io.Serializable;
-
 import org.springframework.util.Assert;
 
+import java.io.Serializable;
+
 
 /**
  * Represents an anonymous <code>Authentication</code>.
@@ -35,6 +35,7 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
 
     private Object principal;
     private GrantedAuthority[] authorities;
+    private boolean authenticated;
     private int keyHash;
 
     //~ Constructors ===========================================================
@@ -58,14 +59,15 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
         }
 
         for (int i = 0; i < authorities.length; i++) {
-            Assert.notNull(authorities[i], "Granted authority element "
-                    + i
-                    + " is null - GrantedAuthority[] cannot contain any null elements");
+            Assert.notNull(authorities[i],
+                "Granted authority element " + i
+                + " is null - GrantedAuthority[] cannot contain any null elements");
         }
 
         this.keyHash = key.hashCode();
         this.principal = principal;
         this.authorities = authorities;
+		this.authenticated = true;
     }
 
     protected AnonymousAuthenticationToken() {
@@ -74,22 +76,12 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
 
     //~ Methods ================================================================
 
-    /**
-     * Ignored (always <code>true</code>).
-     *
-     * @param isAuthenticated ignored
-     */
     public void setAuthenticated(boolean isAuthenticated) {
-        // ignored
+        this.authenticated = isAuthenticated;
     }
 
-    /**
-     * Always returns <code>true</code>.
-     *
-     * @return true
-     */
     public boolean isAuthenticated() {
-        return true;
+        return this.authenticated;
     }
 
     public GrantedAuthority[] getAuthorities() {

+ 10 - 18
core/src/main/java/org/acegisecurity/providers/cas/CasAuthenticationToken.java

@@ -1,4 +1,4 @@
-/* Copyright 2004 Acegi Technology Pty Limited
+/* Copyright 2004, 2005 Acegi Technology Pty Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,12 +19,12 @@ import net.sf.acegisecurity.GrantedAuthority;
 import net.sf.acegisecurity.UserDetails;
 import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
 
+import org.springframework.util.Assert;
+
 import java.io.Serializable;
 
 import java.util.List;
 
-import org.springframework.util.Assert;
-
 
 /**
  * Represents a successful CAS <code>Authentication</code>.
@@ -42,6 +42,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
     private String proxyGrantingTicketIou;
     private UserDetails userDetails;
     private GrantedAuthority[] authorities;
+    private boolean authenticated;
     private int keyHash;
 
     //~ Constructors ===========================================================
@@ -79,9 +80,9 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
         }
 
         for (int i = 0; i < authorities.length; i++) {
-            Assert.notNull(authorities[i], "Granted authority element "
-                    + i
-                    + " is null - GrantedAuthority[] cannot contain any null elements");
+            Assert.notNull(authorities[i],
+                "Granted authority element " + i
+                + " is null - GrantedAuthority[] cannot contain any null elements");
         }
 
         this.keyHash = key.hashCode();
@@ -91,6 +92,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
         this.userDetails = userDetails;
         this.proxyList = proxyList;
         this.proxyGrantingTicketIou = proxyGrantingTicketIou;
+        this.authenticated = true;
     }
 
     protected CasAuthenticationToken() {
@@ -99,22 +101,12 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
 
     //~ Methods ================================================================
 
-    /**
-     * Ignored (always <code>true</code>).
-     *
-     * @param isAuthenticated ignored
-     */
     public void setAuthenticated(boolean isAuthenticated) {
-        // ignored
+        this.authenticated = isAuthenticated;
     }
 
-    /**
-     * Always returns <code>true</code>.
-     *
-     * @return true
-     */
     public boolean isAuthenticated() {
-        return true;
+        return this.authenticated;
     }
 
     public GrantedAuthority[] getAuthorities() {

+ 10 - 8
core/src/main/java/org/acegisecurity/providers/jaas/JaasAuthenticationProvider.java

@@ -327,7 +327,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
     public Authentication authenticate(Authentication auth)
         throws AuthenticationException {
         if (auth instanceof UsernamePasswordAuthenticationToken) {
-            UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) auth;
+            UsernamePasswordAuthenticationToken request = (UsernamePasswordAuthenticationToken) auth;
 
             try {
                 //Create the LoginContext object, and pass our InternallCallbackHandler
@@ -340,8 +340,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
                 //create a set to hold the authorities, and add any that have already been applied.
                 Set authorities = new HashSet();
 
-                if (token.getAuthorities() != null) {
-                    authorities.addAll(Arrays.asList(token.getAuthorities()));
+                if (request.getAuthorities() != null) {
+                    authorities.addAll(Arrays.asList(request.getAuthorities()));
                 }
 
                 //get the subject principals and pass them to each of the AuthorityGranters
@@ -368,19 +368,21 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
                 }
 
                 //Convert the authorities set back to an array and apply it to the token.
-                token.setAuthorities((GrantedAuthority[]) authorities.toArray(
-                        new GrantedAuthority[authorities.size()]));
+                UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(request
+                        .getPrincipal(), request.getCredentials(),
+                        (GrantedAuthority[]) authorities.toArray(
+                            new GrantedAuthority[authorities.size()]));
 
                 //Publish the success event
-                publishSuccessEvent(token);
+                publishSuccessEvent(result);
 
                 //we're done, return the token.
-                return token;
+                return result;
             } catch (LoginException loginException) {
                 AcegiSecurityException ase = loginExceptionResolver
                     .resolveException(loginException);
 
-                publishFailureEvent(token, ase);
+                publishFailureEvent(request, ase);
                 throw ase;
             }
         }

+ 4 - 12
core/src/main/java/org/acegisecurity/providers/rememberme/RememberMeAuthenticationToken.java

@@ -42,6 +42,7 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
     private Object principal;
     private GrantedAuthority[] authorities;
     private int keyHash;
+	private boolean authenticated;
 
     //~ Constructors ===========================================================
 
@@ -72,6 +73,7 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
         this.keyHash = key.hashCode();
         this.principal = principal;
         this.authorities = authorities;
+		this.authenticated = true;
     }
 
     protected RememberMeAuthenticationToken() {
@@ -80,22 +82,12 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
 
     //~ Methods ================================================================
 
-    /**
-     * Ignored (always <code>true</code>).
-     *
-     * @param isAuthenticated ignored
-     */
     public void setAuthenticated(boolean isAuthenticated) {
-        // ignored
+        this.authenticated = isAuthenticated;
     }
 
-    /**
-     * Always returns <code>true</code>.
-     *
-     * @return true
-     */
     public boolean isAuthenticated() {
-        return true;
+        return this.authenticated;
     }
 
     public GrantedAuthority[] getAuthorities() {

+ 5 - 13
core/src/main/java/org/acegisecurity/runas/RunAsUserToken.java

@@ -34,6 +34,7 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
     private Object principal;
     private GrantedAuthority[] authorities;
     private int keyHash;
+	private boolean authenticated;
 
     //~ Constructors ===========================================================
 
@@ -45,6 +46,7 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
         this.principal = principal;
         this.credentials = credentials;
         this.originalAuthentication = originalAuthentication;
+		this.authenticated = true;
     }
 
     protected RunAsUserToken() {
@@ -53,22 +55,12 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
 
     //~ Methods ================================================================
 
-    /**
-     * Setting is ignored. Always considered authenticated.
-     *
-     * @param ignored DOCUMENT ME!
-     */
-    public void setAuthenticated(boolean ignored) {
-        // ignored
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.authenticated = isAuthenticated;
     }
 
-    /**
-     * Always returns <code>true</code>.
-     *
-     * @return DOCUMENT ME!
-     */
     public boolean isAuthenticated() {
-        return true;
+        return this.authenticated;
     }
 
     public GrantedAuthority[] getAuthorities() {

+ 7 - 7
core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodSecurityInterceptorTests.java

@@ -83,14 +83,14 @@ public class MethodSecurityInterceptorTests extends TestCase {
         SecurityContextHolder.getContext().setAuthentication(null);
     }
 
-    public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillProperlySetItsIsAuthenticatedProperty()
+    public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillNotChangeItsIsAuthenticatedProperty()
         throws Exception {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
-                "Password",
-                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_THIS_IS_NOT_REQUIRED_AS_IT_IS_PUBLIC")});
+                "Password");
         assertTrue(!token.isAuthenticated());
         SecurityContextHolder.getContext().setAuthentication(token);
 
+        // The associated MockAuthenticationManager WILL accept the above UsernamePasswordAuthenticationToken
         ITargetObject target = makeInterceptedTarget();
         String result = target.publicMakeLowerCase("HELLO");
         assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken false",
@@ -158,13 +158,13 @@ public class MethodSecurityInterceptorTests extends TestCase {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
                 "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_LOWER")});
-        assertTrue(!token.isAuthenticated());
+        assertTrue(token.isAuthenticated());
         SecurityContextHolder.getContext().setAuthentication(token);
 
         ITargetObject target = makeInterceptedTargetWithoutAnAfterInvocationManager();
         String result = target.makeLowerCase("HELLO");
 
-        // Note we check the isAuthenticated becomes true in following line
+        // Note we check the isAuthenticated remained true in following line
         assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken true",
             result);
 
@@ -203,11 +203,11 @@ public class MethodSecurityInterceptorTests extends TestCase {
     public void testRejectsCallsWhenAuthenticationIsIncorrect()
         throws Exception {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
-                "Password",
-                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_LOWER")});
+                "Password");
         assertTrue(!token.isAuthenticated());
         SecurityContextHolder.getContext().setAuthentication(token);
 
+        // NB: The associated MockAuthenticationManager WILL reject the above UsernamePasswordAuthenticationToken
         ITargetObject target = makeInterceptedTargetRejectsAuthentication();
 
         try {

+ 24 - 16
core/src/test/java/org/acegisecurity/providers/UsernamePasswordAuthenticationTokenTests.java

@@ -1,4 +1,4 @@
-/* Copyright 2004 Acegi Technology Pty Limited
+/* Copyright 2004, 2005 Acegi Technology Pty Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -51,9 +51,30 @@ public class UsernamePasswordAuthenticationTokenTests extends TestCase {
     public void testAuthenticated() {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
                 "Password", null);
-        assertTrue(!token.isAuthenticated());
-        token.setAuthenticated(true);
+
+        // check default given we passed some GrantedAuthorty[]s (well, we passed null)
         assertTrue(token.isAuthenticated());
+
+        // check explicit set to untrusted (we can safely go from trusted to untrusted, but not the reverse)
+        token.setAuthenticated(false);
+        assertTrue(!token.isAuthenticated());
+
+        // Now let's create a UsernamePasswordAuthenticationToken without any GrantedAuthorty[]s (different constructor)
+        token = new UsernamePasswordAuthenticationToken("Test", "Password");
+
+        assertTrue(!token.isAuthenticated());
+
+        // check we're allowed to still set it to untrusted
+        token.setAuthenticated(false);
+        assertTrue(!token.isAuthenticated());
+
+        // check denied changing it to trusted
+        try {
+            token.setAuthenticated(true);
+            fail("Should have prohibited setAuthenticated(true)");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
     }
 
     public void testGetters() {
@@ -67,19 +88,6 @@ public class UsernamePasswordAuthenticationTokenTests extends TestCase {
         assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
     }
 
-    public void testNewAuthorities() {
-        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
-                "Password", null);
-        assertEquals("Test", token.getPrincipal());
-        assertEquals("Password", token.getCredentials());
-        assertEquals(null, token.getAuthorities());
-
-        token.setAuthorities(new GrantedAuthority[] {new GrantedAuthorityImpl(
-                    "ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")});
-        assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority());
-        assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
-    }
-
     public void testNoArgConstructor() {
         try {
             new UsernamePasswordAuthenticationToken();

+ 2 - 3
core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousAuthenticationTokenTests.java

@@ -159,7 +159,6 @@ public class AnonymousAuthenticationTokenTests extends TestCase {
                 "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")});
-        token2.setAuthenticated(true);
 
         assertFalse(token1.equals(token2));
     }
@@ -184,7 +183,7 @@ public class AnonymousAuthenticationTokenTests extends TestCase {
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")});
         assertTrue(token.isAuthenticated());
-        token.setAuthenticated(false); // ignored
-        assertTrue(token.isAuthenticated());
+        token.setAuthenticated(false);
+        assertTrue(!token.isAuthenticated());
     }
 }

+ 3 - 4
core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java

@@ -223,7 +223,6 @@ public class CasAuthenticationTokenTests extends TestCase {
                 "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")});
-        token2.setAuthenticated(true);
 
         assertTrue(!token1.equals(token2));
     }
@@ -295,15 +294,15 @@ public class CasAuthenticationTokenTests extends TestCase {
         assertTrue(!token1.equals(token2));
     }
 
-    public void testSetAuthenticatedIgnored() {
+    public void testSetAuthenticated() {
         CasAuthenticationToken token = new CasAuthenticationToken("key",
                 "Test", "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")}, makeUserDetails(), new Vector(),
                 "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
         assertTrue(token.isAuthenticated());
-        token.setAuthenticated(false); // ignored
-        assertTrue(token.isAuthenticated());
+        token.setAuthenticated(false);
+        assertTrue(!token.isAuthenticated());
     }
 
     public void testToString() {

+ 2 - 3
core/src/test/java/org/acegisecurity/providers/rememberme/RememberMeAuthenticationTokenTests.java

@@ -159,7 +159,6 @@ public class RememberMeAuthenticationTokenTests extends TestCase {
                 "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")});
-        token2.setAuthenticated(true);
 
         assertFalse(token1.equals(token2));
     }
@@ -184,7 +183,7 @@ public class RememberMeAuthenticationTokenTests extends TestCase {
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")});
         assertTrue(token.isAuthenticated());
-        token.setAuthenticated(false); // ignored
-        assertTrue(token.isAuthenticated());
+        token.setAuthenticated(false);
+        assertTrue(!token.isAuthenticated());
     }
 }

+ 3 - 3
core/src/test/java/org/acegisecurity/runas/RunAsUserTokenTests.java

@@ -1,4 +1,4 @@
-/* Copyright 2004 Acegi Technology Pty Limited
+/* Copyright 2004, 2005 Acegi Technology Pty Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,14 +49,14 @@ public class RunAsUserTokenTests extends TestCase {
         junit.textui.TestRunner.run(RunAsUserTokenTests.class);
     }
 
-    public void testAuthenticationSettingAlwaysReturnsTrue() {
+    public void testAuthenticationSetting() {
         RunAsUserToken token = new RunAsUserToken("my_password", "Test",
                 "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                         "ROLE_TWO")}, UsernamePasswordAuthenticationToken.class);
         assertTrue(token.isAuthenticated());
         token.setAuthenticated(false);
-        assertTrue(token.isAuthenticated());
+        assertTrue(!token.isAuthenticated());
     }
 
     public void testGetters() {

+ 10 - 9
doc/xdocs/changes.xml

@@ -26,17 +26,18 @@
   </properties>
   <body>
     <release version="0.9.0" date="In CVS">
-      <action dev="benalex" type="update">JdbcDaoImpl modified to support synthetic primary keys</action>        
-      <action dev="benalex" type="update">Greatly improve BasicAclEntryAfterInvocationCollectionFilteringProvider performance with large collections (if the principal has access to relatively few collection elements)</action>        
-      <action dev="benalex" type="update">Reorder DaoAuthenticationProvider exception logic as per developer list discussion</action>        
-      <action dev="benalex" type="update">ContextHolder refactored and replaced by SecurityContextHolder</action>        
-      <action dev="benalex" type="fix">Made AclEntry Serializable (correct issue with BasicAclEntryCache)</action>        
-      <action dev="luke_t" type="update">Changed order of credentials verification and expiry checking in DaoAuthenticationProvider. Password must now be successfully verified before expired credentials are reported. </action>        
+      <action dev="benalex" type="update">JdbcDaoImpl modified to support synthetic primary keys</action>
+      <action dev="benalex" type="update">Greatly improve BasicAclEntryAfterInvocationCollectionFilteringProvider performance with large collections (if the principal has access to relatively few collection elements)</action>
+      <action dev="benalex" type="update">Reorder DaoAuthenticationProvider exception logic as per developer list discussion</action>
+      <action dev="benalex" type="update">ContextHolder refactored and replaced by SecurityContextHolder</action>
+      <action dev="benalex" type="fix">Made AclEntry Serializable (correct issue with BasicAclEntryCache)</action>
+      <action dev="luke_t" type="update">Changed order of credentials verification and expiry checking in DaoAuthenticationProvider. Password must now be successfully verified before expired credentials are reported. </action>
       <action dev="benalex" type="update">AnonymousProcessingFilter offers protected method to control when it should execute</action>
       <action dev="benalex" type="fix">AbstractAuthenticationToken.getName() now returns username alone if UserDetails present</action>
-      <action dev="raykrueger" type="update">AuthorityGranter.grant now returns a java.util.Set of role names, instead of a single role name</action>  
-      <action dev="benalex" type="update">JavaDoc improvements</action>  
-      <action dev="benalex" type="fix">Correct synchronization issue with FilterToBeanProxy initialization</action>        
+      <action dev="raykrueger" type="update">AuthorityGranter.grant now returns a java.util.Set of role names, instead of a single role name</action>
+      <action dev="benalex" type="update">JavaDoc improvements</action>
+      <action dev="benalex" type="fix">Correct synchronization issue with FilterToBeanProxy initialization (as per developer list discussion)</action>
+      <action dev="benalex" type="update">Refactor Authentication.isAuthenticated() handling to be more performance (as per developer list discussion)</action>
     </release>
     <release version="0.8.2" date="2005-04-20">
       <action dev="benalex" type="fix">Correct location of AuthenticationSimpleHttpInvokerRequestExecutor in clientContext.xml</action>