diff --git a/changelog.txt b/changelog.txt
index fcc6efc11b..5b666ff215 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,10 @@
+Changes in version 0.6 (2004-xx-xx)
+-----------------------------------
+
+* Added feature so DaoAuthenticationProvider returns User in Authentication
+* Fixed Linux compatibility issues (directory case sensitivity etc)
+* Documentation improvements
+
Changes in version 0.51 (2004-06-06)
------------------------------------
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
index 8b1036c34a..ea204b93a3 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
@@ -51,12 +51,26 @@ import org.springframework.dao.DataAccessException;
*
* Upon successful validation, a
* UsernamePasswordAuthenticationToken will be created and
- * returned to the caller. In addition, the {@link User} will be placed in the
- * {@link UserCache} so that subsequent requests with the same username can be
- * validated without needing to query the {@link AuthenticationDao}. It should
- * be noted that if a user appears to present an incorrect password, the
- * {@link AuthenticationDao} will be queried to confirm the most up-to-date
- * password was used for comparison.
+ * returned to the caller. The token will include as its principal either a
+ * String representation of the username, or the {@link User}
+ * that was returned from the authentication repository. Using
+ * String is appropriate if a container adapter is being used, as
+ * it expects String representations of the username. Using
+ * User is appropriate if you require access to additional
+ * properties of the authenticated user, such as email addresses,
+ * human-friendly names etc. As container adapters are not recommended to be
+ * used, and User provides additional flexibility, by default a
+ * User is returned. To override this default, set the {@link
+ * #setForcePrincipalAsString} to true.
+ *
+ *
+ *
+ * Caching is handled via the User object being placed in the
+ * {@link UserCache}. This ensures that subsequent requests with the same
+ * username can be validated without needing to query the {@link
+ * AuthenticationDao}. It should be noted that if a user appears to present an
+ * incorrect password, the {@link AuthenticationDao} will be queried to
+ * confirm the most up-to-date password was used for comparison.
*
*
*
@@ -79,6 +93,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private SaltSource saltSource;
private UserCache userCache = new NullUserCache();
+ private boolean forcePrincipalAsString = false;
//~ Methods ================================================================
@@ -95,6 +110,14 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
return authenticationDao;
}
+ public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
+ this.forcePrincipalAsString = forcePrincipalAsString;
+ }
+
+ public boolean isForcePrincipalAsString() {
+ return forcePrincipalAsString;
+ }
+
/**
* Sets the PasswordEncoder instance to be used to encode and validate
* passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
@@ -148,13 +171,19 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
+ // Determine username
+ String username = authentication.getPrincipal().toString();
+
+ if (authentication.getPrincipal() instanceof User) {
+ username = ((User) authentication.getPrincipal()).getUsername();
+ }
+
boolean cacheWasUsed = true;
- User user = this.userCache.getUserFromCache(authentication.getPrincipal()
- .toString());
+ User user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
- user = getUserFromBackend(authentication);
+ user = getUserFromBackend(username);
}
if (!user.isEnabled()) {
@@ -170,7 +199,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
// Password incorrect, so ensure we're using most current password
if (cacheWasUsed) {
cacheWasUsed = false;
- user = getUserFromBackend(authentication);
+ user = getUserFromBackend(username);
}
if (!isPasswordCorrect(authentication, user)) {
@@ -194,9 +223,15 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
}
}
+ Object principalToReturn = user;
+
+ if (forcePrincipalAsString) {
+ principalToReturn = user.getUsername();
+ }
+
// Ensure we return the original credentials the user supplied,
// so subsequent attempts are successful even with encoded passwords
- return new UsernamePasswordAuthenticationToken(user.getUsername(),
+ return new UsernamePasswordAuthenticationToken(principalToReturn,
authentication.getCredentials(), user.getAuthorities());
}
@@ -220,10 +255,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
authentication.getCredentials().toString(), salt);
}
- private User getUserFromBackend(Authentication authentication) {
+ private User getUserFromBackend(String username) {
try {
- return this.authenticationDao.loadUserByUsername(authentication.getPrincipal()
- .toString());
+ return this.authenticationDao.loadUserByUsername(username);
} catch (UsernameNotFoundException notFound) {
throw new BadCredentialsException("Bad credentials presented");
} catch (DataAccessException repositoryProblem) {
diff --git a/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml b/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml
index 38a23ee9ca..0c3ce24b6e 100644
--- a/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml
+++ b/core/src/main/resources/org/acegisecurity/adapters/acegisecurity.xml
@@ -36,6 +36,7 @@
+ true
diff --git a/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml b/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml
index 38a23ee9ca..0c3ce24b6e 100644
--- a/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml
+++ b/core/src/test/java/org/acegisecurity/adapters/adaptertest-valid.xml
@@ -36,6 +36,7 @@
+ true
diff --git a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java
index 8898aaf240..1e2a37fcfe 100644
--- a/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java
+++ b/core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java
@@ -166,7 +166,7 @@ public class DaoAuthenticationProviderTests extends TestCase {
}
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
- assertEquals("marissa", castResult.getPrincipal());
+ assertEquals(User.class, castResult.getPrincipal().getClass());
assertEquals("koala", castResult.getCredentials());
assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority());
assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
@@ -192,7 +192,7 @@ public class DaoAuthenticationProviderTests extends TestCase {
}
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
- assertEquals("marissa", castResult.getPrincipal());
+ assertEquals(User.class, castResult.getPrincipal().getClass());
// We expect original credentials user submitted to be returned
assertEquals("koala", castResult.getCredentials());
diff --git a/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java
index a1fa7dede5..1f3c8fa451 100644
--- a/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java
+++ b/core/src/test/java/org/acegisecurity/ui/basicauth/BasicProcessingFilterTests.java
@@ -24,6 +24,7 @@ import net.sf.acegisecurity.MockFilterConfig;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
import net.sf.acegisecurity.MockHttpSession;
+import net.sf.acegisecurity.providers.dao.User;
import net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter;
import org.apache.commons.codec.binary.Base64;
@@ -199,8 +200,8 @@ public class BasicProcessingFilterTests extends TestCase {
assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
assertEquals("marissa",
- ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
- .toString());
+ ((User) ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal())
+ .getUsername());
}
public void testOtherAuthorizationSchemeIsIgnored()
@@ -291,8 +292,8 @@ public class BasicProcessingFilterTests extends TestCase {
assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
assertEquals("marissa",
- ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
- .toString());
+ ((User) ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal())
+ .getUsername());
// NOW PERFORM FAILED AUTHENTICATION
// Setup our HTTP request
diff --git a/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java
index f3444b3a6c..1cc674e0bc 100644
--- a/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java
+++ b/core/src/test/java/org/acegisecurity/ui/cas/CasProcessingFilterEntryPointTests.java
@@ -20,6 +20,8 @@ import junit.framework.TestCase;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
+import java.net.URLEncoder;
+
/**
* Tests {@link CasProcessingFilterEntryPoint}.
@@ -99,8 +101,11 @@ public class CasProcessingFilterEntryPointTests extends TestCase {
ep.afterPropertiesSet();
ep.commence(request, response);
- assertEquals("https://cas/login?service=https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
- response.getRedirect());
+
+ assertEquals("https://cas/login?service="
+ + URLEncoder.encode(
+ "https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
+ "UTF-8"), response.getRedirect());
}
public void testNormalOperationWithRenewTrue() throws Exception {
diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml
index f026126927..0d7027008b 100644
--- a/docs/reference/src/index.xml
+++ b/docs/reference/src/index.xml
@@ -7,7 +7,7 @@
Reference Documentation
- 0.51
+ 0.6
@@ -946,10 +946,24 @@
increased the complexity of the AuthenticationDao
interface. For instance, a method would be required to increase the
count of unsuccessful authentication attempts. Such functionality
- could be easily provided in a new
- AuthenticationManager or
- AuthenticationProvider implementation if it were
- desired.
+ could be easily provided by leveraging the application event
+ publishing features discussed below.
+
+ DaoAuthenticationProvider returns an
+ Authentication object which in turn has its
+ principal property set. The principal will be
+ either a String (which is essentially the username)
+ or a User object (which was looked up from the
+ AuthenticationDao). By default the
+ User is returned, as this enables applications to
+ subclass User and add extra properties potentially
+ of use in applications, such as the user's full name, email address
+ etc. If using container adapters, or if your applications were written
+ to operate with Strings (as was the case for
+ releases prior to Acegi Security 0.6), you should set the
+ DaoAuthenticationProvider.forcePrincipalAsString
+ property to true in your application
+ context.
@@ -1927,6 +1941,11 @@ public boolean supports(Class clazz);
provided below. Once installed, please take the time to try the sample
application to ensure your container adapter is properly
configured.
+
+ When using container adapters with the
+ DaoAuthenticationProvider, ensure you set its
+ forcePrincipalAsString property to
+ true.
@@ -2497,7 +2516,7 @@ $CATALINA_HOME/bin/startup.sh
PasswordHandler will do).
To install, you will need to download and extract the CAS server
- archive. We used version 2.0.12 Beta 3. There will be a
+ archive. We used version 2.0.12. There will be a
/web directory in the root of the deployment. Copy
an applicationContext.xml containing your
AuthenticationManager as well as the
diff --git a/project.properties b/project.properties
index 9490cef1b2..cfb572d789 100644
--- a/project.properties
+++ b/project.properties
@@ -6,7 +6,7 @@
# $Id$
# Project version
-acegi-security-version=0.51
+acegi-security-version=0.6
# Project name
name=acegi-security-system-for-spring
diff --git a/samples/contacts/etc/ca/resin-acegisecurity.xml b/samples/contacts/etc/ca/resin-acegisecurity.xml
index 38175dc397..90280d781f 100644
--- a/samples/contacts/etc/ca/resin-acegisecurity.xml
+++ b/samples/contacts/etc/ca/resin-acegisecurity.xml
@@ -33,6 +33,7 @@
+ true
diff --git a/samples/contacts/src/main/java/sample/contact/ContactManagerFacade.java b/samples/contacts/src/main/java/sample/contact/ContactManagerFacade.java
index 38fcf9e046..4cc23dd6a5 100644
--- a/samples/contacts/src/main/java/sample/contact/ContactManagerFacade.java
+++ b/samples/contacts/src/main/java/sample/contact/ContactManagerFacade.java
@@ -19,6 +19,7 @@ import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.providers.dao.User;
import org.springframework.beans.factory.InitializingBean;
@@ -88,7 +89,13 @@ public class ContactManagerFacade implements ContactManager, InitializingBean {
Authentication auth = ((SecureContext) ContextHolder.getContext())
.getAuthentication();
- if (auth.getPrincipal().toString().equals(result.getOwner())) {
+ String username = auth.getPrincipal().toString();
+
+ if (auth.getPrincipal() instanceof User) {
+ username = ((User) auth.getPrincipal()).getUsername();
+ }
+
+ if (username.equals(result.getOwner())) {
return result;
} else {
throw new AccessDeniedException(
diff --git a/samples/contacts/src/main/java/sample/contact/ContactSecurityVoter.java b/samples/contacts/src/main/java/sample/contact/ContactSecurityVoter.java
index 98cc029834..792f49697d 100644
--- a/samples/contacts/src/main/java/sample/contact/ContactSecurityVoter.java
+++ b/samples/contacts/src/main/java/sample/contact/ContactSecurityVoter.java
@@ -18,6 +18,7 @@ package sample.contact;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.providers.dao.User;
import net.sf.acegisecurity.vote.AccessDecisionVoter;
import org.aopalliance.intercept.MethodInvocation;
@@ -96,9 +97,15 @@ public class ContactSecurityVoter implements AccessDecisionVoter {
}
if (passedOwner != null) {
+ String username = authentication.getPrincipal().toString();
+
+ if (authentication.getPrincipal() instanceof User) {
+ username = ((User) authentication.getPrincipal())
+ .getUsername();
+ }
+
// Check the authentication principal matches the passed owner
- if (passedOwner.equals(authentication.getPrincipal()
- .toString())) {
+ if (passedOwner.equals(username)) {
return ACCESS_GRANTED;
}
}
diff --git a/samples/contacts/src/main/java/sample/contact/SecureIndexController.java b/samples/contacts/src/main/java/sample/contact/SecureIndexController.java
index a651203e7a..ea75a074fb 100644
--- a/samples/contacts/src/main/java/sample/contact/SecureIndexController.java
+++ b/samples/contacts/src/main/java/sample/contact/SecureIndexController.java
@@ -20,6 +20,7 @@ import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.providers.dao.User;
import org.springframework.beans.factory.InitializingBean;
@@ -74,10 +75,17 @@ public class SecureIndexController implements Controller, InitializingBean {
+ "SecureContext");
}
- final Authentication currentUser = secureContext.getAuthentication();
+ // Lookup username. As we must accommodate DaoAuthenticationProvider,
+ // CAS and container based authentication, we take care with casting
+ Authentication auth = secureContext.getAuthentication();
+ String username = auth.getPrincipal().toString();
+
+ if (auth.getPrincipal() instanceof User) {
+ username = ((User) auth.getPrincipal()).getUsername();
+ }
boolean supervisor = false;
- GrantedAuthority[] granted = currentUser.getAuthorities();
+ GrantedAuthority[] granted = auth.getAuthorities();
for (int i = 0; i < granted.length; i++) {
if (granted[i].getAuthority().equals("ROLE_SUPERVISOR")) {
@@ -85,13 +93,12 @@ public class SecureIndexController implements Controller, InitializingBean {
}
}
- Contact[] myContacts = contactManager.getAllByOwner(currentUser.getPrincipal()
- .toString());
+ Contact[] myContacts = contactManager.getAllByOwner(username);
Map model = new HashMap();
model.put("contacts", myContacts);
model.put("supervisor", new Boolean(supervisor));
- model.put("user", currentUser.getPrincipal().toString());
+ model.put("user", username);
return new ModelAndView("index", "model", model);
}
diff --git a/samples/contacts/src/main/java/sample/contact/WebContactAddController.java b/samples/contacts/src/main/java/sample/contact/WebContactAddController.java
index 3a3cbb89e8..24c7138f44 100644
--- a/samples/contacts/src/main/java/sample/contact/WebContactAddController.java
+++ b/samples/contacts/src/main/java/sample/contact/WebContactAddController.java
@@ -15,8 +15,10 @@
package sample.contact;
+import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.providers.dao.User;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
@@ -54,8 +56,14 @@ public class WebContactAddController extends SimpleFormController {
public ModelAndView onSubmit(Object command) throws ServletException {
String name = ((WebContact) command).getName();
String email = ((WebContact) command).getEmail();
- String owner = ((SecureContext) ContextHolder.getContext()).getAuthentication()
- .getPrincipal().toString();
+
+ Authentication auth = ((SecureContext) ContextHolder.getContext())
+ .getAuthentication();
+ String owner = auth.getPrincipal().toString();
+
+ if (auth.getPrincipal() instanceof User) {
+ owner = ((User) auth.getPrincipal()).getUsername();
+ }
Contact contact = new Contact(contactManager.getNextId(), name, email,
owner);
diff --git a/upgrade-05-06.txt b/upgrade-05-06.txt
new file mode 100644
index 0000000000..5a351f4edb
--- /dev/null
+++ b/upgrade-05-06.txt
@@ -0,0 +1,27 @@
+===============================================================================
+ ACEGI SECURITY SYSTEM FOR SPRING - UPGRADING FROM 0.5 TO 0.6
+===============================================================================
+
+The following should help most casual users of the project update their
+applications:
+
+- Locate and remove all property references to
+ DaoAuthenticationProvider.key and
+ DaoAuthenticationProvider.refreshTokenInterval.
+
+- If you are using DaoAuthenticationProvider and either (i) you are using
+ container adapters or (ii) your code relies on the Authentication object
+ having its getPrincipal() return a String, you must set the new
+ DaoAuthenticationProvider property, forcePrincipalAsString, to true.
+ By default DaoAuthenticationProvider returns an Authentication object
+ containing the relevant User, which allows access to additional properties.
+ Where possible, we recommend you change your code to something like this,
+ so that you can leave forcePrincipalAsString to the false default:
+
+ String username = authentication.getPrincipal();
+ if (authentication.getPrincipal() instanceof User) {
+ username = ((User) authentication.getPrincipal()).getUsername();
+ }
+
+
+$Id$