Index: branches/TaskRewrite/src/grails-app/conf/BootStrap.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/conf/BootStrap.groovy	(revision 57)
+++ branches/TaskRewrite/src/grails-app/conf/BootStrap.groovy	(revision 58)
@@ -1,7 +1,156 @@
-class BootStrap {
+import grails.util.GrailsUtil
 
-     def init = { servletContext ->
-     }
-     def destroy = {
-     }
-} 
+class BootStrap 
+{
+    //Required to be right here for Acegi plugin.
+    def authenticateService
+    Boolean BootStrapDemoDataSuccessful = true
+
+    def init = { servletContext ->
+
+    println "**** BootStrap GrailsUtil.environment = ${GrailsUtil.environment}"
+    
+        switch (GrailsUtil.environment)
+        {
+            case "development":
+                        bootStrapDemoData()
+                        break
+            case "test":
+                        break
+            case "production":
+                        bootStrapDemoData()
+                        break 
+        }
+    
+    }
+
+    def destroy = {
+    }
+
+    //Insert some demo/startup data.
+    void bootStrapDemoData()
+    {
+        println "BootStrapping demo data..."
+    
+        //TypeOfPersonGroup
+//         new PersonGroupType(name:"Department").save()
+//         new PersonGroupType(name:"Contractor").save()
+//         new PersonGroupType(name:"ProjectTeam").save()
+    
+        //PersonGroup
+//         new PersonGroup(personGroupType:PersonGroupType.findByName("Department"),
+//                         name:"Electrical").save()
+//         new PersonGroup(personGroupType:PersonGroupType.get(2),
+//                         name:"Kewl AirCon Guys").save()
+//         new PersonGroup(personGroupType:PersonGroupType.get(3),
+//                         name:"gnuMims").save()
+
+            
+        //Person
+//         def passwordEncoded = authenticateService.encodePassword("pass")
+
+        def personInstance = new Person(loginName:"admin",
+                                    firstName:"Admin",
+                                    lastName:"Powers",
+                                    password:authenticateService.encodePassword("pass"),
+                                    email:"admin@example.com")
+        BootStrapSaveAndTest(personInstance)
+
+        //Role
+        def authInstance = new Authority(description:"Application Admin",
+                            authority:"ROLE_ADMIN")
+            authInstance.addToPersons(personInstance)
+            authInstance.save()
+
+//         new Person(username:"admin", 
+//             userRealName:"Admin Powers", 
+//             enabled:true,
+//             
+//         new Person(personGroup:PersonGroup.get(3),
+//             firstName:"Admin",
+//             lastName:"Powers",
+//             userId:"admin",
+//             password:"pass").save()
+//         new Person(personGroup:PersonGroup.get(1),
+//             firstName:"Demo",
+//             lastName:"Danza",
+//             userId:"user",
+//             password:"pass").save()
+
+
+//         new Person(personGroup:PersonGroup.get(1),
+//             firstName:"Craig",
+//             lastName:"SuperTech",
+//             userId:"craig",
+//             password:"pass").save()
+//         new Person(personGroup:PersonGroup.get(2),
+//             firstName:"Joe",
+//             lastName:"Samples",
+//             userId:"joe",
+//             password:"pass").save()
+//         new Person(personGroup:PersonGroup.get(1),
+//             firstName:"Production",
+//             lastName:"Mann",
+//             userId:"Mann",
+//             password:"pass").save()
+                
+        //TaskGroup
+//         new TaskGroup(name:"Engineering",
+//                       description:"Engineering task group").save()
+//         new TaskGroup(name:"Production",
+//                       description:"Production task group").save()
+//         new TaskGroup(name:"NewProject(s)",
+//                       description:" ").save()
+                      
+        
+        //Task
+//         new Task(taskGroup:TaskGroup.findByName("Engineering"),
+//                  leadPerson:Person.get(3),
+//                  name:"Check specific level sensor",
+//                  description:"Has been noted as problematic, try recallibrating",
+//                  scheduledDate: new Date(),
+//                  targetDate: new Date() ).save()
+//         new Task(taskGroup:TaskGroup.findByName("Production"),
+//                  leadPerson:Person.get(5),
+//                  name:"Production Report",
+//                  description:"Production report for specific production run or shift",
+//                  scheduledDate: new Date(),
+//                  targetDate: new Date() ).save()
+//         new Task(taskGroup:TaskGroup.findByName("NewProject(s)"),
+//                  leadPerson:Person.get(1),
+//                  name:"Make killer CMMS app",
+//                  description:"Use Grails and get a move on!",
+//                  scheduledDate: new Date(),
+//                  targetDate: new Date() ).save()
+
+        //EntryType
+//         new EntryType(name:"Fault").save()
+//         new EntryType(name:"WorkDone").save()
+//         new EntryType(name:"Production Report").save()
+
+        //ModificationType
+//         new ModificationType(name:"Created").save()
+//         new ModificationType(name:"Completed").save()
+//         new ModificationType(name:"Closed").save()
+//         new ModificationType(name:"Altered").save()
+//         new ModificationType(name:"TargetDateModified").save()
+//         new ModificationType(name:"ScheduledDateModified").save()
+//         new ModificationType(name:"DescriptionModified").save()
+//         new ModificationType(name:"AssignedToModified").save()
+//         new ModificationType(name:"NameModified").save()
+        
+        if(BootStrapDemoDataSuccessful) {
+            println "BootStrapping demo data...successful."
+        }
+        else println "BootStrapping demo data...failed."
+    }
+    
+    void BootStrapSaveAndTest(object) {
+        if(!object.save()) {
+            BootStrapDemoDataSuccessful = false
+            println "'${object}' failed to save!"
+            println object.errors
+
+        }
+    } 
+}
Index: branches/TaskRewrite/src/grails-app/conf/Config.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/conf/Config.groovy	(revision 57)
+++ branches/TaskRewrite/src/grails-app/conf/Config.groovy	(revision 58)
@@ -69,2 +69,5 @@
 
 
+
+
+//log4j.logger.org.springframework.security='off,stdout'
Index: branches/TaskRewrite/src/grails-app/conf/SecurityConfig.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/conf/SecurityConfig.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/conf/SecurityConfig.groovy	(revision 58)
@@ -0,0 +1,20 @@
+security {
+
+	// see DefaultSecurityConfig.groovy for all settable/overridable properties
+
+	active = true
+
+	loginUserDomainClass = "Person"
+    userName = 'loginName'
+    password = 'password'
+    enabled = 'isActive'
+
+	authorityDomainClass = "Authority"
+
+    //Required if we want to run "grails generate-manager"
+    //Which recreates the controller and views, so save the views.
+//     requestMapClass = 'Requestmap'
+
+    useRequestMapDomainClass = false
+    useControllerAnnotations = true
+}
Index: branches/TaskRewrite/src/grails-app/controllers/AuthorityController.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/controllers/AuthorityController.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/controllers/AuthorityController.groovy	(revision 58)
@@ -0,0 +1,123 @@
+
+
+
+/**
+ * Authority Controller.
+ */
+class AuthorityController {
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def authenticateService
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	/**
+	 * Display the list authority page.
+	 */
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[authorityList: Authority.list(params)]
+	}
+
+	/**
+	 * Display the show authority page.
+	 */
+	def show = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Delete an authority.
+	 */
+	def delete = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		authenticateService.deleteRole(authority)
+
+		flash.message = "Authority $params.id deleted."
+		redirect action: list
+	}
+
+	/**
+	 * Display the edit authority page.
+	 */
+	def edit = {
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Authority update action.
+	 */
+	def update = {
+
+		def authority = Authority.get(params.id)
+		if (!authority) {
+			flash.message = "Authority not found with id $params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (authority.version > version) {
+			authority.errors.rejectValue 'version', 'authority.optimistic.locking.failure',
+				'Another user has updated this Authority while you were editing.'
+			render view: 'edit', model: [authority: authority]
+			return
+		}
+
+		if (authenticateService.updateRole(authority, params)) {
+			authenticateService.clearCachedRequestmaps()
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'edit', model: [authority: authority]
+		}
+	}
+
+	/**
+	 * Display the create new authority page.
+	 */
+	def create = {
+		[authority: new Authority()]
+	}
+
+	/**
+	 * Save a new authority.
+	 */
+	def save = {
+
+		def authority = new Authority()
+		authority.properties = params
+		if (authority.save()) {
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'create', model: [authority: authority]
+		}
+	}
+}
Index: branches/TaskRewrite/src/grails-app/controllers/LoginController.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/controllers/LoginController.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/controllers/LoginController.groovy	(revision 58)
@@ -0,0 +1,179 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.RedirectUtils
+import org.grails.plugins.springsecurity.service.AuthenticateService
+
+import org.springframework.security.AuthenticationTrustResolverImpl
+import org.springframework.security.DisabledException
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.security.ui.AbstractProcessingFilter
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
+
+/**
+ * Login Controller (Example).
+ */
+class LoginController {
+
+	/**
+	 * Dependency injection for the authentication service.
+	 */
+	def authenticateService
+
+	/**
+	 * Dependency injection for OpenIDConsumer.
+	 */
+	def openIDConsumer
+
+	/**
+	 * Dependency injection for OpenIDAuthenticationProcessingFilter.
+	 */
+	def openIDAuthenticationProcessingFilter
+
+	private final authenticationTrustResolver = new AuthenticationTrustResolverImpl()
+
+	def index = {
+		if (isLoggedIn()) {
+			redirect uri: '/'
+		}
+		else {
+			redirect action: auth, params: params
+		}
+	}
+
+	/**
+	 * Show the login page.
+	 */
+	def auth = {
+
+		nocache response
+
+		if (isLoggedIn()) {
+			redirect uri: '/'
+			return
+		}
+
+		String view
+		String postUrl
+		def config = authenticateService.securityConfig.security
+		if (config.useOpenId) {
+			view = 'openIdAuth'
+			postUrl = "${request.contextPath}/login/openIdAuthenticate"
+		}
+		else if (config.useFacebook) {
+			view = 'facebookAuth'
+			postUrl = "${request.contextPath}${config.facebook.filterProcessesUrl}"
+		}
+		else {
+			view = 'auth'
+			postUrl = "${request.contextPath}${config.filterProcessesUrl}"
+		}
+
+		render view: view, model: [postUrl: postUrl]
+	}
+
+	/**
+	 * Form submit action to start an OpenID authentication.
+	 */
+	def openIdAuthenticate = {
+		String openID = params['j_username']
+		try {
+			String returnToURL = RedirectUtils.buildRedirectUrl(
+					request, response, openIDAuthenticationProcessingFilter.filterProcessesUrl)
+			String redirectUrl = openIDConsumer.beginConsumption(request, openID, returnToURL)
+			redirect url: redirectUrl
+		}
+		catch (org.springframework.security.ui.openid.OpenIDConsumerException e) {
+			log.error "Consumer error: $e.message", e
+			redirect url: openIDAuthenticationProcessingFilter.authenticationFailureUrl
+		}
+	}
+
+	// Login page (function|json) for Ajax access.
+	def authAjax = {
+		nocache(response)
+		//this is example:
+		render """
+		<script type='text/javascript'>
+		(function() {
+			loginForm();
+		})();
+		</script>
+		"""
+	}
+
+	/**
+	 * The Ajax success redirect url.
+	 */
+	def ajaxSuccess = {
+		nocache(response)
+		render '{success: true}'
+	}
+
+	/**
+	 * Show denied page.
+	 */
+	def denied = {
+		if (isLoggedIn() && authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) {
+			// have cookie but the page is guarded with IS_AUTHENTICATED_FULLY
+			redirect action: full, params: params
+		}
+	}
+
+	/**
+	 * Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page.
+	 */
+	def full = {
+		render view: 'auth', params: params,
+			model: [hasCookie: authenticationTrustResolver.isRememberMe(SCH.context?.authentication)]
+	}
+
+	// Denial page (data|view|json) for Ajax access.
+	def deniedAjax = {
+		//this is example:
+		render "{error: 'access denied'}"
+	}
+
+	/**
+	 * login failed
+	 */
+	def authfail = {
+
+		def username = session[AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
+		def msg = ''
+		def exception = session[AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY]
+		if (exception) {
+			if (exception instanceof DisabledException) {
+				msg = "[$username] is disabled."
+			}
+			else {
+				msg = "[$username] wrong username/password."
+			}
+		}
+
+		if (isAjax()) {
+			render "{error: '${msg}'}"
+		}
+		else {
+			flash.message = msg
+			redirect action: auth, params: params
+		}
+	}
+
+	/**
+	 * Check if logged in.
+	 */
+	private boolean isLoggedIn() {
+		return authenticateService.isLoggedIn()
+	}
+
+	private boolean isAjax() {
+		return authenticateService.isAjax(request)
+	}
+
+	/** cache controls */
+	private void nocache(response) {
+		response.setHeader('Cache-Control', 'no-cache') // HTTP 1.1
+		response.addDateHeader('Expires', 0)
+		response.setDateHeader('max-age', 0)
+		response.setIntHeader ('Expires', -1) //prevents caching at the proxy server
+		response.addHeader('cache-Control', 'private') //IE5.x only
+	}
+}
Index: branches/TaskRewrite/src/grails-app/controllers/LogoutController.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/controllers/LogoutController.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/controllers/LogoutController.groovy	(revision 58)
@@ -0,0 +1,13 @@
+/**
+ * Logout Controller (Example).
+ */
+class LogoutController {
+
+	/**
+	 * Index action. Redirects to the Spring security logout uri.
+	 */
+	def index = {
+		// TODO  put any pre-logout code here
+		redirect(uri: '/j_spring_security_logout')
+	}
+}
Index: branches/TaskRewrite/src/grails-app/controllers/PersonController.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/controllers/PersonController.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/controllers/PersonController.groovy	(revision 58)
@@ -0,0 +1,162 @@
+
+
+
+/**
+ * User controller.
+ */
+class PersonController {
+
+	def authenticateService
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[personList: Person.list(params)]
+	}
+
+	def show = {
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: list
+			return
+		}
+		List roleNames = []
+		for (role in person.authorities) {
+			roleNames << role.authority
+		}
+		roleNames.sort { n1, n2 ->
+			n1 <=> n2
+		}
+		[person: person, roleNames: roleNames]
+	}
+
+	/**
+	 * Person delete action. Before removing an existing person,
+	 * he should be removed from those authorities which he is involved.
+	 */
+	def delete = {
+
+		def person = Person.get(params.id)
+		if (person) {
+			def authPrincipal = authenticateService.principal()
+			//avoid self-delete if the logged-in user is an admin
+			if (!(authPrincipal instanceof String) && authPrincipal.username == person.loginName) {
+				flash.message = "You can not delete yourself, please login as another admin and try again"
+			}
+			else {
+				//first, delete this person from Persons_Authorities table.
+				Authority.findAll().each { it.removeFromPersons(person) }
+				person.delete()
+				flash.message = "Person $params.id deleted."
+			}
+		}
+		else {
+			flash.message = "Person not found with id $params.id"
+		}
+
+		redirect action: list
+	}
+
+	def edit = {
+
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: list
+			return
+		}
+
+		return buildPersonModel(person)
+	}
+
+	/**
+	 * Person update action.
+	 */
+	def update = {
+
+		def person = Person.get(params.id)
+		if (!person) {
+			flash.message = "Person not found with id $params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (person.version > version) {
+			person.errors.rejectValue 'version', "person.optimistic.locking.failure",
+				"Another user has updated this Person while you were editing."
+				render view: 'edit', model: buildPersonModel(person)
+			return
+		}
+
+		def oldPassword = person.password
+		person.properties = params
+		if (!params.password.equals(oldPassword)) {
+			person.password = authenticateService.encodePassword(params.password)
+		}
+		if (person.save()) {
+			Authority.findAll().each { it.removeFromPersons(person) }
+			addRoles(person)
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'edit', model: buildPersonModel(person)
+		}
+	}
+
+	def create = {
+		[person: new Person(params), authorityList: Authority.list()]
+	}
+
+	/**
+	 * Person save action.
+	 */
+	def save = {
+
+		def person = new Person()
+		person.properties = params
+		person.password = authenticateService.encodePassword(params.password)
+		if (person.save()) {
+			addRoles(person)
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'create', model: [authorityList: Authority.list(), person: person]
+		}
+	}
+
+	private void addRoles(person) {
+		for (String key in params.keySet()) {
+			if (key.contains('ROLE') && 'on' == params.get(key)) {
+				Authority.findByAuthority(key).addToPersons(person)
+			}
+		}
+	}
+
+	private Map buildPersonModel(person) {
+
+		List roles = Authority.list()
+		roles.sort { r1, r2 ->
+			r1.authority <=> r2.authority
+		}
+		Set userRoleNames = []
+		for (role in person.authorities) {
+			userRoleNames << role.authority
+		}
+		LinkedHashMap<Authority, Boolean> roleMap = [:]
+		for (role in roles) {
+			roleMap[(role)] = userRoleNames.contains(role.authority)
+		}
+
+		return [person: person, roleMap: roleMap]
+	}
+}
Index: branches/TaskRewrite/src/grails-app/domain/Authority.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/domain/Authority.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/domain/Authority.groovy	(revision 58)
@@ -0,0 +1,14 @@
+class Authority {
+
+	static hasMany = [persons: Person]
+
+	/** description */
+	String description
+	/** ROLE String */
+	String authority
+
+	static constraints = {
+		authority(blank: false, unique: true)
+		description()
+	}
+}
Index: branches/TaskRewrite/src/grails-app/domain/Person.groovy
===================================================================
--- branches/TaskRewrite/src/grails-app/domain/Person.groovy	(revision 58)
+++ branches/TaskRewrite/src/grails-app/domain/Person.groovy	(revision 58)
@@ -0,0 +1,36 @@
+class Person {
+	static transients = ['pass']
+	static hasMany = [authorities: Authority]
+	static belongsTo = Authority
+
+	String loginName
+	String firstName
+    String lastName
+    String employeeID
+
+	/** MD5 Password */
+	String password
+
+	/** enabled */
+	boolean isActive = true
+
+	String email
+	boolean emailShow = true
+
+	/** description */
+	String description = ''
+
+	/** plain password to create a MD5 password */
+	String pass = '[secret]'
+
+	static constraints = {
+		loginName(blank: false, unique: true)
+		firstName(blank: false)
+        lastName(blank: false)
+		password(blank: false)
+        employeeID(blank: true, nullable:true)
+	}
+
+    //Overriding the default toString method
+    String toString() {"${this.firstName} ${this.lastName}"}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/AcegiGrailsPlugin.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/AcegiGrailsPlugin.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/AcegiGrailsPlugin.groovy	(revision 58)
@@ -0,0 +1,989 @@
+import grails.util.GrailsUtil
+
+import org.codehaus.groovy.grails.commons.ControllerArtefactHandler
+
+import org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition
+import org.codehaus.groovy.grails.plugins.springsecurity.AuthenticatedVetoableDecisionManager
+import org.codehaus.groovy.grails.plugins.springsecurity.AuthorizeTools
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsAccessDeniedHandlerImpl
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsAuthenticationProcessingFilter
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoAuthenticationProvider
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoImpl
+import org.codehaus.groovy.grails.plugins.springsecurity.IpAddressFilter
+import org.codehaus.groovy.grails.plugins.springsecurity.LogoutFilterFactoryBean
+import org.codehaus.groovy.grails.plugins.springsecurity.QuietMethodSecurityInterceptor
+import org.codehaus.groovy.grails.plugins.springsecurity.RequestmapFilterInvocationDefinition
+import org.codehaus.groovy.grails.plugins.springsecurity.Secured as SecuredController
+import org.codehaus.groovy.grails.plugins.springsecurity.SecurityAnnotationAttributes
+import org.codehaus.groovy.grails.plugins.springsecurity.SecurityEventListener
+import org.codehaus.groovy.grails.plugins.springsecurity.WithAjaxAuthenticationProcessingFilterEntryPoint
+
+import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
+import org.springframework.beans.factory.config.RuntimeBeanReference
+import org.springframework.cache.ehcache.EhCacheFactoryBean
+import org.springframework.cache.ehcache.EhCacheManagerFactoryBean
+import org.springframework.security.annotation.Secured as SecuredService
+import org.springframework.security.context.HttpSessionContextIntegrationFilter
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.security.event.authentication.LoggerListener
+import org.springframework.security.intercept.method.MethodDefinitionAttributes
+import org.springframework.security.intercept.web.FilterSecurityInterceptor
+import org.springframework.security.ui.ExceptionTranslationFilter
+import org.springframework.security.ui.basicauth.BasicProcessingFilter
+import org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint
+import org.springframework.security.ui.logout.LogoutHandler
+import org.springframework.security.ui.logout.SecurityContextLogoutHandler
+import org.springframework.security.ui.rememberme.NullRememberMeServices
+import org.springframework.security.ui.rememberme.RememberMeProcessingFilter
+import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices
+import org.springframework.security.ui.session.HttpSessionEventPublisher
+import org.springframework.security.ui.switchuser.SwitchUserProcessingFilter
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
+import org.springframework.security.util.AntUrlPathMatcher
+import org.springframework.security.util.PortMapperImpl
+import org.springframework.security.util.PortResolverImpl
+import org.springframework.security.util.RegexUrlPathMatcher
+import org.springframework.security.providers.ProviderManager
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider
+import org.springframework.security.providers.anonymous.AnonymousProcessingFilter
+import org.springframework.security.providers.dao.cache.EhCacheBasedUserCache
+import org.springframework.security.providers.dao.cache.NullUserCache
+import org.springframework.security.providers.encoding.MessageDigestPasswordEncoder
+import org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider
+import org.springframework.security.securechannel.ChannelDecisionManagerImpl
+import org.springframework.security.securechannel.ChannelProcessingFilter
+import org.springframework.security.securechannel.InsecureChannelProcessor
+import org.springframework.security.securechannel.RetryWithHttpEntryPoint
+import org.springframework.security.securechannel.RetryWithHttpsEntryPoint
+import org.springframework.security.securechannel.SecureChannelProcessor
+import org.springframework.security.util.FilterChainProxy
+import org.springframework.security.util.FilterToBeanProxy
+import org.springframework.security.vote.AuthenticatedVoter
+import org.springframework.security.vote.RoleVoter
+import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter
+import org.springframework.web.filter.DelegatingFilterProxy
+
+/**
+ * Grails Spring Security 2.0 Plugin.
+ *
+ * @author T.Yamamoto
+ * @author Haotian Sun
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class AcegiGrailsPlugin {
+
+	private static final String DEFINITION_SOURCE_PREFIX =
+		'CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\n' +
+		'PATTERN_TYPE_APACHE_ANT\n'
+
+	String version = '0.5.1'
+	String author = 'Tsuyoshi Yamamoto'
+	String authorEmail = 'tyama@xmldo.jp'
+	String title = 'Grails Spring Security 2.0 Plugin'
+	String description = 'Plugin to use Grails domain class and secure your applications with Spring Security filters.'
+	String documentation = 'http://grails.org/AcegiSecurity+Plugin'
+	List observe = ['controllers']
+	List loadAfter = ['controllers']
+	List watchedResources = [
+		'file:./grails-app/controllers/**/*Controller.groovy',
+		'file:./plugins/*/grails-app/controllers/**/*Controller.groovy',
+		'file:./grails-app/conf/SecurityConfig.groovy'
+	]
+	Map dependsOn = [:]
+
+	def doWithSpring = {
+
+		def conf = AuthorizeTools.securityConfig.security
+		if (!conf || !conf.active) {
+			println '[active=false] Spring Security not loaded'
+			return
+		}
+
+		println 'loading security config ...'
+
+		createRefList.delegate = delegate
+
+		/** springSecurityFilterChain */
+		configureFilterChain.delegate = delegate
+		configureFilterChain conf
+
+		// OpenID
+		if (conf.useOpenId) {
+			configureOpenId.delegate = delegate
+			configureOpenId conf
+		}
+
+		// Facebook Connect
+		if (conf.useFacebook) {
+			configureFacebook.delegate = delegate
+			configureFacebook conf
+		}
+
+		// logout
+		configureLogout.delegate = delegate
+		configureLogout conf
+
+		// Basic Auth
+		configureBasicAuth.delegate = delegate
+		configureBasicAuth conf
+
+		/** httpSessionContextIntegrationFilter */
+		httpSessionContextIntegrationFilter(HttpSessionContextIntegrationFilter)
+
+		/** authenticationProcessingFilter */
+		authenticationProcessingFilter(GrailsAuthenticationProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1'
+			ajaxAuthenticationFailureUrl = conf.ajaxAuthenticationFailureUrl // /login/authfail?ajax=true
+			defaultTargetUrl = conf.defaultTargetUrl // '/'
+			alwaysUseDefaultTargetUrl = conf.alwaysUseDefaultTargetUrl // false
+			filterProcessesUrl = conf.filterProcessesUrl // '/j_spring_security_check'
+			rememberMeServices = ref('rememberMeServices')
+			authenticateService = ref('authenticateService')
+		}
+
+		/** securityContextHolderAwareRequestFilter */
+		securityContextHolderAwareRequestFilter(SecurityContextHolderAwareRequestFilter) {
+			portResolver = ref('portResolver')
+		}
+
+		/** rememberMeProcessingFilter */
+		rememberMeProcessingFilter(RememberMeProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			rememberMeServices = ref('rememberMeServices')
+		}
+
+		/** rememberMeServices */
+		if (conf.useOpenId || conf.useFacebook) {
+			// auth is external, so no password, so cookie isn't possible
+			rememberMeServices(NullRememberMeServices)
+		}
+		else {
+			rememberMeServices(TokenBasedRememberMeServices) {
+				userDetailsService = ref('userDetailsService')
+				key = conf.rememberMeKey
+				cookieName = conf.cookieName
+				alwaysRemember = conf.alwaysRemember
+				tokenValiditySeconds = conf.tokenValiditySeconds
+				parameter = conf.parameter
+			}
+		}
+
+		/** anonymousProcessingFilter */
+		anonymousProcessingFilter(AnonymousProcessingFilter) {
+			key = conf.key // 'foo'
+			userAttribute = conf.userAttribute //'anonymousUser,ROLE_ANONYMOUS'
+		}
+
+		/** exceptionTranslationFilter */
+		exceptionTranslationFilter(ExceptionTranslationFilter) {
+			authenticationEntryPoint = ref('authenticationEntryPoint')
+			accessDeniedHandler = ref('accessDeniedHandler')
+			portResolver = ref('portResolver')
+		}
+		accessDeniedHandler(GrailsAccessDeniedHandlerImpl) {
+			errorPage = conf.errorPage == 'null' ? null : conf.errorPage // '/login/denied' or 403
+			ajaxErrorPage = conf.ajaxErrorPage
+			portResolver = ref('portResolver')
+			if (conf.ajaxHeader) {
+				ajaxHeader = conf.ajaxHeader //default: X-Requested-With
+			}
+		}
+
+		if (!conf.useNtlm) {
+			authenticationEntryPoint(WithAjaxAuthenticationProcessingFilterEntryPoint) {
+				loginFormUrl = conf.loginFormUrl // '/login/auth'
+				forceHttps = conf.forceHttps // 'false'
+				ajaxLoginFormUrl = conf.ajaxLoginFormUrl // '/login/authAjax'
+				if (conf.ajaxHeader) {
+					ajaxHeader = conf.ajaxHeader //default: X-Requested-With
+				}
+				portMapper = ref('portMapper')
+				portResolver = ref('portResolver')
+			}
+		}
+
+		// voters
+		configureVoters.delegate = delegate
+		configureVoters conf
+
+		/** filterInvocationInterceptor */
+		filterInvocationInterceptor(FilterSecurityInterceptor) {
+			authenticationManager = ref('authenticationManager')
+			accessDecisionManager = ref('accessDecisionManager')
+			if (conf.useControllerAnnotations || conf.useRequestMapDomainClass) {
+				objectDefinitionSource = ref('objectDefinitionSource')
+			}
+			else {
+				objectDefinitionSource = conf.requestMapString
+			}
+		}
+		if (conf.useControllerAnnotations) {
+			objectDefinitionSource(AnnotationFilterInvocationDefinition) {
+				boolean lowercase = conf.controllerAnnotationsMatchesLowercase
+				if ('ant'.equals(conf.controllerAnnotationsMatcher)) {
+					urlMatcher = new AntUrlPathMatcher(lowercase)
+				}
+				else {
+					urlMatcher = new RegexUrlPathMatcher(lowercase)
+				}
+				if (conf.controllerAnnotationsRejectIfNoRule instanceof Boolean) {
+					rejectIfNoRule = conf.controllerAnnotationsRejectIfNoRule
+				}
+			}
+		}
+		else if (conf.useRequestMapDomainClass) {
+			objectDefinitionSource(RequestmapFilterInvocationDefinition) {
+				requestMapClass = conf.requestMapClass
+				requestMapPathFieldName = conf.requestMapPathField
+				requestMapConfigAttributeField = conf.requestMapConfigAttributeField
+				urlMatcher = new AntUrlPathMatcher(true)
+			}
+		}
+
+		/** anonymousAuthenticationProvider */
+		anonymousAuthenticationProvider(AnonymousAuthenticationProvider) {
+			key = conf.key // 'foo'
+		}
+		/** rememberMeAuthenticationProvider */
+		rememberMeAuthenticationProvider(RememberMeAuthenticationProvider) {
+			key = conf.rememberMeKey
+		}
+
+		// authenticationManager
+		configureAuthenticationManager.delegate = delegate
+		configureAuthenticationManager conf
+
+		/** daoAuthenticationProvider */
+		daoAuthenticationProvider(GrailsDaoAuthenticationProvider) {
+			userDetailsService = ref('userDetailsService')
+			passwordEncoder = ref('passwordEncoder')
+			userCache = ref('userCache')
+		}
+
+		/** passwordEncoder */
+		passwordEncoder(MessageDigestPasswordEncoder, conf.algorithm) {
+			if (conf.encodeHashAsBase64) {
+				encodeHashAsBase64 = true
+			}
+		}
+
+		// user details cache
+		if (conf.cacheUsers) {
+			userCache(EhCacheBasedUserCache) {
+				cache = ref('securityUserCache')
+			}
+			securityUserCache(EhCacheFactoryBean) {
+				cacheManager = ref('cacheManager')
+				cacheName = 'userCache'
+			}
+			cacheManager(EhCacheManagerFactoryBean)
+		}
+		else {
+			userCache(NullUserCache)
+		}
+
+		/** userDetailsService */
+		userDetailsService(GrailsDaoImpl) {
+			usernameFieldName = conf.userName
+			passwordFieldName = conf.password
+			enabledFieldName = conf.enabled
+			authorityFieldName = conf.authorityField
+			loginUserDomainClass = conf.loginUserDomainClass
+			relationalAuthoritiesField = conf.relationalAuthorities
+			authoritiesMethodName = conf.getAuthoritiesMethod
+			sessionFactory = ref('sessionFactory')
+			authenticateService = ref('authenticateService')
+		}
+
+		/** loggerListener ( log4j.logger.org.springframework.security=info,stdout ) */
+		if (conf.useLogger) {
+			loggerListener(LoggerListener)
+		}
+
+		// port mappings for channel security, etc.
+		portMapper(PortMapperImpl) {
+			portMappings = [(conf.httpPort.toString()) : conf.httpsPort.toString()]
+		}
+		portResolver(PortResolverImpl) {
+			portMapper = portMapper
+		}
+
+		daacc(DefaultAdvisorAutoProxyCreator)
+
+		// experiment on Annotation and MethodSecurityInterceptor for secure services
+		configureAnnotatedServices.delegate = delegate
+		configureAnnotatedServices conf
+
+		// simple email service
+		configureMail.delegate = delegate
+		configureMail conf
+
+		// Switch User
+		if (conf.switchUserProcessingFilter) {
+			switchUserProcessingFilter(SwitchUserProcessingFilter) {
+				userDetailsService = ref('userDetailsService')
+				switchUserUrl = conf.swswitchUserUrl
+				exitUserUrl = conf.swexitUserUrl
+				targetUrl = conf.swtargetUrl
+			}
+		}
+
+		// LDAP
+		if (conf.useLdap) {
+			configureLdap.delegate = delegate
+			configureLdap conf
+		}
+
+		// SecurityEventListener
+		securityEventListener(SecurityEventListener) {
+			authenticateService = ref('authenticateService')
+		}
+
+		// Kerberos
+		if (conf.useKerberos) {
+			configureKerberos.delegate = delegate
+			configureKerberos conf
+		}
+
+		// NTLM
+		if (conf.useNtlm) {
+			configureNtlm.delegate = delegate
+			configureNtlm conf
+		}
+
+		// CAS
+		if (conf.useCAS) {
+			configureCAS.delegate = delegate
+			configureCAS conf
+		}
+
+		// channel (http/https) security
+		if (useSecureChannel(conf)) {
+			configureChannelProcessingFilter.delegate = delegate
+			configureChannelProcessingFilter conf
+		}
+
+		// IP filter
+		if (conf.ipRestrictions) {
+			configureIpFilter.delegate = delegate
+			configureIpFilter conf
+		}
+	}
+
+	private boolean useSecureChannel(conf) {
+		return conf.secureChannelDefinitionSource || conf.channelConfig.secure || conf.channelConfig.insecure
+	}
+
+	// OpenID
+	private def configureOpenId = { conf ->
+		openIDAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.openid.GrailsOpenIdAuthenticationProvider) {
+			userDetailsService = ref('userDetailsService')
+		}
+		openIDStore(org.openid4java.consumer.InMemoryConsumerAssociationStore)
+		openIDNonceVerifier(org.openid4java.consumer.InMemoryNonceVerifier, conf.openIdNonceMaxSeconds) // 300 seconds
+		openIDConsumerManager(org.openid4java.consumer.ConsumerManager) {
+			nonceVerifier = openIDNonceVerifier
+		}
+		openIDConsumer(org.springframework.security.ui.openid.consumers.OpenID4JavaConsumer, openIDConsumerManager)
+		openIDAuthenticationProcessingFilter(org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1' // /spring_security_login?login_error
+			defaultTargetUrl = conf.defaultTargetUrl // '/'
+			filterProcessesUrl = '/j_spring_openid_security_check' // not configurable
+			rememberMeServices = ref('rememberMeServices')
+			consumer = openIDConsumer
+		}
+	}
+
+	// Facebook
+	private def configureFacebook = { conf ->
+		facebookAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookAuthenticationProvider) {
+			userDetailsService = ref('userDetailsService')
+		}
+		facebookAuthenticationProcessingFilter(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookAuthenticationProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			authenticationFailureUrl = conf.authenticationFailureUrl //'/login/authfail?login_error=1' // /spring_security_login?login_error
+			defaultTargetUrl = conf.defaultTargetUrl // '/'
+			filterProcessesUrl = conf.facebook.filterProcessesUrl // '/j_spring_facebook_security_check'
+			apiKey = conf.facebook.apiKey
+			secretKey = conf.facebook.secretKey
+			authenticationUrlRoot = conf.facebook.authenticationUrlRoot // http://www.facebook.com/login.php?v=1.0&api_key=
+			rememberMeServices = ref('rememberMeServices')
+		}
+		facebookLogoutHandler(org.codehaus.groovy.grails.plugins.springsecurity.facebook.FacebookLogoutHandler) {
+			apiKey = conf.facebook.apiKey
+		}
+	}
+
+	private def configureCAS = { conf ->
+		String casHost = conf.cas.casServer ?: 'localhost'
+		int casPort = (conf.cas.casServerPort ?: '443').toInteger()
+		String casFilterProcessesUrl = conf.cas.filterProcessesUrl ?: '/j_spring_cas_security_check'
+		boolean sendRenew = Boolean.valueOf(conf.cas.sendRenew ?: false)
+		String proxyReceptorUrl = conf.cas.proxyReceptorUrl ?: '/secure/receptor'
+		String applicationHost = System.getProperty('server.host') ?: 'localhost'
+		int applicationPort = (System.getProperty('server.port') ?: 8080).toInteger()
+		String appName = application.metadata['app.name']
+		String casHttp = conf.cas.casServerSecure ? 'https' : 'http'
+		String localHttp = conf.cas.localhostSecure ? 'https' : 'http'
+
+		proxyGrantingTicketStorage(org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl)
+
+		casProcessingFilter(org.springframework.security.ui.cas.CasProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			authenticationFailureUrl = conf.cas.failureURL ?: '/denied.jsp'
+			defaultTargetUrl = conf.cas.defaultTargetURL ?: '/'
+			filterProcessesUrl = casFilterProcessesUrl
+			proxyGrantingTicketStorage = proxyGrantingTicketStorage
+			proxyReceptorUrl = proxyReceptorUrl
+		}
+
+		casServiceProperties(org.springframework.security.ui.cas.ServiceProperties) {
+			service = "$localHttp://$applicationHost:$applicationPort/$appName$casFilterProcessesUrl"
+			sendRenew = sendRenew
+		}
+
+		String casLoginURL = conf.cas.fullLoginURL ?: "$casHttp://$casHost:$casPort/cas/login"
+		authenticationEntryPoint(org.springframework.security.ui.cas.CasProcessingFilterEntryPoint) {
+			loginUrl = casLoginURL
+			serviceProperties = casServiceProperties
+		}
+
+		String casServiceURL = conf.cas.fullServiceURL ?: "$casHttp://$casHost:$casPort/cas"
+		cas20ServiceTicketValidator(org.jasig.cas.client.validation.Cas20ServiceTicketValidator, casServiceURL) {
+			proxyGrantingTicketStorage = proxyGrantingTicketStorage
+			proxyCallbackUrl = "$localHttp://$applicationHost:$applicationPort/$appName$proxyReceptorUrl"
+		}
+
+		// the CAS authentication provider key doesn't need to be anything special, it just identifies individual providers
+		// so that they can identify tokens it previously authenticated
+		String casAuthenticationProviderKey = conf.cas.authenticationProviderKey ?: appName + System.currentTimeMillis()
+		casAuthenticationProvider(org.springframework.security.providers.cas.CasAuthenticationProvider) {
+			userDetailsService = ref(conf.cas.userDetailsService ?: 'userDetailsService')
+			serviceProperties = casServiceProperties
+			ticketValidator = cas20ServiceTicketValidator
+			key = casAuthenticationProviderKey
+		}
+	}
+
+	private def configureLogout = { conf ->
+
+		securityContextLogoutHandler(SecurityContextLogoutHandler)
+		def logoutHandlerNames = conf.logoutHandlerNames
+		if (!logoutHandlerNames) {
+			logoutHandlerNames = []
+			if (conf.useFacebook) {
+				logoutHandlerNames << 'facebookLogoutHandler'
+			}
+			else if (!conf.useOpenId) {
+				logoutHandlerNames << 'rememberMeServices'
+			}
+			logoutHandlerNames << 'securityContextLogoutHandler'
+		}
+
+		def logoutHandlers = createRefList(logoutHandlerNames)
+		def afterLogoutUrl = conf.afterLogoutUrl // '/'
+
+		/** logoutFilter */
+		logoutFilter(LogoutFilterFactoryBean) {
+			logoutSuccessUrl = afterLogoutUrl
+			handlers = logoutHandlers
+		}
+	}
+
+	private def configureBasicAuth = { conf ->
+
+		basicProcessingFilterEntryPoint(BasicProcessingFilterEntryPoint) {
+			realmName = conf.realmName // 'Grails Realm'
+		}
+		basicProcessingFilter(BasicProcessingFilter) {
+			authenticationManager = ref('authenticationManager')
+			authenticationEntryPoint = basicProcessingFilterEntryPoint
+		}
+	}
+
+	private def configureVoters = { conf ->
+
+		roleVoter(RoleVoter)
+
+		authenticatedVoter(AuthenticatedVoter)
+
+		def decisionVoterNames = conf.decisionVoterNames
+		if (!decisionVoterNames) {
+			decisionVoterNames = ['authenticatedVoter', 'roleVoter']
+		}
+		def decisionVoterList = createRefList(decisionVoterNames)
+		/** accessDecisionManager */
+		accessDecisionManager(AuthenticatedVetoableDecisionManager) {
+			allowIfAllAbstainDecisions = false
+			decisionVoters = decisionVoterList
+		}
+	}
+
+	private def configureAuthenticationManager = { conf ->
+
+		def providerNames = conf.providerNames
+		if (!providerNames) {
+			providerNames = []
+			if (conf.useKerberos) {
+				providerNames << 'kerberosAuthProvider'
+			}
+			if (conf.useCAS) {
+				providerNames << 'casAuthenticationProvider'
+			}
+			if (conf.useLdap) {
+				providerNames << 'ldapAuthProvider'
+			}
+			if (conf.useFacebook) {
+				providerNames << 'facebookAuthProvider'
+			}
+
+			if (providerNames.empty) {
+				providerNames << 'daoAuthenticationProvider'
+				if (conf.useOpenId) {
+					providerNames << 'openIDAuthProvider'
+				}
+			}
+
+			providerNames << 'anonymousAuthenticationProvider'
+			providerNames << 'rememberMeAuthenticationProvider'
+		}
+
+		def providerList = createRefList(providerNames)
+		/** authenticationManager */
+		authenticationManager(ProviderManager) {
+			providers = providerList
+		}
+	}
+
+	private def configureAnnotatedServices = { conf ->
+
+		serviceSecureAnnotation(SecurityAnnotationAttributes)
+
+		serviceSecureAnnotationODS(MethodDefinitionAttributes) {
+			attributes = serviceSecureAnnotation
+		}
+
+		/** securityInteceptor */
+		securityInteceptor(QuietMethodSecurityInterceptor) {
+			validateConfigAttributes = false
+			authenticationManager = ref('authenticationManager')
+			accessDecisionManager = ref('accessDecisionManager')
+			objectDefinitionSource = serviceSecureAnnotationODS
+			throwException = true
+		}
+
+		// load Services which have Annotations
+		application.serviceClasses.each { serviceClass ->
+			if (hasAnnotation(serviceClass.clazz)) {
+				"${serviceClass.propertyName}Sec"(BeanNameAutoProxyCreator) {
+					beanNames = serviceClass.propertyName
+					interceptorNames = ['securityInteceptor']
+					proxyTargetClass = true
+				}
+			}
+		}
+	}
+
+	private def configureMail = { conf ->
+
+		if (conf.useMail) {
+			mailSender(org.springframework.mail.javamail.JavaMailSenderImpl) {
+				host = conf.mailHost
+				username = conf.mailUsername
+				password = conf.mailPassword
+				protocol = conf.mailProtocol
+				port = conf.mailPort
+				if (conf.javaMailProperties) {
+					javaMailProperties = conf.javaMailProperties as Properties
+				}
+			}
+
+			mailMessage(org.springframework.mail.SimpleMailMessage) {
+				from = conf.mailFrom
+			}
+		}
+	}
+
+	private def configureLdap = { conf ->
+
+		contextSource(org.springframework.security.ldap.DefaultSpringSecurityContextSource, conf.ldapServer) {
+			userDn = conf.ldapManagerDn
+			password = conf.ldapManagerPassword
+		}
+
+		ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
+			           conf.ldapSearchBase, conf.ldapSearchFilter, contextSource) {
+			searchSubtree = conf.ldapSearchSubtree
+		}
+
+		ldapAuthenticator(org.springframework.security.providers.ldap.authenticator.BindAuthenticator,
+			              contextSource) {
+			userSearch = ldapUserSearch
+		}
+
+		ldapUserDetailsMapper(org.codehaus.groovy.grails.plugins.springsecurity.ldap.GrailsLdapUserDetailsMapper) {
+			userDetailsService = ref('userDetailsService')
+			passwordAttributeName = conf.ldapPasswordAttributeName // 'userPassword'
+			usePassword = conf.ldapUsePassword // true
+			retrieveDatabaseRoles = conf.ldapRetrieveDatabaseRoles // false
+		}
+
+		if (conf.ldapRetrieveGroupRoles) {
+			ldapAuthoritiesPopulator(org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator,
+				                     contextSource, conf.ldapGroupSearchBase) {
+				groupRoleAttribute = conf.ldapGroupRoleAttribute
+				groupSearchFilter = conf.ldapGroupSearchFilter
+				searchSubtree = conf.ldapSearchSubtree
+			}
+			ldapAuthProvider(org.springframework.security.providers.ldap.LdapAuthenticationProvider,
+				             ldapAuthenticator, ldapAuthoritiesPopulator) {
+				userDetailsContextMapper = ldapUserDetailsMapper
+			}
+		}
+		else {
+			// use the NullAuthoritiesPopulator
+			ldapAuthProvider(org.springframework.security.providers.ldap.LdapAuthenticationProvider,
+				             ldapAuthenticator) {
+				userDetailsContextMapper = ldapUserDetailsMapper
+			}
+		}
+	}
+
+	private def configureKerberos = { conf ->
+
+		jaasNameCallbackHandler(org.springframework.security.providers.jaas.JaasNameCallbackHandler)
+
+		jaasPasswordCallbackHandler(org.springframework.security.providers.jaas.JaasPasswordCallbackHandler)
+
+		kerberosAuthProvider(org.codehaus.groovy.grails.plugins.springsecurity.kerberos.GrailsKerberosAuthenticationProvider) {
+			authenticateService = ref('authenticateService')
+			userDetailsService = ref('userDetailsService')
+			loginConfig = conf.kerberosLoginConfigFile
+			loginContextName = 'KrbAuthentication'
+			callbackHandlers = [jaasNameCallbackHandler, jaasPasswordCallbackHandler]
+			authorityGranters = []
+		}
+
+		//TODO: Improve
+		System.setProperty('java.security.krb5.realm', conf.kerberosRealm)
+		System.setProperty('java.security.krb5.kdc', conf.kerberosKdc)
+	}
+
+	private def configureNtlm = { conf ->
+
+		ntlmFilter(org.springframework.security.ui.ntlm.NtlmProcessingFilter) {
+			stripDomain = conf.ntlm.stripDomain // true
+			retryOnAuthFailure = conf.ntlm.retryOnAuthFailure // true
+			defaultDomain = conf.ntlm.defaultDomain
+			netbiosWINS = conf.ntlm.netbiosWINS
+			forceIdentification = conf.ntlm.forceIdentification // false
+			authenticationManager = ref('authenticationManager')
+		}
+
+		authenticationEntryPoint(org.codehaus.groovy.grails.plugins.springsecurity.GrailsNtlmProcessingFilterEntryPoint) {
+			authenticationFailureUrl = conf.authenticationFailureUrl
+		}
+	}
+
+	private def configureFilterChain = { conf ->
+
+		def filterNames = conf.filterNames
+		if (!filterNames) {
+			filterNames = []
+
+			if (useSecureChannel(conf)) {
+				filterNames << 'channelProcessingFilter' // CHANNEL_FILTER
+			}
+
+			// CONCURRENT_SESSION_FILTER
+
+			filterNames << 'httpSessionContextIntegrationFilter' // HTTP_SESSION_CONTEXT_FILTER
+
+			filterNames << 'logoutFilter' // LOGOUT_FILTER
+
+			if (conf.ipRestrictions) {
+				filterNames << 'ipAddressFilter'
+			}
+
+			// X509_FILTER
+
+			// PRE_AUTH_FILTER
+
+			if (conf.useCAS) {
+				filterNames << 'casProcessingFilter' // CAS_PROCESSING_FILTER
+			}
+
+			filterNames << 'authenticationProcessingFilter' // AUTHENTICATION_PROCESSING_FILTER
+
+			if (conf.useOpenId) {
+				filterNames << 'openIDAuthenticationProcessingFilter' // OPENID_PROCESSING_FILTER
+			}
+
+			if (conf.useFacebook) {
+				filterNames << 'facebookAuthenticationProcessingFilter'
+			}
+
+			// LOGIN_PAGE_FILTER
+
+			if (conf.basicProcessingFilter) {
+				filterNames << 'basicProcessingFilter' // BASIC_PROCESSING_FILTER
+			}
+
+			if (!conf.useNtlm) {
+				// seems to remove NTLM authentication tokens
+				filterNames << 'securityContextHolderAwareRequestFilter' // SERVLET_API_SUPPORT_FILTER
+			}
+
+			filterNames << 'rememberMeProcessingFilter' // REMEMBER_ME_FILTER
+
+			filterNames << 'anonymousProcessingFilter' // ANONYMOUS_FILTER
+
+			filterNames << 'exceptionTranslationFilter' // EXCEPTION_TRANSLATION_FILTER
+
+			if (conf.useNtlm) {
+				filterNames << 'ntlmFilter' // NTLM_FILTER
+			}
+
+			// SESSION_FIXATION_FILTER
+
+			filterNames << 'filterInvocationInterceptor' // FILTER_SECURITY_INTERCEPTOR
+
+			if (conf.switchUserProcessingFilter) {
+				filterNames << 'switchUserProcessingFilter' // SWITCH_USER_FILTER
+			}
+		}
+		String joinedFilters = filterNames.join(',')
+
+		String definitionSource
+		if (conf.filterInvocationDefinitionSource) {
+			// if the entire string is set in the config, use that
+			definitionSource = conf.filterInvocationDefinitionSource
+		}
+		else if (conf.filterInvocationDefinitionSourceMap) {
+			// otherwise if there's a map of configs, use those
+			definitionSource = DEFINITION_SOURCE_PREFIX
+			conf.filterInvocationDefinitionSourceMap.each { key, value ->
+				if (value == 'JOINED_FILTERS') {
+					// special case to use either the filters defined by
+					// conf.filterNames or the filters defined by config settings
+					value = joinedFilters
+				}
+				definitionSource += "$key=$value\n"
+			}
+		}
+		else {
+			// otherwise build the default string - all urls guarded by all filters
+			definitionSource = "$DEFINITION_SOURCE_PREFIX\n/**=$joinedFilters"
+		}
+		springSecurityFilterChain(FilterChainProxy) {
+			filterInvocationDefinitionSource = definitionSource
+		}
+	}
+
+	private def configureChannelProcessingFilter = { conf ->
+
+		retryWithHttpEntryPoint(RetryWithHttpEntryPoint) {
+			portMapper = ref('portMapper')
+			portResolver = ref('portResolver')
+		}
+
+		retryWithHttpsEntryPoint(RetryWithHttpsEntryPoint) {
+			portMapper = ref('portMapper')
+			portResolver = ref('portResolver')
+		}
+
+		secureChannelProcessor(SecureChannelProcessor) {
+			entryPoint = retryWithHttpsEntryPoint
+		}
+
+		insecureChannelProcessor(InsecureChannelProcessor) {
+			entryPoint = retryWithHttpEntryPoint
+		}
+
+		channelDecisionManager(ChannelDecisionManagerImpl) {
+			channelProcessors = [insecureChannelProcessor, secureChannelProcessor]
+		}
+
+		String definitionSource
+		if (conf.secureChannelDefinitionSource) {
+			// if the entire string is set in the config, use that
+			definitionSource = conf.secureChannelDefinitionSource
+		}
+		else {
+			definitionSource = DEFINITION_SOURCE_PREFIX
+			conf.channelConfig.secure.each { pattern ->
+				definitionSource += "$pattern=REQUIRES_SECURE_CHANNEL\n"
+			}
+			conf.channelConfig.insecure.each { pattern ->
+				definitionSource += "$pattern=REQUIRES_INSECURE_CHANNEL\n"
+			}
+		}
+		channelProcessingFilter(ChannelProcessingFilter) {
+			channelDecisionManager = channelDecisionManager
+			filterInvocationDefinitionSource = definitionSource
+		}
+	}
+
+	private def configureIpFilter = { conf ->
+		ipAddressFilter(IpAddressFilter) {
+			ipRestrictions = conf.ipRestrictions
+		}
+	}
+
+	def doWithApplicationContext = { ctx ->
+		// nothing to do
+	}
+
+	def doWithWebDescriptor = { xml ->
+
+		def conf = AuthorizeTools.securityConfig.security
+		if (!conf || !conf.active) {
+			return
+		}
+
+		// we add the filter(s) right after the last context-param
+		def contextParam = xml.'context-param'
+
+		// the name of the filter matches the name of the Spring bean that it delegates to
+		contextParam[contextParam.size() - 1] + {
+			'filter' {
+				'filter-name'('springSecurityFilterChain')
+				'filter-class'(DelegatingFilterProxy.name)
+			}
+		}
+
+		// add the filter-mapping after the Spring character encoding filter
+		findMappingLocation.delegate = delegate
+		def mappingLocation = findMappingLocation(xml)
+		mappingLocation + {
+			'filter-mapping'{
+				'filter-name'('springSecurityFilterChain')
+				'url-pattern'('/*')
+			}
+		}
+
+		if (conf.useHttpSessionEventPublisher) {
+			def filterMapping = xml.'filter-mapping'
+			filterMapping[filterMapping.size() - 1] + {
+				'listener' {
+					'listener-class'(HttpSessionEventPublisher.name)
+				}
+			}
+		}
+	}
+
+	private def findMappingLocation = { xml ->
+
+		// find the location to insert the filter-mapping; needs to be after the 'charEncodingFilter'
+		// which may not exist. should also be before the sitemesh filter.
+		// thanks to the JSecurity plugin for the logic.
+
+		def mappingLocation = xml.'filter-mapping'.find { it.'filter-name'.text() == 'charEncodingFilter' }
+		if (mappingLocation) {
+			return mappingLocation
+		}
+
+		// no 'charEncodingFilter'; try to put it before sitemesh
+		int i = 0
+		int siteMeshIndex = -1
+		xml.'filter-mapping'.each {
+			if (it.'filter-name'.text().equalsIgnoreCase('sitemesh')) {
+				siteMeshIndex = i
+			}
+			i++
+		}
+		if (siteMeshIndex > 0) {
+			return xml.'filter-mapping'[siteMeshIndex - 1]
+		}
+
+		if (siteMeshIndex == 0 || xml.'filter-mapping'.size() == 0) {
+			def filters = xml.'filter'
+			return filters[filters.size() - 1]
+		}
+
+		// neither filter found
+		def filters = xml.'filter'
+		return filters[filters.size() - 1]
+	}
+
+	def doWithDynamicMethods = { ctx ->
+
+		def conf = AuthorizeTools.securityConfig.security
+		if (!conf || !conf.active) {
+			return
+		}
+
+		for (controllerClass in application.controllerClasses) {
+			addControllerMethods controllerClass.metaClass
+		}
+
+		if (conf.useControllerAnnotations) {
+			ctx.objectDefinitionSource.initialize conf.controllerAnnotationStaticRules,
+				ctx.grailsUrlMappingsHolder, application.controllerClasses
+		}
+	}
+
+	def onChange = { event ->
+
+		def conf = AuthorizeTools.securityConfig.security
+		if (!conf || !conf.active) {
+			return
+		}
+
+		def ctx = event.ctx
+		if (event.source && ctx && event.application) {
+			boolean isControllerClass = application.isControllerClass(event.source)
+			boolean configChanged = 'SecurityConfig'.equals(event.source.name)
+			if (configChanged || isControllerClass) {
+				if (conf.useControllerAnnotations) {
+					ctx.objectDefinitionSource.initialize conf.controllerAnnotationStaticRules,
+						ctx.grailsUrlMappingsHolder, application.controllerClasses
+				}
+				if (isControllerClass) {
+					addControllerMethods application.getControllerClass(event.source.name).metaClass
+				}
+			}
+		}
+	}
+
+	def onApplicationChange = { event ->
+		// nothing to do
+	}
+
+	private void addControllerMethods(MetaClass mc) {
+		mc.getAuthUserDomain = {
+			def principal = SCH.context?.authentication?.principal
+			if (principal != null && principal != 'anonymousUser') {
+				return principal?.domainClass
+			}
+
+			return null
+		}
+
+		mc.getPrincipalInfo = {
+			return SCH.context?.authentication?.principal
+		}
+
+		mc.isUserLogon = {
+			def principal = SCH.context?.authentication?.principal
+			return principal != null && principal != 'anonymousUser'
+		}
+	}
+
+	private boolean hasAnnotation(serviceClass) {
+		for (method in serviceClass.methods) {
+			if (method.isAnnotationPresent(SecuredService)) {
+				return true
+			}
+		}
+
+		return false
+	}
+
+	private def createRefList = { names ->
+		names.collect { name -> ref(name) }
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/application.properties
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/application.properties	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/application.properties	(revision 58)
@@ -0,0 +1,5 @@
+#utf-8
+#Mon Jan 12 23:53:53 EST 2009
+plugins.code-coverage=1.1.1
+app.grails.version=1.1-beta2
+app.name=acegi
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/conf/DefaultSecurityConfig.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/conf/DefaultSecurityConfig.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/conf/DefaultSecurityConfig.groovy	(revision 58)
@@ -0,0 +1,196 @@
+security {
+
+	/** enable Spring Security or not */
+	active = false
+
+	/** login user class fields (default user class = Person)*/
+	loginUserDomainClass = 'Person'
+	userName = 'username'
+	password = 'passwd'
+	enabled = 'enabled'
+	relationalAuthorities = 'authorities'
+	//you can specify method for to retrieve the roles. (you need to set relationalAuthorities=null)
+	getAuthoritiesMethod = null // 'getMoreAuthorities'
+
+	/**
+	 * Authority domain class authority field name
+	 * authorityFieldInList
+	 */
+	authorityDomainClass = 'Authority'
+	authorityField = 'authority'
+
+	/** authenticationProcessingFilter */
+	authenticationFailureUrl = '/login/authfail?login_error=1'
+	ajaxAuthenticationFailureUrl = '/login/authfail?ajax=true'
+	defaultTargetUrl = '/'
+	alwaysUseDefaultTargetUrl = false
+	filterProcessesUrl = '/j_spring_security_check'
+
+	/** anonymousProcessingFilter */
+	key = 'foo'
+	userAttribute = 'anonymousUser,ROLE_ANONYMOUS'
+
+	/** authenticationEntryPoint */
+	loginFormUrl = '/login/auth'
+	forceHttps = 'false'
+	ajaxLoginFormUrl = '/login/authAjax'
+
+	/** logoutFilter */
+	afterLogoutUrl = '/'
+
+	/** accessDeniedHandler
+	 *  set errorPage to null, if you want to get error code 403 (FORBIDDEN).
+	 */
+	errorPage = '/login/denied'
+	ajaxErrorPage = '/login/deniedAjax'
+	ajaxHeader = 'X-Requested-With'
+
+	/** passwordEncoder */
+	//The digest algorithm to use.
+	//Supports the named Message Digest Algorithms in the Java environment.
+	//http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA
+	algorithm = 'SHA' // Ex. MD5 SHA
+	//use Base64 text ( true or false )
+	encodeHashAsBase64 = false
+
+	/** rememberMeServices */
+	cookieName = 'grails_remember_me'
+	alwaysRemember = false
+	tokenValiditySeconds = 1209600 //14 days
+	parameter = '_spring_security_remember_me'
+	rememberMeKey = 'grailsRocks'
+
+	/** LoggerListener
+	 * ( add 'log4j.logger.org.springframework.security=info,stdout'
+	 * to log4j.*.properties to see logs )
+	 */
+	useLogger = false
+
+	/** use RequestMap from DomainClass */
+	useRequestMapDomainClass = true
+
+	/** Requestmap domain class (if useRequestMapDomainClass = true) */
+	requestMapClass = 'Requestmap'
+	requestMapPathField = 'url'
+	requestMapConfigAttributeField = 'configAttribute'
+
+	/** use annotations from Controllers to define security rules */
+	useControllerAnnotations = false
+	controllerAnnotationsMatcher = 'ant' // or 'regex'
+	controllerAnnotationsMatchesLowercase = true
+	controllerAnnotationStaticRules = [:]
+	controllerAnnotationsRejectIfNoRule = false
+
+	/**
+	 * if useRequestMapDomainClass is false, set request map pattern in string
+	 * see example below
+	 */
+	requestMapString = """
+		CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
+		PATTERN_TYPE_APACHE_ANT
+
+		/login/**=IS_AUTHENTICATED_ANONYMOUSLY
+		/admin/**=ROLE_USER
+		/book/test/**=IS_AUTHENTICATED_FULLY
+		/book/**=ROLE_SUPERVISOR
+		/**=IS_AUTHENTICATED_ANONYMOUSLY
+	"""
+
+	// basic auth
+	realmName = 'Grails Realm'
+
+	/** use basicProcessingFilter */
+	basicProcessingFilter = false
+	/** use switchUserProcessingFilter */
+	switchUserProcessingFilter = false
+	swswitchUserUrl = '/j_spring_security_switch_user'
+	swexitUserUrl = '/j_spring_security_exit_user'
+	swtargetUrl = '/'
+
+	/**use email notification while registration*/
+	useMail = false
+	mailHost = 'localhost'
+	mailUsername = 'user@localhost'
+	mailPassword = 'sungod'
+	mailProtocol = 'smtp'
+	mailFrom = 'user@localhost'
+	mailPort = 25
+
+	/** default user's role for user registration */
+	defaultRole = 'ROLE_USER'
+
+	// OpenId
+	useOpenId = false
+	openIdNonceMaxSeconds = 300 // max time between auth start and end
+
+	// LDAP/ActiveDirectory
+	useLdap = false
+	ldapRetrieveGroupRoles = true
+	ldapRetrieveDatabaseRoles = false
+	ldapSearchSubtree = true
+	ldapGroupRoleAttribute = 'cn'
+	ldapPasswordAttributeName = 'userPassword'
+	ldapServer = 'ldap://localhost:389' // 'ldap://ad.example.com', 'ldap://monkeymachine:389/dc=acegisecurity,dc=org'
+	ldapManagerDn = 'cn=admin,dc=example,dc=com'
+	ldapManagerPassword = 'secret'
+	ldapSearchBase = 'dc=example,dc=com' // 'ou=users,dc=example,dc=com'
+	ldapSearchFilter = '(uid={0})' //, '(mailNickname={0})'
+	ldapGroupSearchBase = 'ou=groups,dc=example,dc=com'
+	ldapGroupSearchFilter = 'uniquemember={0}'
+	ldapUsePassword = true
+
+	// Kerberos
+	useKerberos = false
+	kerberosLoginConfigFile = 'WEB-INF/jaas.conf'
+	kerberosRealm = 'KERBEROS.REALM'
+	kerberosKdc = 'krbserver.domain.lan'
+	kerberosRetrieveDatabaseRoles = true
+
+	// HttpSessionEventPublisher
+	useHttpSessionEventPublisher = false
+
+	// user caching
+	cacheUsers = true
+
+	// CAS
+	useCAS = false
+	cas.casServer = 'localhost'
+	cas.casServerPort = '443'
+	cas.casServerSecure = true
+	cas.localhostSecure = true
+	cas.failureURL = '/denied.jsp'
+	cas.defaultTargetURL = '/'
+	cas.fullLoginURL = 'https://localhost:443/cas/login'
+	cas.fullServiceURL = 'https://localhost:443/cas'
+	cas.authenticationProviderKey = 'cas_key_changeme'
+	cas.userDetailsService = 'userDetailsService'
+	cas.sendRenew = false
+	cas.proxyReceptorUrl = '/secure/receptor'
+	cas.filterProcessesUrl = '/j_spring_cas_security_check'
+
+	// NTLM
+	useNtlm = false
+	ntlm.stripDomain = true
+	ntlm.retryOnAuthFailure = true
+	ntlm.forceIdentification = false
+	ntlm.defaultDomain = null // set in SecurityConfig.groovy
+	ntlm.netbiosWINS = null // set in SecurityConfig.groovy
+
+	// port mappings
+	httpPort = 8080
+	httpsPort = 8443
+
+	// secure channel filter (http/https)
+	secureChannelDefinitionSource = ''
+	channelConfig = [secure: [], insecure: []]
+
+	// ip restriction filter
+	ipRestrictions = [:]
+
+	// Facebook Connect
+	useFacebook = false
+	facebook.filterProcessesUrl = '/j_spring_facebook_security_check'
+	facebook.authenticationUrlRoot = 'http://www.facebook.com/login.php?v=1.0&api_key='
+	facebook.apiKey = '' // set in SecurityConfig
+	facebook.secretKey = '' // set in SecurityConfig
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/controllers/org/grails/plugins/springsecurity/controller/AuthBase.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/controllers/org/grails/plugins/springsecurity/controller/AuthBase.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/controllers/org/grails/plugins/springsecurity/controller/AuthBase.groovy	(revision 58)
@@ -0,0 +1,85 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.grails.plugins.springsecurity.controller
+
+import org.grails.plugins.springsecurity.service.AuthenticateService
+
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.web.servlet.support.RequestContextUtils as RCU
+
+/**
+ * [Example] Controllers Base class for to use Spring Security (authentication and authorization).
+ * Usage: class SomeController extends AuthBase { }
+ * @author T.Yamamoto
+ */
+class AuthBase {
+
+	/** Authenticate Service */
+	def authenticateService
+
+	/** Authenticated user domain instance */
+	def loginUser
+
+	/** is user logged on or not */
+	boolean logon
+
+	/** principal */
+	def authPrincipal
+
+	/** is Admin */
+	boolean isAdmin
+
+	/** Locale */
+	Locale locale
+
+	/** main request permission setting */
+	def requestAllowed
+
+	def beforeInterceptor = {
+		if (requestAllowed != null && !authenticateService.ifAnyGranted(requestAllowed)) {
+			println 'request not allowed: ' + requestAllowed
+			redirect(uri: '/')
+		 	return
+		}
+
+		authPrincipal = SCH?.context?.authentication?.principal
+		if (authPrincipal != null && authPrincipal != 'anonymousUser') {
+			loginUser = authPrincipal?.domainClass
+			logon = true
+			isAdmin = authenticateService.ifAnyGranted('ROLE_SUPERVISOR')
+		}
+
+		/* i18n: if lang params */
+		if (params['lang']) {
+			locale = new Locale(params['lang'])
+			RCU.getLocaleResolver(request).setLocale(request,response,locale)
+			session.lang = params['lang']
+		}
+		/* need this for jetty */
+		if (session.lang != null) {
+			locale = new Locale(session.lang)
+			RCU.getLocaleResolver(request).setLocale(request,response,locale)
+		}
+		if (locale == null) {
+			locale = RCU.getLocale(request)
+		}
+
+		/* cache */
+		response.setHeader('Cache-Control','no-cache') // HTTP 1.1
+		response.setDateHeader('max-age', 0) 
+		response.setIntHeader ('Expires', -1) //prevents caching at the proxy server 
+		response.addHeader('cache-Control', 'private') //IE5.x only
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/services/org/grails/plugins/springsecurity/service/AuthenticateService.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/services/org/grails/plugins/springsecurity/service/AuthenticateService.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/services/org/grails/plugins/springsecurity/service/AuthenticateService.groovy	(revision 58)
@@ -0,0 +1,216 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.grails.plugins.springsecurity.service
+
+import org.codehaus.groovy.grails.plugins.springsecurity.AuthorizeTools
+
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.security.ui.AbstractProcessingFilter as APF
+import org.springframework.security.userdetails.UserDetails
+
+/**
+ * Authentication utility methods.
+ *
+ * @author T.Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class AuthenticateService {
+
+	boolean transactional = false
+
+	private securityConfig
+
+	/** dependency injection for {@link GrailsFilterInvocationDefinition} */
+	def objectDefinitionSource
+
+	/** dependency injection for the password encoder */
+	def passwordEncoder
+
+	/**
+	 * @deprecated You can invoke tags from controllers (since grails-0.6)
+	 */
+	boolean ifAllGranted(role) {
+		return AuthorizeTools.ifAllGranted(role)
+	}
+
+	/**
+	 * @deprecated You can invoke tags from controllers (since grails-0.6)
+	 */
+	boolean ifNotGranted(role) {
+		return AuthorizeTools.ifNotGranted(role)
+	}
+
+	/**
+	 * @deprecated You can invoke tags from controllers (since grails-0.6)
+	 */
+	boolean ifAnyGranted(role) {
+		return AuthorizeTools.ifAnyGranted(role)
+	}
+
+	/**
+	 * Get the currently logged in user's principal.
+	 * @return the principal or <code>null</code> if not logged in
+	 */
+	def principal() {
+		return SCH?.context?.authentication?.principal
+	}
+
+	/**
+	 * Get the currently logged in user's domain class.
+	 * @return the domain object or <code>null</code> if not logged in
+	 */
+	def userDomain() {
+		return isLoggedIn() ? principal().domainClass : null
+	}
+
+	/**
+	 * Load the security configuration.
+	 * @return the config
+	 */
+	ConfigObject getSecurityConfig() {
+		if (securityConfig == null) {
+			securityConfig = AuthorizeTools.getSecurityConfig()
+		}
+		return securityConfig
+	}
+
+	/**
+	 * returns a MessageDigest password.
+	 * (changes algorithm method dynamically by param of config)
+	 * @deprecated use <code>encodePassword</code> instead
+	 */
+	String passwordEncoder(String passwd) {
+		return encodePassword(passwd)
+	}
+
+	String encodePassword(String passwd) {
+		return passwordEncoder.encodePassword(passwd, null)
+	}
+
+	/**
+	 * Check if the request was triggered by an Ajax call.
+	 * @param request the request
+	 * @return <code>true</code> if Ajax
+	 */
+	boolean isAjax(request) {
+
+		// look for an ajax=true parameter
+		if ('true' == request.getParameter('ajax')) {
+			return true
+		}
+
+		// check the current request's headers
+		String ajaxHeader = getSecurityConfig().security.ajaxHeader
+		if (request.getHeader(ajaxHeader) != null) {
+			return true
+		}
+
+		// check the SavedRequest's headers
+		def savedRequest = request.session.getAttribute(APF.SPRING_SECURITY_SAVED_REQUEST_KEY)
+		if (savedRequest) {
+			return savedRequest.getHeaderValues(ajaxHeader).hasNext()
+		}
+
+		return false
+	}
+
+	/**
+	 * Quick check to see if the current user is logged in.
+	 * @return <code>true</code> if the principal is a {@link UserDetails} or subclass
+	 */
+	boolean isLoggedIn() {
+		return principal() instanceof UserDetails
+	}
+
+	/**
+	 * Call when editing, creating, or deleting a Requestmap to flush the cached
+	 * configuration and rebuild using the most recent data.
+	 */
+	void clearCachedRequestmaps() {
+		objectDefinitionSource.reset()
+	}
+
+	/**
+	 * Delete a role, and if Requestmap class is used to store roles, remove the role
+	 * from all Requestmap definitions. If a Requestmap's config attribute is this role,
+	 * it will be deleted.
+	 *
+	 * @param role  the role to delete
+	 */
+	void deleteRole(role) {
+		def conf = getSecurityConfig().security
+		String configAttributeName = conf.requestMapConfigAttributeField
+
+		role.getClass().withTransaction { status ->
+			if (conf.useRequestMapDomainClass) {
+				String roleName = role.authority
+				def requestmaps = findRequestmapsByRole(roleName, role.getClass(), conf)
+				requestmaps.each { rm ->
+					String configAttribute = rm."$configAttributeName"
+					if (configAttribute.equals(roleName)) {
+						rm.delete()
+					}
+					else {
+						List parts = configAttribute.split(',') as List
+						parts.remove roleName
+						rm."$configAttributeName" = parts.join(',')
+					}
+				}
+				clearCachedRequestmaps()
+			}
+
+			role.delete()
+		}
+	}
+
+	/**
+	 * Update a role, and if Requestmap class is used to store roles, replace the new role
+	 * name in all Requestmap definitions that use it if the name was changed.
+	 *
+	 * @param role  the role to update
+	 * @param newProperties  the new role attributes ('params' from the calling controller)
+	 */
+	boolean updateRole(role, newProperties) {
+
+		String oldRoleName = role.authority
+		role.properties = newProperties
+
+		def conf = getSecurityConfig().security
+
+		String configAttributeName = conf.requestMapConfigAttributeField
+		if (conf.useRequestMapDomainClass) {
+			String newRoleName = role.authority
+			if (newRoleName != oldRoleName) {
+				def requestmaps = findRequestmapsByRole(oldRoleName, role.getClass(), conf)
+				requestmaps.each { rm ->
+					rm."$configAttributeName" = rm."$configAttributeName".replace(oldRoleName, newRoleName)
+				}
+			}
+			clearCachedRequestmaps()
+		}
+
+		role.save()
+		return !role.hasErrors()
+	}
+
+	private List findRequestmapsByRole(String roleName, domainClass, conf) {
+		String requestmapClassName = conf.requestMapClass
+		String configAttributeName = conf.requestMapConfigAttributeField
+		return domainClass.executeQuery(
+				"SELECT rm FROM $requestmapClassName rm " +
+				"WHERE rm.$configAttributeName LIKE :roleName",
+				[roleName: "%$roleName%"])
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/taglib/org/grails/plugins/springsecurity/taglib/AuthorizeTagLib.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/taglib/org/grails/plugins/springsecurity/taglib/AuthorizeTagLib.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/grails-app/taglib/org/grails/plugins/springsecurity/taglib/AuthorizeTagLib.groovy	(revision 58)
@@ -0,0 +1,113 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.grails.plugins.springsecurity.taglib
+
+import org.springframework.security.context.SecurityContextHolder as SCH
+
+import org.codehaus.groovy.grails.plugins.springsecurity.AuthorizeTools
+
+/**
+ * Authorize Taglibs.
+ * Rewritten in Groovy from Java source of org.acegisecurity.taglibs.authz.AuthorizeTag.
+ *
+ * @author T.Yamamoto
+ */
+class AuthorizeTagLib {
+
+	/**
+	 * <g:ifAllGranted role="ROLE_USER,ROLE_ADMIN,ROLE_SUPERVISOR">
+	 *  All the listed roles must be granted for the tag to output its body.
+	 * </g:ifAllGranted>
+	 */
+	def ifAllGranted = { attrs, body ->
+		if (AuthorizeTools.ifAllGranted(attrs.role)) {
+			out << body()
+		}
+	}
+
+	/**
+	 * <g:ifNotGranted role="ROLE_USER,ROLE_ADMIN,ROLE_SUPERVISOR">
+	 *  None of the listed roles must be granted for the tag to output its body.
+	 * </g:ifNotGranted>
+	 */
+	def ifNotGranted = { attrs, body ->
+		if (AuthorizeTools.ifNotGranted(attrs.role)) {
+			out << body()
+		}
+	}
+
+	/**
+	 * <g:ifAnyGranted role="ROLE_USER,ROLE_ADMIN,ROLE_SUPERVISOR">
+	 *  Any of the listed roles must be granted for the tag to output its body.
+	 * </g:ifAnyGranted>
+	 */
+	def ifAnyGranted = { attrs, body ->
+		if (AuthorizeTools.ifAnyGranted(attrs.role)) {
+			out << body()
+		}
+	}
+
+	/**
+	 * <g:loggedInUserInfo field="userRealName">Guest User</g:loggedInUserInfo>
+	 */
+	def loggedInUserInfo = { attrs, body ->
+		if (isAuthenticated()) {
+			def source = determineSource()
+			out << source."$attrs.field"
+		}
+		else {
+			out << body()
+		}
+	}
+
+	private def determineSource() {
+		def principal = SCH.context.authentication.principal
+		def source
+
+		// check to see if it's a GrailsUser/GrailsUserImpl/subclass,
+		// or otherwise has a 'domainClass' property
+		if (principal.metaClass.respondsTo(principal, 'getDomainClass')) {
+			source = principal.domainClass
+		}
+		if (!source) {
+			source = principal
+		}
+
+		return source
+	}
+
+	def isLoggedIn = { attrs, body ->
+		if (isAuthenticated()) {
+			out << body()
+		}
+	}
+
+	def isNotLoggedIn = {attrs, body ->
+		if (!isAuthenticated()) {
+			out << body()
+		}
+	}
+
+	def loggedInUsername = { attrs ->
+		if (isAuthenticated()) {
+			out << SCH.context.authentication.principal.username
+		}
+	}
+
+	private boolean isAuthenticated() {
+		def authPrincipal = SCH?.context?.authentication?.principal
+		return authPrincipal != null && authPrincipal != 'anonymousUser'
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/plugin.xml
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/plugin.xml	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/plugin.xml	(revision 58)
@@ -0,0 +1,13 @@
+<plugin name='acegi' version='0.5.1'>
+  <author>Tsuyoshi Yamamoto</author>
+  <authorEmail>tyama@xmldo.jp</authorEmail>
+  <title>Grails Spring Security 2.0 Plugin</title>
+  <documentation>http://grails.org/AcegiSecurity+Plugin</documentation>
+  <resources>
+    <resource>DefaultSecurityConfig</resource>
+    <resource>org.grails.plugins.springsecurity.taglib.AuthorizeTagLib</resource>
+    <resource>org.grails.plugins.springsecurity.service.AuthenticateService</resource>
+    <resource>org.grails.plugins.springsecurity.controller.AuthBase</resource>
+  </resources>
+  <dependencies />
+</plugin>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/CreateAuthDomains.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/CreateAuthDomains.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/CreateAuthDomains.groovy	(revision 58)
@@ -0,0 +1,99 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Create/Copy Domains, auth.gsp, Controllers for security plugin.
+ *
+ * @author Tsuyoshi Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+
+includeTargets << new File("$acegiPluginDir/scripts/_SecurityTargets.groovy")
+
+target('default': 'Creates Domain classes for Spring Security plugin') {
+	parseArgs()
+	createDomains()
+	copyViewAndControllers()
+}
+
+private void parseArgs() {
+	args = args ? args.split('\n') : []
+	switch (args.size()) {
+		case 0:
+			println 'Creating domain classes with default names'
+			break
+		case 3:
+			splitPersonClassName args[0]
+			splitAuthorityClassName args[1]
+			splitRequestmapClassName args[2]
+			println "Login user domain class: ${args[0]}"
+			println "Authority domain class: ${args[1]}"
+			println "Requestmap domain class: ${args[2]}"
+			break
+		default:
+			usage()
+			break
+	}
+}
+
+private void usage() {
+	println 'usage: grails create-auth-domains <person class name> <authority class name> <request map class name>'
+	System.exit(1)
+}
+
+private void createDomains() {
+
+	// create Person domain class
+	generateFile "$templateDir/_Person.groovy",
+		"$appDir/domain/${packageToDir(personClassPackage)}${personClassName}.groovy"
+
+	// create Authority domain class
+	generateFile "$templateDir/_Authority.groovy",
+		"$appDir/domain/${packageToDir(authorityClassPackage)}${authorityClassName}.groovy"
+
+	// create Requestmap domain class
+	generateFile "$templateDir/_Requestmap.groovy",
+		"$appDir/domain/${packageToDir(requestmapClassPackage)}${requestmapClassName}.groovy"
+
+	// create SecurityConfig
+	generateFile "$templateDir/_SecurityConfig.groovy", "$appDir/conf/SecurityConfig.groovy"
+}
+
+private String packageToDir(pkg) {
+	String dir = ''
+	if (pkg) {
+		dir = pkg.replaceAll('\\.', '/') + '/'
+	}
+
+	return dir
+}
+
+private void copyViewAndControllers() {
+
+	// copy login.gsp and Login/Logout Controller example.
+	println 'copying login.gsp and Login/Logout Controller example. '
+	Ant.mkdir dir: "$appDir/views/login"
+	copyFile "$templateDir/views/login/auth.gsp", "$appDir/views/login/auth.gsp"
+	copyFile "$templateDir/views/login/openIdAuth.gsp", "$appDir/views/login/openIdAuth.gsp"
+	copyFile "$templateDir/views/login/denied.gsp", "$appDir/views/login/denied.gsp"
+	copyFile "$templateDir/controllers/LoginController.groovy", "$appDir/controllers/LoginController.groovy"
+	copyFile "$templateDir/controllers/LogoutController.groovy", "$appDir/controllers/LogoutController.groovy"
+
+	// log4j.logger.org.springframework.security='off,stdout'
+	def configFile = new File("$appDir/conf/Config.groovy")
+	if (configFile.exists()) {
+		configFile.append("\n\n//log4j.logger.org.springframework.security='off,stdout'")
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateManager.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateManager.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateManager.groovy	(revision 58)
@@ -0,0 +1,62 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generates view files and controller files for Spring Security user management.
+ * @author Tsuyoshi Yamamoto
+ * @author Haotian Sun
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+
+includeTargets << new File("$acegiPluginDir/scripts/_SecurityTargets.groovy")
+
+pluginTemplatePath = "$templateDir/manager"
+
+target('default': 'Generates view and controller for Spring Security user management') {
+	loadConfig()
+
+	generateControllerAndViews personClassName, 'User'
+	generateControllerAndViews authorityClassName, 'Role'
+	generateControllerAndViews requestmapClassName, 'Requestmap'
+}
+
+private void generateControllerAndViews(String name, String templateName) {
+
+	String path = "$basedir/grails-app/controllers/${name}Controller.groovy"
+	def outFile = new File(path)
+	if (outFile.exists()) {
+		Ant.input addProperty: 'overwrite', message: "$outFile.name exists - overwrite? y/n"
+		if ('y' == Ant.antProject.properties.'overwrite') {
+			overwrite = true
+		}
+	}
+	else {
+		overwrite = true
+	}
+
+	println "generating files for $name ......."
+
+	println "generating file $path"
+	generateFile "$pluginTemplatePath/controllers/_${templateName}Controller.groovy", path
+
+	path = "$basedir/grails-app/views/${org.apache.commons.lang.WordUtils.uncapitalize(name)}"
+	String viewPath = "$pluginTemplatePath/views/${templateName.toLowerCase()}"
+	println "generating view files - $path/* "
+	Ant.mkdir dir: path
+	generateFile "$viewPath/list.gsp", "$path/list.gsp"
+	generateFile "$viewPath/edit.gsp", "$path/edit.gsp"
+	generateFile "$viewPath/create.gsp", "$path/create.gsp"
+	generateFile "$viewPath/show.gsp", "$path/show.gsp"
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateRegistration.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateRegistration.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/GenerateRegistration.groovy	(revision 58)
@@ -0,0 +1,89 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generates user registration views and controllers.
+ *
+ * @author Haotian Sun
+ * @author Tsuyoshi Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+
+includeTargets << new File("$acegiPluginDir/scripts/_SecurityTargets.groovy")
+
+pluginTemplatePath = "$templateDir/manager"
+
+target('default': 'Generates user registration views and controllers') {
+
+	loadConfig()
+
+	if (!new File("$basedir/lib/mail-1.4.jar").exists()) {
+		println "Downloading mail-1.4 ..."
+		get(dest: "$basedir/lib/mail-1.4.jar",
+			src: "http://repo1.maven.org/maven2/javax/mail/mail/1.4/mail-1.4.jar",
+			verbose: true,
+			usetimestamp: true)
+	}
+
+	if (!new File("$basedir/lib/activation-1.1.jar").exists()) {
+		println "Downloading activation-1.1.jar ..."
+		get(dest: "$basedir/lib/activation-1.1.jar",
+			src: "http://repo1.maven.org/maven2/javax/activation/activation/1.1/activation-1.1.jar",
+			verbose: true,
+			usetimestamp: true)
+	}
+
+	generateRegistration 'register'
+}
+
+private void generateRegistration(String name) {
+
+	def uname = name[0].toUpperCase() + name.substring(1)
+	def outFile = new File("$basedir/grails-app/controllers/${uname}Controller.groovy")
+	if (outFile.exists()) {
+		Ant.input(addProperty: 'overwrite', message: 'Do you want to overwrite? y/n')
+		if ('y' == Ant.antProject.properties.'overwrite') {
+			overwrite = true
+		}
+	}
+	else {
+		overwrite = true
+	}
+
+	println "generating files for $uname ......."
+
+	//copy the CaptchaController
+	String dest = "$basedir/grails-app/controllers/CaptchaController.groovy"
+	println "copying CaptchaController.groovy to - $dest"
+	copyFile "$pluginTemplatePath/controllers/_CaptchaController.groovy", dest
+
+	//copy the EmailerService
+	dest = "$basedir/grails-app/services/EmailerService.groovy"
+	println "copying EmailerService.groovy to - $dest"
+	copyFile "$pluginTemplatePath/services/_EmailerService.groovy", dest
+
+	//generate RegisterController.groovy
+	dest = "$basedir/grails-app/controllers/${uname}Controller.groovy"
+	println "generating file $dest"
+	generateFile "$pluginTemplatePath/controllers/_${uname}Controller.groovy", dest
+
+	//generate views for RegisterController
+	dest = "$basedir/grails-app/views/$name"
+	println "copying view files to - $dest/*"
+	Ant.mkdir dir: dest
+	copyFile "$pluginTemplatePath/views/$name/edit.gsp", "$dest/edit.gsp"
+	copyFile "$pluginTemplatePath/views/$name/index.gsp", "$dest/index.gsp"
+	copyFile "$pluginTemplatePath/views/$name/show.gsp", "$dest/show.gsp"
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Install.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Install.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Install.groovy	(revision 58)
@@ -0,0 +1,1 @@
+// nothing to do
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_SecurityTargets.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_SecurityTargets.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_SecurityTargets.groovy	(revision 58)
@@ -0,0 +1,126 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shared methods/closures and initialization.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+
+import groovy.text.SimpleTemplateEngine
+
+grailsHome = Ant.project.properties.'environment.GRAILS_HOME'
+includeTargets << new File("$grailsHome/scripts/Init.groovy")
+
+personClassName = 'Person'
+personClassPackage = ''
+authorityClassName = 'Authority'
+authorityClassPackage = ''
+requestmapClassName = 'Requestmap'
+requestmapClassPackage = ''
+templateDir = "$acegiPluginDir/src/templates"
+appDir = "$basedir/grails-app"
+
+overwrite = true
+
+generateFile = { String templatePath, String outputPath ->
+
+	File templateFile = new File(templatePath)
+	if (!templateFile.exists()) {
+		println "$templatePath doesn't exist"
+		return
+	}
+
+	File outFile = new File(outputPath)
+	if (outFile.exists() && !overwrite) {
+	    println "file *not* generated: $outFile.absolutePath"
+		return
+	}
+
+	// in case it's in a package, create dirs
+	Ant.mkdir dir: outFile.parentFile
+
+	def binding = [personClassName: personClassName,
+	               personClassPackage: '',
+	               personClass: personClassName, personClassImport: '',
+	               authorityClassName: authorityClassName,
+	               authorityClassPackage: '',
+	               authorityClass: authorityClassName, authorityClassImport: '',
+	               requestmapClassName: requestmapClassName,
+	               requestmapClassPackage: '',
+	               requestmapClass: requestmapClassName, requestmapClassImport: '']
+
+	if (personClassPackage) {
+		binding.personClass = "${personClassPackage}.$personClassName"
+		binding.personClassImport = "import $binding.personClass"
+		binding.personClassPackage = "package $personClassPackage"
+	}
+	if (authorityClassPackage) {
+		binding.authorityClass = "${authorityClassPackage}.$authorityClassName"
+		binding.authorityClassImport = "import $binding.authorityClass"
+		binding.authorityClassPackage = "package $authorityClassPackage"
+	}
+	if (requestmapClassPackage) {
+		binding.requestmapClass = "${requestmapClassPackage}.$requestmapClassName"
+		binding.requestmapClassImport = "import $binding.requestmapClass"
+		binding.requestmapClassPackage = "package $requestmapClassPackage"
+	}
+
+	outFile.withWriter { writer ->
+		def template = new SimpleTemplateEngine().createTemplate(templateFile.text)
+		template.make(binding).writeTo(writer)
+	}
+
+	println "file generated at $outFile.absolutePath"
+}
+
+copyFile = { String from, String to ->
+	Ant.copy(file: from, tofile: to, overwrite: overwrite)
+}
+
+loadConfig = {
+	GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader())
+	Class clazz = loader.parseClass(new File("$basedir/grails-app/conf/SecurityConfig.groovy"))
+	def securityConfig = new ConfigSlurper().parse(clazz)
+	splitPersonClassName securityConfig.security.loginUserDomainClass
+	splitAuthorityClassName securityConfig.security.authorityDomainClass
+	splitRequestmapClassName securityConfig.security.requestMapClass
+	println "Login user domain class: $securityConfig.security.loginUserDomainClass"
+	println "Authority domain class: $securityConfig.security.authorityDomainClass"
+	println "Request Map domain class: $securityConfig.security.requestMapClass"
+}
+
+splitClassName = { name ->
+	int index = name.lastIndexOf('.')
+	return index == -1 ? [name, ''] : [name.substring(index + 1), name.substring(0, index)]
+}
+
+splitPersonClassName = { name ->
+	def packageAndClass = splitClassName(name)
+	personClassName = packageAndClass[0]
+	personClassPackage = packageAndClass[1]
+}
+
+splitAuthorityClassName = { name ->
+	def packageAndClass = splitClassName(name)
+	authorityClassName = packageAndClass[0]
+	authorityClassPackage = packageAndClass[1]
+}
+
+splitRequestmapClassName = { name ->
+	def packageAndClass = splitClassName(name)
+	requestmapClassName = packageAndClass[0]
+	requestmapClassPackage = packageAndClass[1]
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Upgrade.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Upgrade.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/scripts/_Upgrade.groovy	(revision 58)
@@ -0,0 +1,1 @@
+// not implemented
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthenticatedVetoableDecisionManager.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthenticatedVetoableDecisionManager.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthenticatedVetoableDecisionManager.groovy	(revision 58)
@@ -0,0 +1,113 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.springframework.security.AccessDeniedException
+import org.springframework.security.Authentication
+import org.springframework.security.ConfigAttribute
+import org.springframework.security.ConfigAttributeDefinition
+import org.springframework.security.vote.AbstractAccessDecisionManager
+import org.springframework.security.vote.AccessDecisionVoter
+import org.springframework.security.vote.AuthenticatedVoter
+
+/**
+ * Uses the affirmative-based logic for roles, i.e. any in the list will grant access, but allows
+ * an authenticated voter to 'veto' access. This allows specification of roles and
+ * <code>IS_AUTHENTICATED_FULLY</code> on one line in SecurityConfig.groovy.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class AuthenticatedVetoableDecisionManager extends AbstractAccessDecisionManager {
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.vote.AbstractAccessDecisionManager#decide(
+	 * 	org.springframework.security.Authentication, java.lang.Object,
+	 * 	org.springframework.security.ConfigAttributeDefinition)
+	 */
+	void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
+            throws AccessDeniedException {
+
+		boolean authenticatedVotersGranted = checkAuthenticatedVoters(authentication, object, config)
+		boolean otherVotersGranted = checkOtherVoters(authentication, object, config)
+
+		if (!authenticatedVotersGranted && !otherVotersGranted) {
+			checkAllowIfAllAbstainDecisions()
+		}
+	}
+
+	/**
+	 * Allow any {@link AuthenticatedVoter} to veto. If any voter denies,
+	 * throw an exception; if any grant, return <code>true</code>;
+	 * otherwise return <code>false</code> if all abstain.
+	 */
+	private boolean checkAuthenticatedVoters(authentication, object, config) {
+		boolean grant = false
+		for (AccessDecisionVoter voter in decisionVoters) {
+			if (voter instanceof AuthenticatedVoter) {
+				int result = voter.vote(authentication, object, config)
+				switch (result) {
+					case AccessDecisionVoter.ACCESS_GRANTED:
+						grant = true
+						break
+					case AccessDecisionVoter.ACCESS_DENIED:
+						deny()
+						break
+					default: // abstain
+						break
+				}
+			}
+		}
+		return grant
+	}
+
+	/**
+	 * Check the other (non-{@link AuthenticatedVoter}) voters. If any voter grants,
+	 * return true. If any voter denies, throw exception. Otherwise return <code>false</code>
+	 * to indicate that all abstained.
+	 */
+	private boolean checkOtherVoters(authentication, object, config) {
+		int denyCount = 0
+		for (AccessDecisionVoter voter in decisionVoters) {
+			if (voter instanceof AuthenticatedVoter) {
+				continue
+			}
+
+			int result = voter.vote(authentication, object, config)
+			switch (result) {
+            	case AccessDecisionVoter.ACCESS_GRANTED:
+            		return true
+            	case AccessDecisionVoter.ACCESS_DENIED:
+            		denyCount++
+            		break
+				default: // abstain
+            		break
+            }
+        }
+
+        if (denyCount) {
+            deny()
+        }
+
+        // all abstain
+        return false
+	}
+
+	private void deny() {
+		throw new AccessDeniedException(messages.getMessage(
+				"AbstractAccessDecisionManager.accessDenied",
+				"Access is denied"))
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthorizeTools.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthorizeTools.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/AuthorizeTools.groovy	(revision 58)
@@ -0,0 +1,141 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.springframework.security.GrantedAuthority
+import org.springframework.security.GrantedAuthorityImpl
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.util.StringUtils as STU
+
+import grails.util.GrailsUtil
+
+/**
+ * Helper methods.
+ * @author Tsuyoshi Yamamoto
+ */
+class AuthorizeTools {
+
+	/**
+	 * Extract the role names from authorities.
+	 * @param  authorities  the authorities
+	 * @return  the names
+	 */
+	static Set<String> authoritiesToRoles(authorities) {
+		def roles = [] as Set
+		authorities.each { authority ->
+			if (null == authority.authority) {
+				throw new IllegalArgumentException(
+						"Cannot process GrantedAuthority objects which return null from getAuthority() - attempting to process "
+						+ authority)
+			}
+			roles.add(authority.authority)
+		}
+
+		return roles
+	}
+
+	/**
+	 * Get the current user's authorities.
+	 * @return  a list of authorities (empty if not authenticated).
+	 */
+	static List getPrincipalAuthorities() {
+		def currentUser = SCH.context.authentication
+		if (null == currentUser) {
+			return Collections.emptyList()
+		}
+
+		if (!currentUser.authorities) {
+			return Collections.emptyList()
+		}
+
+		return Arrays.asList(currentUser.authorities)
+	}
+
+	/**
+	 * Split the role names and create {@link GrantedAuthority}s for each.
+	 * @param authorizationsString  comma-delimited role names
+	 * @return authorities (possibly empty)
+	 */
+	static Set<GrantedAuthority> parseAuthoritiesString(String authorizationsString) {
+		def requiredAuthorities = [] as Set
+		STU.commaDelimitedListToStringArray(authorizationsString).each { auth ->
+			requiredAuthorities << new GrantedAuthorityImpl(auth)
+		}
+
+		return requiredAuthorities
+	}
+
+	static Set<String> retainAll(granted, required) {
+		def grantedRoles = authoritiesToRoles(granted)
+		def requiredRoles = authoritiesToRoles(required)
+		grantedRoles.retainAll(requiredRoles)
+
+		return rolesToAuthorities(grantedRoles, granted)
+	}
+
+	static Set<String> rolesToAuthorities(grantedRoles, granted) {
+		def target = new HashSet()
+		grantedRoles.each { role ->
+			def auth = granted.find { authority -> authority.authority == role }
+			if (auth != null) {
+				target.add(auth.authority)
+			}
+		}
+
+		return target
+	}
+
+	static boolean ifAllGranted(role) {
+		def granted = getPrincipalAuthorities()
+		return granted.containsAll(parseAuthoritiesString(role))
+	}
+
+	static boolean ifNotGranted(role) {
+		def granted = getPrincipalAuthorities()
+		Set grantedCopy = retainAll(granted, parseAuthoritiesString(role))
+		return grantedCopy.empty
+	}
+
+	static boolean ifAnyGranted(role) {
+		def granted = getPrincipalAuthorities()
+		Set grantedCopy = retainAll(granted, parseAuthoritiesString(role))
+		return !grantedCopy.empty
+	}
+
+	static ConfigObject getSecurityConfig() {
+
+		GroovyClassLoader classLoader = new GroovyClassLoader(AuthorizeTools.getClassLoader())
+
+		def slurper = new ConfigSlurper(GrailsUtil.environment)
+		ConfigObject userConfig
+		try {
+			userConfig = slurper.parse(classLoader.loadClass('SecurityConfig'))
+		}
+		catch (e) {
+			// ignored, use defaults
+		}
+
+		ConfigObject config
+		ConfigObject defaultConfig = slurper.parse(classLoader.loadClass('DefaultSecurityConfig'))
+		if (userConfig) {
+			config = defaultConfig.merge(userConfig)
+		}
+		else {
+			config = defaultConfig
+		}
+
+		return config
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAuthenticationProcessingFilter.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAuthenticationProcessingFilter.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAuthenticationProcessingFilter.groovy	(revision 58)
@@ -0,0 +1,90 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import javax.servlet.FilterChain
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+import org.springframework.security.Authentication
+import org.springframework.security.AuthenticationException
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
+
+/**
+ * Extends the default {@link AuthenticationProcessingFilter} to override the <code>sendRedirect()</code>
+ * logic and always send absolute redirects.
+ *
+ * @author Tsuyoshi Yamamoto
+ */
+class GrailsAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
+
+	/**
+	 * Dependency injection for the authentication service.
+	 */
+	def authenticateService
+
+	/**
+	 * Dependency injection for the Ajax auth fail url.
+	 */
+	String ajaxAuthenticationFailureUrl
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#doFilterHttp(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+	 * 	javax.servlet.FilterChain)
+	 */
+	@Override
+	void doFilterHttp(HttpServletRequest request, HttpServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+
+		SecurityRequestHolder.set request, response
+		try {
+			super.doFilterHttp(request, response, chain)
+		}
+		finally {
+			SecurityRequestHolder.reset()
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#sendRedirect(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+	 * 	java.lang.String)
+	 */
+	@Override
+	protected void sendRedirect(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			String url) throws IOException {
+		RedirectUtils.sendRedirect(request, response, url);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#determineFailureUrl(
+	 * 	javax.servlet.http.HttpServletRequest, org.springframework.security.AuthenticationException)
+	 */
+	@Override
+	protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
+		String url = super.determineFailureUrl(request, failed)
+		if (url == authenticationFailureUrl && authenticateService.isAjax(request)) {
+			url = ajaxAuthenticationFailureUrl ?: authenticationFailureUrl
+		}
+		return url
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoAuthenticationProvider.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoAuthenticationProvider.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoAuthenticationProvider.groovy	(revision 58)
@@ -0,0 +1,38 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.springframework.security.AuthenticationException
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken
+import org.springframework.security.providers.dao.DaoAuthenticationProvider
+import org.springframework.security.ui.ntlm.NtlmUsernamePasswordAuthenticationToken
+import org.springframework.security.userdetails.UserDetails
+
+/**
+ * @author Martin Vlcek
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsDaoAuthenticationProvider extends DaoAuthenticationProvider {
+
+	@Override
+	protected void additionalAuthenticationChecks(UserDetails userDetails,
+            UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+
+        // don't check password in case of NTLM authentication
+        if (!(authentication instanceof NtlmUsernamePasswordAuthenticationToken)) {
+            super.additionalAuthenticationChecks(userDetails, authentication)
+        }
+    }
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoImpl.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoImpl.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsDaoImpl.groovy	(revision 58)
@@ -0,0 +1,160 @@
+/**
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.apache.log4j.Logger
+
+import org.springframework.security.GrantedAuthority
+import org.springframework.security.GrantedAuthorityImpl
+import org.springframework.security.userdetails.UserDetails
+import org.springframework.security.userdetails.UserDetailsService
+import org.springframework.security.userdetails.UsernameNotFoundException
+import org.springframework.dao.DataAccessException
+
+/**
+ * {@link UserDetailsService} with {@link GrailsDomainClass} Data Access Object.
+ * @author Tsuyoshi Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsDaoImpl extends GrailsWebApplicationObjectSupport implements UserDetailsService {
+
+	private final Logger logger = Logger.getLogger(getClass())
+
+	def authenticateService
+
+	// dependency-injected fields
+	String loginUserDomainClass
+	String usernameFieldName
+	String passwordFieldName
+	String enabledFieldName
+	String relationalAuthoritiesField
+	String authorityFieldName
+	String authoritiesMethodName
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
+	 */
+	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+		return loadUserByUsername(username, true)
+	}
+
+	/**
+	 * Load a user by username, optionally not loading roles.
+	 * @param username  the login name
+	 * @param loadRoles  if <code>true</code> load roles from the database
+	 * @return the user if found, otherwise throws {@link UsernameNotFoundException}
+	 * @throws UsernameNotFoundException  if the user isn't found
+	 * @throws DataAccessException  if there's a database problem
+	 */
+	UserDetails loadUserByUsername(String username, boolean loadRoles)
+			throws UsernameNotFoundException, DataAccessException {
+
+		if (authenticateService.securityConfig.security.useNtlm) {
+			username = username.toLowerCase()
+		}
+
+		GrailsWebApplicationObjectSupport.SessionContainer container = setUpSession()
+		try {
+			def user = loadDomainUser(username, container.session)
+			GrantedAuthority[] authorities = loadAuthorities(user, username, loadRoles)
+			return createUserDetails(
+					username, user."$passwordFieldName", user."$enabledFieldName",
+					authorities, user)
+		}
+		finally {
+			releaseSession(container)
+		}
+    }
+
+	protected GrantedAuthority[] loadAuthorities(user, String username, boolean loadRoles) {
+		if (!loadRoles) {
+			return []
+		}
+
+		if (relationalAuthoritiesField) {
+			return createRolesByRelationalAuthorities(user, username)
+		}
+
+		if (authoritiesMethodName) {
+			return createRolesByAuthoritiesMethod(user, username)
+		}
+
+		logger.error("User [${username}] has no GrantedAuthority")
+		throw new UsernameNotFoundException("User has no GrantedAuthority")
+	}
+
+	protected def loadDomainUser(username, session) throws UsernameNotFoundException, DataAccessException {
+
+		List users = session.createQuery(
+				"from $loginUserDomainClass where $usernameFieldName=:username")
+				.setString('username', username)
+				.setCacheable(true)
+				.list()
+
+		if (users.empty) {
+			logger.error "User not found: $username"
+			throw new UsernameNotFoundException("User not found", username)
+		}
+
+		return users[0]
+    }
+
+	/**
+	 * Create the {@link UserDetails} instance. Subclasses can override to inherit core functionality
+	 * but determine the concrete class without reimplementing the entire class.
+	 * @param username the username
+	 * @param password the password
+	 * @param enabled set to <code>true</code> if the user is enabled
+	 * @param authorities the authorities that should be granted to the caller
+	 * @param user  the user domain instance
+	 * @return  the instance
+	 */
+	protected UserDetails createUserDetails(
+			String username, String password, boolean enabled,
+			GrantedAuthority[] authorities, Object user) {
+		new GrailsUserImpl(
+				username, password, enabled,
+				true, true, true, authorities, user)
+	}
+
+	protected GrantedAuthority[] createRolesByAuthoritiesMethod(user, String username) {
+		Set authorities = user."$authoritiesMethodName"()
+		assertNotEmpty authorities, username
+
+		authorities.collect { roleName -> new GrantedAuthorityImpl(roleName) } as GrantedAuthority[]
+	}
+
+	protected GrantedAuthority[] createRolesByRelationalAuthorities(user, String username) {
+		// get authorities from LoginUser [LoginUser]--M:M--[Authority]
+
+		Set authorities = user."$relationalAuthoritiesField"
+		assertNotEmpty authorities, username
+
+		authorities.collect { item -> new GrantedAuthorityImpl(item."$authorityFieldName") } as GrantedAuthority[]
+	}
+
+	protected void assertNotEmpty(Collection authorities, String username) {
+		if (authorities == null || authorities.empty) {
+			logger.error("User [${username}] has no GrantedAuthority")
+			throw new UsernameNotFoundException("User has no GrantedAuthority")
+		}
+	}
+
+	protected Logger getLog() {
+		return logger
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsNtlmProcessingFilterEntryPoint.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsNtlmProcessingFilterEntryPoint.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/GrailsNtlmProcessingFilterEntryPoint.groovy	(revision 58)
@@ -0,0 +1,66 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.springframework.security.AuthenticationException
+import org.springframework.security.BadCredentialsException
+import org.springframework.security.ui.ntlm.NtlmBaseException
+import org.springframework.security.ui.ntlm.NtlmBeginHandshakeException
+import org.springframework.security.ui.ntlm.NtlmProcessingFilter
+import org.springframework.security.ui.ntlm.NtlmProcessingFilterEntryPoint
+
+import javax.servlet.ServletException
+import javax.servlet.ServletRequest
+import javax.servlet.ServletResponse
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+/**
+ * @author Martin Vlcek
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsNtlmProcessingFilterEntryPoint extends NtlmProcessingFilterEntryPoint {
+
+	private final String STATE_ATTR = NtlmProcessingFilter.@STATE_ATTR
+	private final Integer BEGIN = NtlmProcessingFilter.@BEGIN
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.ntlm.NtlmProcessingFilterEntryPoint#commence(
+	 * 	javax.servlet.ServletRequest, javax.servlet.ServletResponse,
+	 * 	org.springframework.security.AuthenticationException)
+	 */
+	@Override
+	void commence(ServletRequest req, ServletResponse res, AuthenticationException authException) throws IOException, ServletException {
+
+		// start authentication, if necessary and forceIdentification in NtlmProcessingFilter is false
+		if (!(authException instanceof NtlmBaseException
+				|| authException instanceof BadCredentialsException)) {
+
+			req.session.setAttribute STATE_ATTR, BEGIN
+
+			HttpServletResponse response = (HttpServletResponse)res
+
+			response.setHeader 'WWW-Authenticate', new NtlmBeginHandshakeException().message
+			response.setHeader 'Connection', 'Keep-Alive'
+			response.status = HttpServletResponse.SC_UNAUTHORIZED
+			response.contentLength = 0
+			response.flushBuffer()
+		}
+		else {
+			super.commence(req, res, authException)
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/LogoutFilterFactoryBean.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/LogoutFilterFactoryBean.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/LogoutFilterFactoryBean.groovy	(revision 58)
@@ -0,0 +1,109 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+import org.springframework.beans.factory.FactoryBean
+import org.springframework.beans.factory.InitializingBean
+import org.springframework.beans.factory.annotation.Required
+import org.springframework.security.ui.logout.LogoutFilter
+import org.springframework.security.ui.logout.LogoutHandler
+
+/**
+ * Configures a {@link LogoutFilter} given a list of {@link LogoutHandler}s.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class LogoutFilterFactoryBean implements FactoryBean, InitializingBean {
+
+	private List<LogoutHandler> _handlers
+	private LogoutFilter _logoutFilter
+	private String _logoutSuccessUrl
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.FactoryBean#getObject()
+	 */
+	LogoutFilter getObject() {
+		return _logoutFilter
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+	 */
+	Class<LogoutFilter> getObjectType() {
+		return LogoutFilter
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.FactoryBean#isSingleton()
+	 */
+	boolean isSingleton() {
+		return true
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	void afterPropertiesSet() {
+		_logoutFilter = new FixRedirectLogoutFilter(_logoutSuccessUrl, _handlers as LogoutHandler[])
+	}
+
+	/**
+	 * Dependency injection for the logout success url.
+	 * @param logoutSuccessUrl  the url
+	 */
+	@Required
+	void setLogoutSuccessUrl(String logoutSuccessUrl) {
+		_logoutSuccessUrl = logoutSuccessUrl
+	}
+
+	/**
+	 * Dependency injection for the handlers.
+	 * @param handlers  the handlers
+	 */
+	@Required
+	void setHandlers(List<LogoutHandler> handlers) {
+		_handlers = handlers
+	}
+}
+
+/**
+ * Overrides the default redirect behavior to use {@link RedirectUtils#sendRedirect(HttpServletRequest, HttpServletResponse, String)}.
+ */
+class FixRedirectLogoutFilter extends LogoutFilter {
+
+	FixRedirectLogoutFilter(String logoutSuccessUrl, LogoutHandler[] handlers) {
+		super(logoutSuccessUrl, handlers)
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.logout.LogoutFilter#sendRedirect(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
+	 */
+	@Override
+	protected void sendRedirect(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			String url) throws IOException {
+		RedirectUtils.sendRedirect(request, response, url)
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RedirectUtils.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RedirectUtils.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RedirectUtils.groovy	(revision 58)
@@ -0,0 +1,78 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+import org.springframework.security.util.PortResolver
+import org.springframework.security.util.PortResolverImpl
+
+/**
+ * Fixes <a href='http://jira.codehaus.org/browse/GRAILSPLUGINS-273'>this redirect bug</a>.
+ * @author Tsuyoshi Yamamoto
+ */
+class RedirectUtils {
+
+	private static final PortResolver RESOLVER = new PortResolverImpl()
+
+	/**
+	 * Send a redirect.
+	 * @param request  the request
+	 * @param response  the response
+	 * @param url the target url to redirect to
+	 * @throws IOException  if there's a problem
+	 */
+	static void sendRedirect(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			String url) throws IOException {
+
+		String redirect = buildRedirectUrl(request, response, url)
+		response.sendRedirect(response.encodeRedirectURL(redirect))
+	}
+
+	/**
+	 * Build a redirect url.
+	 * @param request  the request
+	 * @param response  the response
+	 * @param url the target url to redirect to
+	 * @return  the url
+	 * @throws IOException  if there's a problem
+	 */
+	static String buildRedirectUrl(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			String url) throws IOException {
+
+		if (!url.startsWith("http://") && !url.startsWith("https://")) {
+			String scheme = request.scheme
+			int serverPort = RESOLVER.getServerPort(request)
+			boolean inHttp = "http".equalsIgnoreCase(scheme)
+			boolean inHttps = "https".equalsIgnoreCase(scheme)
+			boolean includePort = true
+			if (inHttp && (serverPort == 80)) {
+				includePort = false
+			}
+			else if (inHttps && (serverPort == 443)) {
+				includePort = false
+			}
+			String port = includePort ? ":" + serverPort : ""
+			return "${scheme}://${request.serverName}${port}${request.contextPath}${url}"
+		}
+
+		return url
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinitionHelper.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinitionHelper.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinitionHelper.groovy	(revision 58)
@@ -0,0 +1,59 @@
+/* Copyright 2006-2009 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
+
+/**
+ * Helper class for RequestmapFilterInvocationDefinition to perform Requestmap dynamic query.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class RequestmapFilterInvocationDefinitionHelper {
+
+	private String _requestMapClass // 'Requestmap'
+	private String _requestMapPathFieldName // 'url'
+	private String _requestMapConfigAttributeField // 'configAttribute'
+
+	/**
+	 * Constructor.
+	 * @param requestMapClass  Requestmap class name
+	 * @param requestMapPathFieldName  Requestmap path pattern field name
+	 * @param requestMapConfigAttributeField  Requestmap config attribute (roles) field name
+	 */
+	RequestmapFilterInvocationDefinitionHelper(String requestMapClass,
+			String requestMapPathFieldName, String requestMapConfigAttributeField) {
+		_requestMapClass = requestMapClass
+		_requestMapPathFieldName = requestMapPathFieldName
+		_requestMapConfigAttributeField = requestMapConfigAttributeField
+	}
+
+	/**
+	 * Load all Requestmaps and build a map with the relevant data.
+	 * @return keys are url patterns, values are roles
+	 */
+	Map<String, String> loadRequestmaps() {
+		Class requestmapClass = AH.application.getClassForName(_requestMapClass)
+		Map<String, String> data = [:]
+		def requestmaps = requestmapClass.list()
+		for (requestmap in requestmaps) {
+			String urlPattern = requestmap."${_requestMapPathFieldName}"
+			String configAttribute = requestmap."${_requestMapConfigAttributeField}"
+			data[urlPattern] = configAttribute
+		}
+
+		return data
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/SecurityEventListener.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/SecurityEventListener.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/SecurityEventListener.groovy	(revision 58)
@@ -0,0 +1,120 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity
+
+import org.grails.plugins.springsecurity.service.AuthenticateService
+
+import org.springframework.context.ApplicationContext
+import org.springframework.context.ApplicationContextAware
+import org.springframework.context.ApplicationEvent
+import org.springframework.context.ApplicationListener
+import org.springframework.security.event.authentication.AbstractAuthenticationEvent
+import org.springframework.security.event.authentication.AbstractAuthenticationFailureEvent
+import org.springframework.security.event.authentication.AuthenticationSuccessEvent
+import org.springframework.security.event.authentication.AuthenticationSwitchUserEvent
+import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent
+import org.springframework.security.event.authorization.AbstractAuthorizationEvent
+
+/**
+ * Registers as an event listener and delegates handling of security-related events
+ * to optional closures defined in SecurityConfig.groovy.
+ * <p/>
+ * The following callbacks are supported:<br/>
+ * <ul>
+ * <li>onInteractiveAuthenticationSuccessEvent</li>
+ * <li>onAbstractAuthenticationFailureEvent</li>
+ * <li>onAuthenticationSuccessEvent</li>
+ * <li>onAuthenticationSwitchUserEvent</li>
+ * <li>onAuthorizationEvent</li>
+ * </ul>
+ * All callbacks are optional; you can implement just the ones you're interested in, e.g.
+ * <pre>
+ * security {
+ *    active = true
+ *
+ *    onAuthenticationSuccessEvent = { e, appCtx ->
+ *       ...
+ *    }
+ * }
+ * </pre>
+ * The event and the Spring context are provided in case you need to look up a Spring bean,
+ * e.g. the Hibernate SessionFactory.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class SecurityEventListener implements ApplicationListener, ApplicationContextAware {
+
+	private ApplicationContext _applicationContext;
+
+	/**
+	 * Dependency injection for AuthenticateService.
+	 */
+	AuthenticateService authenticateService
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.context.ApplicationListener#onApplicationEvent(
+	 * 	org.springframework.context.ApplicationEvent)
+	 */
+	void onApplicationEvent(ApplicationEvent e) {
+		if (e instanceof AbstractAuthenticationEvent) {
+			if (e instanceof InteractiveAuthenticationSuccessEvent) {
+				if (config.onInteractiveAuthenticationSuccessEvent) {
+					config.onInteractiveAuthenticationSuccessEvent.call(
+							(InteractiveAuthenticationSuccessEvent)e, _applicationContext)
+				}
+			}
+			else if (e instanceof AbstractAuthenticationFailureEvent) {
+				if (config.onAbstractAuthenticationFailureEvent) {
+					config.onAbstractAuthenticationFailureEvent.call(
+							(AbstractAuthenticationFailureEvent)e, _applicationContext)
+				}
+			}
+			else if (e instanceof AuthenticationSuccessEvent) {
+				if (config.onAuthenticationSuccessEvent) {
+					config.onAuthenticationSuccessEvent.call(
+							(AuthenticationSuccessEvent)e, _applicationContext)
+				}
+			}
+			else if (e instanceof AuthenticationSwitchUserEvent) {
+				if (config.onAuthenticationSwitchUserEvent) {
+//					GrailsUser userInfo = (GrailsUser)event.getAuthentication().getPrincipal()
+//					UserDetails userDetails = event.getTargetUser()
+					config.onAuthenticationSwitchUserEvent.call(
+							(AuthenticationSwitchUserEvent)e, _applicationContext)
+				}
+			}
+		}
+		else if (e instanceof AbstractAuthorizationEvent) {
+			if (config.onAuthorizationEvent) {
+				config.onAuthorizationEvent.call(
+						(AbstractAuthorizationEvent)e, _applicationContext)
+			}
+		}
+	}
+
+ 	/**
+ 	 * {@inheritDoc}
+ 	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(
+ 	 * 	org.springframework.context.ApplicationContext)
+ 	 */
+ 	void setApplicationContext(ApplicationContext applicationContext) {
+ 		_applicationContext = applicationContext;
+ 	}
+
+	private def getConfig() {
+		return authenticateService.securityConfig.security
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/kerberos/GrailsKerberosAuthenticationProvider.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/kerberos/GrailsKerberosAuthenticationProvider.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/kerberos/GrailsKerberosAuthenticationProvider.groovy	(revision 58)
@@ -0,0 +1,70 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.kerberos
+
+import org.springframework.security.Authentication
+import org.springframework.security.AuthenticationException
+import org.springframework.security.GrantedAuthority
+import org.springframework.security.providers.jaas.JaasAuthenticationProvider
+import org.springframework.security.providers.jaas.JaasAuthenticationToken
+
+/**
+ * Kerberos {@link AuthenticationProvider}.
+ *
+ * @author <a href='mailto:mmornati@byte-code.com'>Marco Mornati</a>
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsKerberosAuthenticationProvider extends JaasAuthenticationProvider {
+
+	def authenticateService
+	def userDetailsService
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.jaas.JaasAuthenticationProvider#authenticate(
+	 * 	org.springframework.security.Authentication)
+	 */
+	@Override
+	Authentication authenticate(Authentication auth) throws AuthenticationException {
+
+		Authentication authToken = super.authenticate(auth)
+
+		if (authToken instanceof JaasAuthenticationToken) {
+			String username = authToken.principal
+			boolean retrieveDatabaseRoles = authenticateService.securityConfig.security.kerberosRetrieveDatabaseRoles
+			def dbDetails = userDetailsService.loadUserByUsername(username, retrieveDatabaseRoles)
+			def authorities = mergeDatabaseRoles(dbDetails, authToken.authorities)
+			dbDetails.authorities = authorities
+			authToken = new JaasAuthenticationToken(
+					dbDetails, authToken.credentials,
+					dbDetails.authorities, authToken.loginContext);
+		}
+
+		return authToken
+	}
+
+	private GrantedAuthority[] mergeDatabaseRoles(details, GrantedAuthority[] authorities) {
+		List merged = []
+		if (authorities) {
+			merged.addAll(authorities as List)
+		}
+
+		if (details.authorities) {
+			merged.addAll(details.authorities as List)
+		}
+
+		return merged as GrantedAuthority[]
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUserDetailsMapper.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUserDetailsMapper.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUserDetailsMapper.groovy	(revision 58)
@@ -0,0 +1,84 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.ldap
+
+import org.codehaus.groovy.grails.plugins.springsecurity.ldap.GrailsLdapUser
+
+import org.springframework.ldap.core.DirContextOperations
+import org.springframework.security.GrantedAuthority
+import org.springframework.security.userdetails.UserDetails
+import org.springframework.security.userdetails.ldap.LdapUserDetails
+import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper
+
+/**
+ * Extends the default to return a {@link GrailsLdapUser} implementing
+ * both {@link GrailsUser} and {@link LdapUserDetails}.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsLdapUserDetailsMapper extends LdapUserDetailsMapper {
+
+	/**
+	 * Dependency injection for the user details service.
+	 */
+	def userDetailsService
+
+	/**
+	 * Dependency injection for whether to use passwords retrieved from LDAP.
+	 */
+	boolean usePassword
+
+	/**
+	 * Dependency injection for whether to retrieve roles from the database in addition to LDAP
+	 */
+	boolean retrieveDatabaseRoles
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.userdetails.ldap.LdapUserDetailsMapper#mapUserFromContext(
+	 * 	org.springframework.ldap.core.DirContextOperations, java.lang.String,
+	 * 	org.springframework.security.GrantedAuthority[])
+	 */
+	@Override
+	UserDetails mapUserFromContext(DirContextOperations ctx, String username, GrantedAuthority[] authorities) {
+
+		def dbDetails = userDetailsService.loadUserByUsername(username, retrieveDatabaseRoles)
+		authorities = mergeDatabaseRoles(dbDetails, authorities)
+
+		LdapUserDetails ldapDetails = (LdapUserDetails)super.mapUserFromContext(ctx, username, authorities)
+		if (usePassword) {
+			return new GrailsLdapUser(ldapDetails, dbDetails.domainClass)
+		}
+
+		// use a dummy password to avoid an exception from the User base class
+		return new GrailsLdapUser(details.username, 'not_used', details.enabled,
+				details.accountNonExpired, details.credentialsNonExpired,
+				details.accountNonLocked, details.authorities,
+				details.attributes, details.dn, dbDetails.domainClass)
+	}
+
+	private GrantedAuthority[] mergeDatabaseRoles(details, GrantedAuthority[] authorities) {
+		List merged = []
+		if (authorities) {
+			merged.addAll(authorities as List)
+		}
+
+		if (details.authorities) {
+			merged.addAll(details.authorities as List)
+		}
+
+		return merged as GrantedAuthority[]
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationProvider.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationProvider.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationProvider.groovy	(revision 58)
@@ -0,0 +1,93 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.openid
+
+import org.springframework.security.Authentication
+import org.springframework.security.AuthenticationException
+import org.springframework.security.AuthenticationServiceException
+import org.springframework.security.BadCredentialsException
+import org.springframework.security.providers.openid.AuthenticationCancelledException
+import org.springframework.security.providers.openid.OpenIDAuthenticationStatus
+import org.springframework.security.providers.openid.OpenIDAuthenticationProvider
+import org.springframework.security.providers.openid.OpenIDAuthenticationToken
+import org.springframework.security.userdetails.UserDetails
+import org.springframework.security.userdetails.UserDetailsService
+
+/**
+ * Subclass that returns a {@link GrailsOpenIdAuthenticationToken}.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsOpenIdAuthenticationProvider extends OpenIDAuthenticationProvider {
+
+	private _userDetailsService
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.openid.OpenIDAuthenticationProvider#authenticate(
+	 * 	org.springframework.security.Authentication)
+	 */
+	@Override
+	Authentication authenticate(Authentication authentication) throws AuthenticationException {
+
+		if (!supports(authentication.getClass())) {
+			return null
+		}
+
+		if (authentication instanceof OpenIDAuthenticationToken) {
+			OpenIDAuthenticationToken response = (OpenIDAuthenticationToken) authentication
+			OpenIDAuthenticationStatus status = response.status
+
+			// handle the various possibilites
+			if (status == OpenIDAuthenticationStatus.SUCCESS) {
+				// Lookup user details
+				UserDetails userDetails = _userDetailsService.loadUserByUsername(response.identityUrl)
+				return new GrailsOpenIdAuthenticationToken(userDetails, response.status, response.identityUrl)
+			}
+
+			if (status == OpenIDAuthenticationStatus.CANCELLED) {
+				throw new AuthenticationCancelledException("Log in cancelled")
+			}
+
+			if (status == OpenIDAuthenticationStatus.ERROR) {
+				throw new AuthenticationServiceException("Error message from server: $response.message")
+			}
+
+			if (status == OpenIDAuthenticationStatus.FAILURE) {
+				throw new BadCredentialsException("Log in failed - identity could not be verified")
+			}
+
+			if (status == OpenIDAuthenticationStatus.SETUP_NEEDED) {
+				throw new AuthenticationServiceException(
+						"The server responded setup was needed, which shouldn't happen")
+			}
+
+			throw new AuthenticationServiceException("Unrecognized return value $status")
+		}
+
+		return null
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.openid.OpenIDAuthenticationProvider#setUserDetailsService(
+	 * 	org.springframework.security.userdetails.UserDetailsService)
+	 */
+	@Override
+	void setUserDetailsService(UserDetailsService userDetailsService) {
+		_userDetailsService = userDetailsService
+		super.setUserDetailsService(userDetailsService)
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationToken.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationToken.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid/GrailsOpenIdAuthenticationToken.groovy	(revision 58)
@@ -0,0 +1,53 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.openid
+
+import org.springframework.security.GrantedAuthority
+import org.springframework.security.providers.openid.OpenIDAuthenticationStatus
+import org.springframework.security.providers.openid.OpenIDAuthenticationToken
+import org.springframework.security.userdetails.UserDetails
+
+/**
+ * Subclass that holds the user domain instance.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+class GrailsOpenIdAuthenticationToken extends OpenIDAuthenticationToken {
+
+	private UserDetails _userDetails
+
+ 	/**
+ 	 * Full constructor.
+ 	 * @param userDetails  the details
+ 	 * @param status  the status
+ 	 * @param identityUrl  the url
+ 	 */
+	GrailsOpenIdAuthenticationToken(
+			UserDetails userDetails,
+			OpenIDAuthenticationStatus status,
+			String identityUrl) {
+		super(userDetails.authorities, status, identityUrl)
+		_userDetails = userDetails
+	}
+
+ 	/**
+ 	 * {@inheritDoc}
+ 	 * @see org.springframework.security.providers.openid.OpenIDAuthenticationToken#getPrincipal()
+ 	 */
+	@Override
+	Object getPrincipal() {
+		return _userDetails
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AbstractFilterInvocationDefinition.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AbstractFilterInvocationDefinition.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AbstractFilterInvocationDefinition.java	(revision 58)
@@ -0,0 +1,184 @@
+/* Copyright 2006-2009 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.intercept.web.FilterInvocation;
+import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
+import org.springframework.security.util.AntUrlPathMatcher;
+import org.springframework.security.util.UrlMatcher;
+import org.springframework.util.Assert;
+
+/**
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public abstract class AbstractFilterInvocationDefinition
+      implements FilterInvocationDefinitionSource, InitializingBean {
+
+	private UrlMatcher _urlMatcher;
+	private boolean _rejectIfNoRule;
+	private boolean _stripQueryStringFromUrls = true;
+
+	protected static final ConfigAttributeDefinition DENY =
+		new ConfigAttributeDefinition(Collections.emptyList());
+
+	protected final Map<Object, ConfigAttributeDefinition> _compiled =
+		new HashMap<Object, ConfigAttributeDefinition>();
+
+	protected final Logger _log = Logger.getLogger(getClass());
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.intercept.ObjectDefinitionSource#getAttributes(java.lang.Object)
+	 */
+	public ConfigAttributeDefinition getAttributes(Object object) {
+		if (object == null || !supports(object.getClass())) {
+			throw new IllegalArgumentException("Object must be a FilterInvocation");
+		}
+
+		FilterInvocation filterInvocation = (FilterInvocation)object;
+
+		String url = determineUrl(filterInvocation);
+
+		ConfigAttributeDefinition configAttribute = findConfigAttribute(url);
+		if (configAttribute == null && _rejectIfNoRule) {
+			return DENY;
+		}
+
+		return configAttribute;
+	}
+
+	protected abstract String determineUrl(FilterInvocation filterInvocation);
+
+	private ConfigAttributeDefinition findConfigAttribute(final String url) {
+
+		initialize();
+
+		ConfigAttributeDefinition configAttribute = null;
+		Object configAttributePattern = null;
+
+		for (Map.Entry<Object, ConfigAttributeDefinition> entry : _compiled.entrySet()) {
+			Object pattern = entry.getKey();
+			if (_urlMatcher.pathMatchesUrl(pattern, url)) {
+				// TODO  this assumes Ant matching, not valid for regex
+				if (configAttribute == null || _urlMatcher.pathMatchesUrl(configAttributePattern, (String)pattern)) {
+					configAttribute = entry.getValue();
+					configAttributePattern = pattern;
+					if (_log.isTraceEnabled()) {
+						_log.trace("new candidate for '" + url + "': '" + pattern
+								+ "':" + configAttribute.getConfigAttributes());
+					}
+				}
+			}
+		}
+
+		if (_log.isTraceEnabled()) {
+			if (configAttribute == null) {
+				_log.trace("no config for '" + url + "'");
+			}
+			else {
+				_log.trace("config for '" + url + "' is '" + configAttributePattern
+						+ "':" + configAttribute.getConfigAttributes());
+			}
+		}
+
+		return configAttribute;
+	}
+
+	protected void initialize() {
+		// override if necessary
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public boolean supports(final Class clazz) {
+		return FilterInvocation.class.isAssignableFrom(clazz);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection getConfigAttributeDefinitions() {
+		return null;
+	}
+
+	/**
+	 * Dependency injection for the url matcher.
+	 * @param urlMatcher  the matcher
+	 */
+	public void setUrlMatcher(final UrlMatcher urlMatcher) {
+		_urlMatcher = urlMatcher;
+		_stripQueryStringFromUrls = _urlMatcher instanceof AntUrlPathMatcher;
+	}
+
+	/**
+	 * Dependency injection for whether to reject if there's no matching rule.
+	 * @param reject  if true, reject access unless there's a pattern for the specified resource
+	 */
+	public void setRejectIfNoRule(final boolean reject) {
+		_rejectIfNoRule = reject;
+	}
+
+	protected String lowercaseAndStringQuerystring(final String url) {
+
+		String fixed = url;
+
+		if (getUrlMatcher().requiresLowerCaseUrl()) {
+			fixed = fixed.toLowerCase();
+		}
+
+		if (_stripQueryStringFromUrls) {
+			int firstQuestionMarkIndex = fixed.indexOf("?");
+			if (firstQuestionMarkIndex != -1) {
+				fixed = fixed.substring(0, firstQuestionMarkIndex);
+			}
+		}
+
+		return fixed;
+	}
+
+	protected UrlMatcher getUrlMatcher() {
+		return _urlMatcher;
+	}
+
+	/**
+	 * For debugging.
+	 * @return  an unmodifiable map of {@link AnnotationFilterInvocationDefinition}ConfigAttributeDefinition
+	 * keyed by compiled patterns
+	 */
+	public Map<Object, ConfigAttributeDefinition> getConfigAttributeMap() {
+		return Collections.unmodifiableMap(_compiled);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() {
+		Assert.notNull(_urlMatcher, "url matcher is required");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AnnotationFilterInvocationDefinition.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AnnotationFilterInvocationDefinition.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AnnotationFilterInvocationDefinition.java	(revision 58)
@@ -0,0 +1,276 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.WordUtils;
+import org.codehaus.groovy.grails.commons.ApplicationHolder;
+import org.codehaus.groovy.grails.commons.ControllerArtefactHandler;
+import org.codehaus.groovy.grails.commons.GrailsApplication;
+import org.codehaus.groovy.grails.commons.GrailsClass;
+import org.codehaus.groovy.grails.commons.GrailsControllerClass;
+import org.codehaus.groovy.grails.web.context.ServletContextHolder;
+import org.codehaus.groovy.grails.web.mapping.UrlMappingInfo;
+import org.codehaus.groovy.grails.web.mapping.UrlMappingsHolder;
+import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap;
+import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
+import org.codehaus.groovy.grails.web.util.WebUtils;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.intercept.web.FilterInvocation;
+import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link FilterInvocationDefinitionSource} that uses rules defined with Controller annotations
+ * combined with static rules defined in <code>SecurityConfig.groovy</code>, e.g. for js, images, css
+ * or for rules that cannot be expressed in a controller like '/**'.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class AnnotationFilterInvocationDefinition extends AbstractFilterInvocationDefinition {
+
+	private UrlMappingsHolder _urlMappingsHolder;
+
+	@Override
+	protected String determineUrl(final FilterInvocation filterInvocation) {
+		HttpServletRequest request = filterInvocation.getHttpRequest();
+		HttpServletResponse response = filterInvocation.getHttpResponse();
+		ServletContext servletContext = ServletContextHolder.getServletContext();
+		GrailsApplication application = ApplicationHolder.getApplication();
+
+		GrailsWebRequest existingRequest = WebUtils.retrieveGrailsWebRequest();
+
+		String requestUrl = request.getRequestURI().substring(request.getContextPath().length());
+
+		String url = null;
+		try {
+			GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response, servletContext);
+			WebUtils.storeGrailsWebRequest(grailsRequest);
+
+			Map<String, Object> savedParams = copyParams(grailsRequest);
+
+			for (UrlMappingInfo mapping : _urlMappingsHolder.matchAll(requestUrl)) {
+				configureMapping(mapping, grailsRequest, savedParams);
+
+				url = findGrailsUrl(mapping, application);
+				if (url != null) {
+					break;
+				}
+			}
+		}
+		finally {
+			if (existingRequest == null) {
+				WebUtils.clearGrailsWebRequest();
+			}
+			else {
+				WebUtils.storeGrailsWebRequest(existingRequest);
+			}
+		}
+
+		if (!StringUtils.hasLength(url)) {
+			// probably css/js/image
+			url = requestUrl;
+		}
+
+		return lowercaseAndStringQuerystring(url);
+	}
+
+	private String findGrailsUrl(final UrlMappingInfo mapping, final GrailsApplication application) {
+
+		String actionName = mapping.getActionName();
+		if (!StringUtils.hasLength(actionName)) {
+			actionName = "";
+		}
+
+		String controllerName = mapping.getControllerName();
+
+		if (isController(controllerName, actionName, application)) {
+			if (!StringUtils.hasLength(actionName) || "null".equals(actionName)) {
+				actionName = "index";
+			}
+			return ("/" + controllerName + "/" + actionName).trim();
+		}
+
+		return null;
+	}
+
+	private boolean isController(final String controllerName, final String actionName,
+			final GrailsApplication application) {
+		return application.getArtefactForFeature(ControllerArtefactHandler.TYPE,
+				"/" + controllerName + "/" + actionName) != null;
+	}
+
+	private void configureMapping(final UrlMappingInfo mapping, final GrailsWebRequest grailsRequest,
+			final Map<String, Object> savedParams) {
+
+		// reset params since mapping.configure() sets values
+		GrailsParameterMap params = grailsRequest.getParams();
+		params.clear();
+		params.putAll(savedParams);
+
+		mapping.configure(grailsRequest);
+	}
+
+	@SuppressWarnings("unchecked")
+	private Map<String, Object> copyParams(final GrailsWebRequest grailsRequest) {
+		return new HashMap<String, Object>(grailsRequest.getParams());
+	}
+
+	/**
+	 * Called by the plugin to set controller role info.<br/>
+	 *
+	 * Reinitialize by calling <code>ctx.objectDefinitionSource.initialize(
+	 * 	ctx.authenticateService.securityConfig.security.annotationStaticRules,
+	 * 	ctx.grailsUrlMappingsHolder,
+	 * 	ApplicationHolder.application.controllerClasses)</code>
+	 *
+	 * @param staticRules  keys are URL patterns, values are role names for that pattern
+	 * @param urlMappingsHolder  mapping holder
+	 * @param controllerClasses  all controllers
+	 */
+	public void initialize(
+			final Map<String, Collection<String>> staticRules,
+			final UrlMappingsHolder urlMappingsHolder,
+			final GrailsClass[] controllerClasses) {
+
+		Map<String, Map<String, Set<String>>> actionRoleMap = new HashMap<String, Map<String,Set<String>>>();
+		Map<String, Set<String>> classRoleMap = new HashMap<String, Set<String>>();
+
+		Assert.notNull(staticRules, "staticRules map is required");
+		Assert.notNull(urlMappingsHolder, "urlMappingsHolder is required");
+
+		_compiled.clear();
+
+		_urlMappingsHolder = urlMappingsHolder;
+
+		for (GrailsClass controllerClass : controllerClasses) {
+			findControllerAnnotations((GrailsControllerClass)controllerClass, actionRoleMap, classRoleMap);
+		}
+
+		compileActionMap(actionRoleMap);
+		compileClassMap(classRoleMap);
+		compileStaticRules(staticRules);
+
+		if (_log.isTraceEnabled()) {
+			_log.trace("configs: " + _compiled);
+		}
+	}
+
+	private void compileActionMap(final Map<String, Map<String, Set<String>>> map) {
+		for (Map.Entry<String, Map<String, Set<String>>> controllerEntry : map.entrySet()) {
+			String controllerName = controllerEntry.getKey();
+			Map<String, Set<String>> actionRoles = controllerEntry.getValue();
+			for (Map.Entry<String, Set<String>> actionEntry : actionRoles.entrySet()) {
+				String actionName = actionEntry.getKey();
+				Set<String> roles = actionEntry.getValue();
+				storeMapping(controllerName, actionName, roles, false);
+			}
+		}
+	}
+
+	private void compileClassMap(final Map<String, Set<String>> classRoleMap) {
+		for (Map.Entry<String, Set<String>> entry : classRoleMap.entrySet()) {
+			String controllerName = entry.getKey();
+			Set<String> roles = entry.getValue();
+			storeMapping(controllerName, null, roles, false);
+		}
+	}
+
+	private void compileStaticRules(final Map<String, Collection<String>> staticRules) {
+		for (Map.Entry<String, Collection<String>> entry : staticRules.entrySet()) {
+			String pattern = entry.getKey();
+			Collection<String> roles = entry.getValue();
+			storeMapping(pattern, null, roles, true);
+		}
+	}
+
+	private void storeMapping(final String controllerNameOrPattern, final String actionName,
+			final Collection<String> roles, final boolean isPattern) {
+
+		String fullPattern;
+		if (isPattern) {
+			fullPattern = controllerNameOrPattern;
+		}
+		else {
+			StringBuilder sb = new StringBuilder();
+			sb.append('/').append(controllerNameOrPattern);
+			if (actionName != null) {
+				sb.append('/').append(actionName);
+			}
+			sb.append("/**");
+			fullPattern = sb.toString();
+		}
+
+		ConfigAttributeDefinition configAttribute = new ConfigAttributeDefinition(
+				roles.toArray(new String[roles.size()]));
+
+		Object key = getUrlMatcher().compile(fullPattern);
+		ConfigAttributeDefinition replaced = _compiled.put(key, configAttribute);
+		if (replaced != null) {
+			_log.warn("replaced rule for '" + key + "' with roles " + replaced.getConfigAttributes()
+					+ " with roles " + configAttribute.getConfigAttributes());
+		}
+	}
+
+	private void findControllerAnnotations(final GrailsControllerClass controllerClass,
+			final Map<String, Map<String, Set<String>>> actionRoleMap,
+			final Map<String, Set<String>> classRoleMap) {
+
+		Class<?> clazz = controllerClass.getClazz();
+		String controllerName = WordUtils.uncapitalize(controllerClass.getName());
+
+		Secured annotation = clazz.getAnnotation(Secured.class);
+		if (annotation != null) {
+			classRoleMap.put(controllerName, asSet(annotation.value()));
+		}
+
+		Map<String, Set<String>> annotatedClosureNames = findActionRoles(clazz);
+		if (annotatedClosureNames != null) {
+			actionRoleMap.put(controllerName, annotatedClosureNames);
+		}
+	}
+
+	private Map<String, Set<String>> findActionRoles(final Class<?> clazz) {
+		// since action closures are defined as "def foo = ..." they're
+		// fields, but they end up as private
+		Map<String, Set<String>> actionRoles = new HashMap<String, Set<String>>();
+		for (Field field : clazz.getDeclaredFields()) {
+			Secured annotation = field.getAnnotation(Secured.class);
+			if (annotation != null) {
+				actionRoles.put(field.getName(), asSet(annotation.value()));
+			}
+		}
+		return actionRoles;
+	}
+
+	private Set<String> asSet(final String[] strings) {
+		Set<String> set = new HashSet<String>();
+		for (String string : strings) {
+			set.add(string);
+		}
+		return set;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAccessDeniedHandlerImpl.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAccessDeniedHandlerImpl.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsAccessDeniedHandlerImpl.java	(revision 58)
@@ -0,0 +1,167 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.io.IOException;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.AccessDeniedException;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationTrustResolver;
+import org.springframework.security.AuthenticationTrustResolverImpl;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.ui.AccessDeniedHandler;
+import org.springframework.security.ui.savedrequest.SavedRequest;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.util.PortResolver;
+import org.springframework.util.Assert;
+
+/**
+ * {@link AccessDeniedHandler} for redirect to errorPage (not RequestDispatcher#forward).
+ *
+ * @author T.Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class GrailsAccessDeniedHandlerImpl implements AccessDeniedHandler, InitializingBean {
+
+	private String errorPage;
+	private String ajaxErrorPage;
+	private String ajaxHeader = WithAjaxAuthenticationProcessingFilterEntryPoint.AJAX_HEADER;
+	private PortResolver portResolver;
+	private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AccessDeniedHandler#handle(
+	 * 	javax.servlet.ServletRequest, javax.servlet.ServletResponse,
+	 * 	org.springframework.security.AccessDeniedException)
+	 */
+	public void handle(final ServletRequest req, final ServletResponse res, final AccessDeniedException e)
+			throws IOException {
+
+		HttpServletRequest request = (HttpServletRequest)req;
+		HttpServletResponse response = (HttpServletResponse)res;
+
+		if (e != null && isLoggedIn() && authenticationTrustResolver.isRememberMe(getAuthentication())) {
+			// user has a cookie but is getting bounced because of IS_AUTHENTICATED_FULLY,
+			// so Acegi won't save the original request
+			request.getSession().setAttribute(
+					AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY,
+					new SavedRequest(request, portResolver));
+		}
+
+		if (errorPage != null || (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null)) {
+			boolean includePort = true;
+			String scheme = request.getScheme();
+			String serverName = request.getServerName();
+			int serverPort = portResolver.getServerPort(request);
+			String contextPath = request.getContextPath();
+			boolean inHttp = "http".equals(scheme.toLowerCase());
+			boolean inHttps = "https".equals(scheme.toLowerCase());
+
+			if (inHttp && (serverPort == 80)) {
+				includePort = false;
+			}
+			else if (inHttps && (serverPort == 443)) {
+				includePort = false;
+			}
+
+			String commonRedirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "")
+					+ contextPath;
+			String redirectUrl = commonRedirectUrl;
+			if (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null) {
+				redirectUrl += ajaxErrorPage;
+			}
+			else if (errorPage != null) {
+				redirectUrl += errorPage;
+			}
+			else {
+				response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
+			}
+
+			response.sendRedirect(response.encodeRedirectURL(redirectUrl));
+		}
+
+		if (!response.isCommitted()) {
+			// Send 403 (we do this after response has been written)
+			response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
+		}
+	}
+
+	private boolean isLoggedIn() {
+		if (getAuthentication() == null) {
+			return false;
+		}
+		return getAuthentication().getPrincipal() instanceof UserDetails;
+	}
+
+	private Authentication getAuthentication() {
+		return SecurityContextHolder.getContext() == null ? null
+				: SecurityContextHolder.getContext().getAuthentication();
+	}
+
+	/**
+	 * Dependency injection for the error page, e.g. '/login/denied'.
+	 * @param page  the page
+	 */
+	public void setErrorPage(final String page) {
+		if (page != null && !page.startsWith("/")) {
+			throw new IllegalArgumentException("ErrorPage must begin with '/'");
+		}
+		errorPage = page;
+	}
+
+	/**
+	 * Dependency injection for the Ajax error page, e.g. '/login/deniedAjax'.
+	 * @param page  the page
+	 */
+	public void setAjaxErrorPage(final String page) {
+		if (page != null && !page.startsWith("/")) {
+			throw new IllegalArgumentException("ErrorPage must begin with '/'");
+		}
+		ajaxErrorPage = page;
+	}
+
+	/**
+	 * Dependency injection for the Ajax header name; defaults to 'X-Requested-With'.
+	 * @param header  the header name
+	 */
+	public void setAjaxHeader(final String header) {
+		ajaxHeader = header;
+	}
+
+	/**
+	 * Dependency injection for the port resolver.
+	 * @param resolver  the resolver
+	 */
+	public void setPortResolver(final PortResolver resolver) {
+		portResolver = resolver;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() {
+		Assert.notNull(ajaxHeader, "ajaxHeader is required");
+		Assert.notNull(portResolver, "portResolver is required");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUser.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUser.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUser.java	(revision 58)
@@ -0,0 +1,32 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import org.springframework.security.userdetails.UserDetails;
+
+/**
+ * Extends Spring Security's {@link UserDetails} interface to set Grails Domain
+ * Class at login, to load auth class from context.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public interface GrailsUser extends UserDetails {
+
+	/**
+	 * Get the domain object representing the user.
+	 * @return  the user
+	 */
+	Object getDomainClass();
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUserImpl.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUserImpl.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsUserImpl.java	(revision 58)
@@ -0,0 +1,73 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.dao.DaoAuthenticationProvider;
+import org.springframework.security.userdetails.User;
+
+/**
+ * Extends Spring Security's {@link User} class to set Grails Domain Class at login,
+ * to load auth class from context.
+ *
+ * @author T.Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class GrailsUserImpl extends User implements GrailsUser {
+
+	private static final long serialVersionUID = 6089520028447407158L;
+
+	private final Object domainClass;
+
+	/**
+	 * Constructor.
+	 * @param username the username presented to the
+	 *        {@link DaoAuthenticationProvider}
+	 * @param password the password that should be presented to the
+	 *        {@link DaoAuthenticationProvider}
+	 * @param enabled set to <code>true</code> if the user is enabled
+	 * @param accountNonExpired set to <code>true</code> if the account has not
+	 *        expired
+	 * @param credentialsNonExpired set to <code>true</code> if the credentials
+	 *        have not expired
+	 * @param accountNonLocked set to <code>true</code> if the account is not
+	 *        locked
+	 * @param authorities the authorities that should be granted to the caller
+	 *        if they presented the correct username and password and the user
+	 *        is enabled
+	 * @param user  the user domain instance
+	 *
+	 * @throws IllegalArgumentException if a <code>null</code> value was passed
+	 *         either as a parameter or as an element in the
+	 *         {@link GrantedAuthority}[] array
+	 */
+	public GrailsUserImpl(
+			final String username, final String password, final boolean enabled,
+			final boolean accountNonExpired, final boolean credentialsNonExpired,
+			final boolean accountNonLocked, final GrantedAuthority[] authorities,
+			final Object user) throws IllegalArgumentException {
+		super(username, password, enabled, accountNonExpired,
+				credentialsNonExpired, accountNonLocked, authorities);
+		domainClass = user;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser#getDomainClass()
+	 */
+	public Object getDomainClass() {
+		return domainClass;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsWebApplicationObjectSupport.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsWebApplicationObjectSupport.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/GrailsWebApplicationObjectSupport.java	(revision 58)
@@ -0,0 +1,113 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.springframework.orm.hibernate3.SessionFactoryUtils;
+import org.springframework.orm.hibernate3.SessionHolder;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+import org.springframework.web.context.support.WebApplicationObjectSupport;
+
+/**
+ * Grails Web Application Object Support.
+ *
+ * @author T.Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public abstract class GrailsWebApplicationObjectSupport extends WebApplicationObjectSupport {
+
+	private final Logger _log = Logger.getLogger(getClass());
+
+	private SessionFactory _sessionFactory;
+
+	/**
+	 * Dependency injection for Hibernate session factory.
+	 * @param sessionFactory  the factory
+	 */
+	public void setSessionFactory(final SessionFactory sessionFactory) {
+		_sessionFactory = sessionFactory;
+	}
+
+	/**
+	 * Holds the session created or existing session and a flag indicating whether it was
+	 * existing (so we know whether to close it or not).
+	 */
+	public static class SessionContainer {
+		private final Session _session;
+		private final boolean _existingSession;
+
+		private SessionContainer(final Session session, final boolean existingSession) {
+			_session = session;
+			_existingSession = existingSession;
+		}
+
+		/**
+		 * Get the session.
+		 * @return  the session
+		 */
+		public Session getSession() {
+			return _session;
+		}
+	}
+
+	/**
+	 * Set up hibernate session.
+	 * @return  the session container, which holds the session and a boolean indicating if the session was pre-existing
+	 */
+	protected SessionContainer setUpSession() {
+		SessionFactory sessionFactory = getSessionFactory();
+
+		Session session;
+		boolean existing;
+		if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
+			_log.debug("Session already has transaction attached");
+			existing = true;
+			session = ((SessionHolder)TransactionSynchronizationManager.getResource(sessionFactory)).getSession();
+		}
+		else {
+			_log.debug("Session does not have transaction attached... Creating new one");
+			existing = false;
+			session = SessionFactoryUtils.getSession(sessionFactory, true);
+			SessionHolder sessionHolder = new SessionHolder(session);
+			TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+		}
+
+		return new SessionContainer(session, existing);
+	}
+
+	/**
+	 * Release Session.
+	 */
+	protected void releaseSession(final SessionContainer session) {
+		if (session._existingSession) {
+			return;
+		}
+
+		SessionFactory sessionFactory = getSessionFactory();
+		SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory);
+		SessionFactoryUtils.releaseSession(sessionHolder.getSession(), sessionFactory);
+		_log.debug("Session released");
+	}
+
+	private SessionFactory getSessionFactory() {
+		if (_sessionFactory == null) {
+			// should be set via DI, but for backwards compatibility lookup the standard bean
+			_sessionFactory  = (SessionFactory)getWebApplicationContext().getBean("sessionFactory");
+		}
+		return _sessionFactory;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/IpAddressFilter.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/IpAddressFilter.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/IpAddressFilter.java	(revision 58)
@@ -0,0 +1,187 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Required;
+import org.springframework.security.ui.FilterChainOrder;
+import org.springframework.security.ui.SpringSecurityFilter;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Blocks access to protected resources based on IP address. Sends 404 rather than
+ * reporting error to hide visibility of the resources.
+ * <br/>
+ * Supports either Ant-style patterns (e.g. 10.**) or masked patterns
+ * (e.g. 192.168.1.0/24 or 202.24.0.0/14).
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class IpAddressFilter extends SpringSecurityFilter implements InitializingBean {
+
+	private final Logger _log = Logger.getLogger(getClass());
+
+	private final AntPathMatcher _pathMatcher = new AntPathMatcher();
+
+	private Map<String, String> _restrictions;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.SpringSecurityFilter#doFilterHttp(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+	 * 	javax.servlet.FilterChain)
+	 */
+	@Override
+	protected void doFilterHttp(
+			  final HttpServletRequest request,
+			  final HttpServletResponse response,
+			  final FilterChain chain) throws IOException, ServletException {
+
+		if (!isAllowed(request.getRemoteAddr(), request.getRequestURI())) {
+			response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404
+			return;
+		}
+
+		chain.doFilter(request, response);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.SpringSecurityFilter#getOrder()
+	 */
+	public int getOrder() {
+		return FilterChainOrder.LOGOUT_FILTER + 1;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() {
+		Assert.notNull(_restrictions, "ipRestrictions is required");
+	}
+
+	/**
+	 * Dependency injection for the ip/pattern restriction map.
+	 * @param restrictions  the map
+	 */
+	@Required
+	public void setIpRestrictions(final Map<String, String> restrictions) {
+		_restrictions = restrictions;
+	}
+
+	private boolean isAllowed(final String ip, final String requestURI) {
+
+		if ("127.0.0.1".equals(ip)) {
+			return true;
+		}
+
+		String reason = null;
+
+		for (Map.Entry<String, String> entry : _restrictions.entrySet()) {
+			String uriPattern = entry.getKey();
+			if (!_pathMatcher.match(uriPattern, requestURI)) {
+				continue;
+			}
+
+			String ipPattern = entry.getValue();
+			if (ipPattern.contains("/")) {
+				try {
+					if (!matchesUsingMask(ipPattern, ip)) {
+						reason = ipPattern;
+						break;
+					}
+				}
+				catch (UnknownHostException e) {
+					reason = e.getMessage();
+					break;
+				}
+			}
+			else if (!_pathMatcher.match(ipPattern, ip)) {
+				reason = ipPattern;
+				break;
+			}
+		}
+
+		if (reason != null) {
+			_log.error("disallowed request " + requestURI + " from " + ip + ": " + reason);
+			return false;
+		}
+
+		return true;
+	}
+
+	private boolean matchesUsingMask(final String ipPattern, final String ip) throws UnknownHostException {
+
+		String[] addressAndMask = StringUtils.split(ipPattern, "/");
+
+		InetAddress requiredAddress = parseAddress(addressAndMask[0]);
+		InetAddress remoteAddress = parseAddress(ip);
+		if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
+			throw new IllegalArgumentException(
+					"IP Address in expression must be the same type as version returned by request");
+		}
+
+		int maskBits = Integer.parseInt(addressAndMask[1]);
+		if (maskBits == 0) {
+			return remoteAddress.equals(requiredAddress);
+		}
+
+		int oddBits = maskBits % 8;
+		byte[] mask = new byte[maskBits / 8 + (oddBits == 0 ? 0 : 1)];
+
+		Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte)0xFF);
+
+		if (oddBits != 0) {
+			int finalByte = (1 << oddBits) - 1;
+			finalByte <<= 8 - oddBits;
+			mask[mask.length - 1] = (byte) finalByte;
+		}
+
+		byte[] remoteAddressBytes = remoteAddress.getAddress();
+		byte[] requiredAddressBytes = requiredAddress.getAddress();
+		for (int i = 0; i < mask.length; i++) {
+			if ((remoteAddressBytes[i] & mask[i]) != (requiredAddressBytes[i] & mask[i])) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	private InetAddress parseAddress(final String address) throws UnknownHostException {
+		try {
+			return InetAddress.getByName(address);
+		}
+		catch (UnknownHostException e) {
+			_log.error("unable to parse " + address);
+			throw e;
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/QuietMethodSecurityInterceptor.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/QuietMethodSecurityInterceptor.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/QuietMethodSecurityInterceptor.java	(revision 58)
@@ -0,0 +1,95 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.security.intercept.InterceptorStatusToken;
+import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
+
+/**
+ * {@link MethodSecurityInterceptor} that doesn't throw exceptions if Method Access is
+ * denied, returns <code>null</code> instead.
+ *
+ * @author T.Yamamoto
+ */
+public class QuietMethodSecurityInterceptor extends MethodSecurityInterceptor {
+
+	private boolean throwException;
+	private Exception lastException;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor#invoke(
+	 * 	org.aopalliance.intercept.MethodInvocation)
+	 */
+	@Override
+	public Object invoke(final MethodInvocation mi) throws Throwable {
+		Object result = null;
+		InterceptorStatusToken token = null;
+		try {
+			token = super.beforeInvocation(mi);
+		}
+		catch (Exception e) {
+			lastException = e;
+			if (throwException) {
+				throw e;
+			}
+			logger.error(e.getMessage());
+			return null;
+		}
+
+		try {
+			result = mi.proceed();
+		}
+		catch (Exception e) {
+			lastException = e;
+			if (throwException) {
+				throw e;
+			}
+			logger.error(e.getMessage());
+			return null;
+		}
+
+		try {
+			result = super.afterInvocation(token, result);
+		}
+		catch (Exception e) {
+			lastException = e;
+			if (throwException) {
+				throw e;
+			}
+			logger.error(e.getMessage());
+			return null;
+		}
+
+		return result;
+	}
+
+	/**
+	 * For testing.
+	 * @return  the most recent exception, if any.
+	 */
+	/*package*/ Exception getLastException() {
+		return lastException;
+	}
+
+	/**
+	 * Dependency injection for throw exception flag.
+	 * @param throwException  if <code>true</code> throw exceptions, otherwise just log
+	 */
+	public void setThrowException(final boolean throwException) {
+		this.throwException = throwException;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinition.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinition.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/RequestmapFilterInvocationDefinition.java	(revision 58)
@@ -0,0 +1,122 @@
+/* Copyright 2006-2009 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.intercept.web.FilterInvocation;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class RequestmapFilterInvocationDefinition extends AbstractFilterInvocationDefinition {
+
+	private boolean _initialized;
+
+	private String _requestMapClass;
+	private String _requestMapPathFieldName;
+	private String _requestMapConfigAttributeField;
+	private RequestmapFilterInvocationDefinitionHelper _helper;
+
+	@Override
+	protected String determineUrl(final FilterInvocation filterInvocation) {
+		HttpServletRequest request = filterInvocation.getHttpRequest();
+		String requestUrl = request.getRequestURI().substring(request.getContextPath().length());
+		return lowercaseAndStringQuerystring(requestUrl);
+	}
+
+	@Override
+	protected void initialize() {
+		if (!_initialized) {
+			reset();
+			_initialized = true;
+		}
+	}
+
+	/**
+	 * Call at startup or when <code>Requestmap</code> instances have been added, removed, or changed.
+	 */
+	public synchronized void reset() {
+		Map<String, String> data = _helper.loadRequestmaps();
+		_compiled.clear();
+
+		for (Map.Entry<String, String> entry : data.entrySet()) {
+			String pattern = entry.getKey();
+			String[] tokens = StringUtils.commaDelimitedListToStringArray(entry.getValue());
+			storeMapping(pattern, tokens);
+		}
+
+		if (_log.isTraceEnabled()) {
+			_log.trace("configs: " + _compiled);
+		}
+	}
+
+	private void storeMapping(final String pattern, final String[] tokens) {
+
+		ConfigAttributeDefinition configAttribute = new ConfigAttributeDefinition(tokens);
+
+		Object key = getUrlMatcher().compile(pattern);
+
+		ConfigAttributeDefinition replaced = _compiled.put(key, configAttribute);
+		if (replaced != null) {
+			_log.warn("replaced rule for '" + key + "' with roles " + replaced.getConfigAttributes()
+					+ " with roles " + configAttribute.getConfigAttributes());
+		}
+	}
+
+	/**
+	 * Dependency injection for the Requestmap class name.
+	 * @param name  the class name
+	 */
+	public void setRequestMapClass(final String name) {
+		_requestMapClass = name;
+	}
+
+	/**
+	 * Dependency injection for the Requestmap config attribute (e.g. roles) field name.
+	 * @param name
+	 */
+	public void setRequestMapConfigAttributeField(final String name) {
+		_requestMapConfigAttributeField = name;
+	}
+
+	/**
+	 * Dependency injection for the Requestmap path field name.
+	 * @param name
+	 */
+	public void setRequestMapPathFieldName(final String name) {
+		_requestMapPathFieldName = name;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	@Override
+	public void afterPropertiesSet() {
+		super.afterPropertiesSet();
+		Assert.notNull(_requestMapClass, "Requestmap class name is required");
+		Assert.notNull(_requestMapPathFieldName, "Requestmap path field name is required");
+		Assert.notNull(_requestMapConfigAttributeField, "Requestmap config attribute field name is required");
+
+		_helper = new RequestmapFilterInvocationDefinitionHelper(_requestMapClass,
+				_requestMapPathFieldName, _requestMapConfigAttributeField);
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/Secured.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/Secured.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/Secured.java	(revision 58)
@@ -0,0 +1,25 @@
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for Controllers at the class level or per-action, defining what roles
+ * are required for the entire controller or action.
+ */
+@Target({ElementType.FIELD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface Secured {
+
+	/**
+	 * Defines the security configuration attributes (e.g. ROLE_USER, ROLE_ADMIN, etc.)
+	 * @return  the names of the roles
+	 */
+   String[] value();
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityAnnotationAttributes.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityAnnotationAttributes.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityAnnotationAttributes.java	(revision 58)
@@ -0,0 +1,115 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.metadata.Attributes;
+import org.springframework.security.SecurityConfig;
+import org.springframework.security.annotation.Secured;
+
+/**
+ * Re-implementation of Acegi's {@link SecurityAnnotationAttributes} as a temporary
+ * fix until I can figure out how to do this correctly in 2.0.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class SecurityAnnotationAttributes implements Attributes {
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Set<SecurityConfig> getAttributes(final Class target) {
+		Set<SecurityConfig> attributes = new HashSet<SecurityConfig>();
+
+		for (Annotation annotation : target.getAnnotations()) {
+			if (annotation instanceof Secured) {
+				Secured attr = (Secured)annotation;
+				for (String auth : attr.value()) {
+					attributes.add(new SecurityConfig(auth));
+				}
+				break;
+			}
+		}
+
+		return attributes;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method)
+	 */
+	public Set<SecurityConfig> getAttributes(final Method method) {
+		Set<SecurityConfig> attributes = new HashSet<SecurityConfig>();
+
+		Annotation[] annotations = AnnotationUtils.getAnnotations(method);
+		for (Annotation annotation : annotations) {
+			if (annotation instanceof Secured) {
+				Secured attr = (Secured)annotation;
+				for (String auth : attr.value()) {
+					attributes.add(new SecurityConfig(auth));
+				}
+
+				break;
+			}
+		}
+
+		return attributes;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.Class, java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection getAttributes(final Class clazz, final Class filter) {
+		throw new UnsupportedOperationException("Unsupported operation");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Method, java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection getAttributes(final Method method, final Class clazz) {
+		throw new UnsupportedOperationException("Unsupported operation");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field)
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection getAttributes(final Field field) {
+		throw new UnsupportedOperationException("Unsupported operation");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.metadata.Attributes#getAttributes(java.lang.reflect.Field, java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection getAttributes(final Field field, final Class clazz) {
+		throw new UnsupportedOperationException("Unsupported operation");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityRequestHolder.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityRequestHolder.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/SecurityRequestHolder.java	(revision 58)
@@ -0,0 +1,67 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Uses a {@link ThreadLocal} to store the current request and response.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public final class SecurityRequestHolder {
+
+	private static final ThreadLocal<HttpServletRequest> REQUEST_HOLDER = new ThreadLocal<HttpServletRequest>();
+	private static final ThreadLocal<HttpServletResponse> RESPONSE_HOLDER = new ThreadLocal<HttpServletResponse>();
+
+	private SecurityRequestHolder() {
+		// static only
+	}
+
+	/**
+	 * Clear the saved request.
+	 */
+	public static void reset() {
+		REQUEST_HOLDER.set(null);
+		RESPONSE_HOLDER.set(null);
+	}
+
+	/**
+	 * Set the current request and response.
+	 * @param request  the request
+	 * @param response  the response
+	 */
+	public static void set(final HttpServletRequest request, final HttpServletResponse response) {
+		REQUEST_HOLDER.set(request);
+		RESPONSE_HOLDER.set(response);
+	}
+
+	/**
+	 * Get the current request.
+	 * @return  the request
+	 */
+	public static HttpServletRequest getRequest() {
+		return REQUEST_HOLDER.get();
+	}
+
+	/**
+	 * Get the current response.
+	 * @return  the response
+	 */
+	public static HttpServletResponse getResponse() {
+		return RESPONSE_HOLDER.get();
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/WithAjaxAuthenticationProcessingFilterEntryPoint.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/WithAjaxAuthenticationProcessingFilterEntryPoint.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/WithAjaxAuthenticationProcessingFilterEntryPoint.java	(revision 58)
@@ -0,0 +1,76 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint;
+
+/**
+ * {@link AuthenticationProcessingFilterEntryPoint} with Ajax login form option if
+ * Method Access is denied returns <code>null</code>.
+ *
+ * @author T.Yamamoto
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class WithAjaxAuthenticationProcessingFilterEntryPoint extends AuthenticationProcessingFilterEntryPoint {
+
+	/**
+	 * Default value for the name of the Ajax header.
+	 */
+	public static final String AJAX_HEADER = "X-Requested-With";
+
+	private String ajaxLoginFormUrl;
+	private String ajaxHeader = AJAX_HEADER;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint#determineUrlToUseForThisRequest(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+	 * 	org.springframework.security.AuthenticationException)
+	 */
+	@Override
+	protected String determineUrlToUseForThisRequest(
+			final HttpServletRequest request, final HttpServletResponse response,
+			final AuthenticationException exception) {
+
+		if (request.getHeader(ajaxHeader) != null && ajaxLoginFormUrl != null) {
+			return ajaxLoginFormUrl;
+		}
+
+		return getLoginFormUrl();
+	}
+
+	/**
+	 * Dependency injection for the Ajax login form url, e.g. '/login/authAjax'.
+	 * @param url  the url
+	 */
+	public void setAjaxLoginFormUrl(final String url) {
+		if (url != null && !url.startsWith("/")) {
+			throw new IllegalArgumentException("ajaxLoginFormUrl must begin with '/'");
+		}
+		ajaxLoginFormUrl = url;
+	}
+
+	/**
+	 * Dependency injection for the Ajax header name; defaults to 'X-Requested-With'.
+	 * @param header  the header name
+	 */
+	public void setAjaxHeader(final String header) {
+		ajaxHeader = header;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProcessingFilter.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProcessingFilter.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProcessingFilter.java	(revision 58)
@@ -0,0 +1,175 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.facebook;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.codehaus.groovy.grails.plugins.springsecurity.SecurityRequestHolder;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.ui.FilterChainOrder;
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Document;
+
+import com.google.code.facebookapi.FacebookWebappHelper;
+
+/**
+ * Intercepts j_spring_facebook_security_check to trigger Facebook login.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class FacebookAuthenticationProcessingFilter extends AbstractProcessingFilter {
+
+	private String _apiKey;
+	private String _secretKey;
+	private String _authenticationUrlRoot;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#attemptAuthentication(
+	 * 	javax.servlet.http.HttpServletRequest)
+	 */
+	@Override
+	public Authentication attemptAuthentication(final HttpServletRequest request) throws AuthenticationException {
+
+		String authToken = request.getParameter("auth_token");
+		if (!StringUtils.hasText(authToken)) {
+			// trigger a redirect to the Facebook login
+			throw new FacebookAuthenticationRequiredException();
+		}
+
+		FacebookAuthenticationToken token = createToken(
+				authToken, request, SecurityRequestHolder.getResponse(),
+				_apiKey, _secretKey);
+
+		token.setDetails(authenticationDetailsSource.buildDetails(request));
+
+		Authentication authentication = getAuthenticationManager().authenticate(token);
+		if (authentication.isAuthenticated()) {
+			setLastUsername(token.getUserId(), request);
+		}
+
+		return authentication;
+	}
+
+	private void setLastUsername(final long userId, final HttpServletRequest request) {
+		HttpSession session = request.getSession(false);
+		if (session != null || getAllowSessionCreation()) {
+			request.getSession().setAttribute(
+					AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY,
+					String.valueOf(userId));
+		}
+	}
+
+	/**
+	 * Build an authentication from a login <code>auth_token</code>.
+	 * @param authToken  the <code>auth_token</code>
+	 * @param request  the http request
+	 * @param response  the http response
+	 * @param apiKey  the API key
+	 * @param secretKey  the secret key
+	 * @return  the auth token
+	 */
+	protected FacebookAuthenticationToken createToken(
+			final String authToken, final HttpServletRequest request, final HttpServletResponse response,
+			final String apiKey, final String secretKey) {
+
+		try {
+			FacebookWebappHelper<Document> helper = FacebookWebappHelper.newInstanceXml(
+					request, response, apiKey, secretKey);
+
+			if (helper.isLogin()) {
+				String sessionKey = helper.doGetSession(authToken);
+				return new FacebookAuthenticationToken(helper.getUser().longValue(), sessionKey);
+			}
+
+			return new FacebookAuthenticationToken(FacebookAuthenticationToken.Status.failure, null);
+		}
+		catch (RuntimeException e) {
+			return new FacebookAuthenticationToken(FacebookAuthenticationToken.Status.error, e.getMessage());
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#determineFailureUrl(
+	 * 	javax.servlet.http.HttpServletRequest, org.springframework.security.AuthenticationException)
+	 */
+	@Override
+	protected String determineFailureUrl(final HttpServletRequest request, final AuthenticationException failed) {
+		if (failed instanceof FacebookAuthenticationRequiredException) {
+			return _authenticationUrlRoot + _apiKey;
+		}
+
+		return super.determineFailureUrl(request, failed);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#getDefaultFilterProcessesUrl()
+	 */
+	@Override
+	public String getDefaultFilterProcessesUrl() {
+		return "/j_spring_facebook_security_check";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.SpringSecurityFilter#getOrder()
+	 */
+	public int getOrder() {
+		return FilterChainOrder.OPENID_PROCESSING_FILTER + 1;
+	}
+
+	/**
+	 * Dependency injection for the API key.
+	 * @param key  the key
+	 */
+	public void setApiKey(final String key) {
+		_apiKey = key;
+	}
+
+	/**
+	 * Dependency injection for the secret key.
+	 * @param key  the key
+	 */
+	public void setSecretKey(final String key) {
+		_secretKey = key;
+	}
+
+	/**
+	 * Dependency injection for the Facebook auth url root.
+	 * @param authenticationUrlRoot  the url root
+	 */
+	public void setAuthenticationUrlRoot(String authenticationUrlRoot) {
+		_authenticationUrlRoot = authenticationUrlRoot;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.AbstractProcessingFilter#afterPropertiesSet()
+	 */
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		super.afterPropertiesSet();
+      Assert.notNull(_apiKey, "API key must be specified");
+      Assert.notNull(_secretKey, "Secret key must be specified");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProvider.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProvider.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationProvider.java	(revision 58)
@@ -0,0 +1,90 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.facebook;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.AuthenticationServiceException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.providers.AuthenticationProvider;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.util.Assert;
+
+/**
+ * Finalizes the authentication process by populating the local authorities for the authenticated user.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class FacebookAuthenticationProvider implements AuthenticationProvider, InitializingBean {
+
+	private UserDetailsService _userDetailsService;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.AuthenticationProvider#authenticate(
+	 * 	org.springframework.security.Authentication)
+	 */
+	public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
+
+		if (!supports(authentication.getClass()) ||
+				!(authentication instanceof FacebookAuthenticationToken)) {
+			return null;
+		}
+
+		FacebookAuthenticationToken token = (FacebookAuthenticationToken)authentication;
+		FacebookAuthenticationToken.Status status = token.getStatus();
+
+		switch (status) {
+			case success:
+				UserDetails userDetails = _userDetailsService.loadUserByUsername(String.valueOf(token.getUserId()));
+				return new FacebookAuthenticationToken(userDetails.getAuthorities(),
+						token.getUserId(), token.getSessionKey());
+			case failure:
+				throw new BadCredentialsException("Log in failed - identity could not be verified");
+			case error:
+				throw new AuthenticationServiceException("Error message from server: " + token.getErrorMessage());
+		}
+
+		// unreachable
+		return null;
+	}
+
+	/**
+	 * Dependency injection for the user detail service.
+	 * @param userDetailsService  the service
+	 */
+	public void setUserDetailsService(final UserDetailsService userDetailsService) {
+		_userDetailsService = userDetailsService;
+	}
+
+   /**
+    * {@inheritDoc}
+    * @see org.springframework.security.providers.AuthenticationProvider#supports(java.lang.Class)
+    */
+   @SuppressWarnings("unchecked")
+	public boolean supports(final Class authentication) {
+       return FacebookAuthenticationToken.class.isAssignableFrom(authentication);
+   }
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() {
+		Assert.notNull(_userDetailsService, "The userDetailsService must be set");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationRequiredException.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationRequiredException.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationRequiredException.java	(revision 58)
@@ -0,0 +1,34 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.facebook;
+
+import org.springframework.security.AuthenticationException;
+
+/**
+ * Used to tigger a redirect to the Facebook login page.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class FacebookAuthenticationRequiredException extends AuthenticationException {
+
+	private static final long serialVersionUID = -1162739729449839527L;
+
+	/**
+	 * Default constructor.
+	 */
+	public FacebookAuthenticationRequiredException() {
+		super("External Authentication Required");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationToken.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationToken.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookAuthenticationToken.java	(revision 58)
@@ -0,0 +1,135 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.facebook;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.AbstractAuthenticationToken;
+
+/**
+ * Authentication token with Facebook-specific extra information.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class FacebookAuthenticationToken extends AbstractAuthenticationToken {
+
+	private static final long serialVersionUID = 1022970403466610153L;
+
+	private Status _status;
+	private long _userId;
+	private String _sessionKey;
+	private String _errorMessage;
+
+	/**
+	 * Token type.
+	 */
+	public static enum Status {
+		/** successful authentication. */
+		success,
+		/** failed authentication. */
+		failure,
+		/** authentication error. */
+		error
+	}
+
+	/**
+	 * Created by the OpenIDAuthenticationProvider on successful authentication.
+	 * @param authorities  roles
+	 * @param userId
+	 * @param sessionKey
+	 */
+	public FacebookAuthenticationToken(final GrantedAuthority[] authorities,
+			final long userId, final String sessionKey) {
+		super(authorities);
+		_status = Status.success;
+		_userId = userId;
+		_sessionKey = sessionKey;
+		setAuthenticated(true);
+	}
+
+	/**
+	 * Created by {@link FacebookAuthenticationProcessingFilter} from Facebook login info,
+	 * but before loading roles.
+	 * @param userId  the UID
+	 * @param sessionKey  the session key
+	 */
+	public FacebookAuthenticationToken(final long userId, final String sessionKey) {
+		super(new GrantedAuthority[0]);
+		_status = Status.success;
+		_userId = userId;
+		_sessionKey = sessionKey;
+		setAuthenticated(false);
+	}
+
+	/**
+	 * Create a failure token.
+	 * @param status  a non-success token
+	 * @param errorMessage  the error message
+	 */
+	public FacebookAuthenticationToken(final Status status, final String errorMessage) {
+		super(new GrantedAuthority[0]);
+		_status = status;
+		_errorMessage = errorMessage;
+		setAuthenticated(false);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.AbstractAuthenticationToken#getCredentials()
+	 */
+	public Object getCredentials() {
+		// we don't have access to password
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.providers.AbstractAuthenticationToken#getPrincipal()
+	 */
+	public Object getPrincipal() {
+		return _userId;
+	}
+
+	/**
+	 * The Facebook UID.
+	 * @return  the uid
+	 */
+	public long getUserId() {
+		return _userId;
+	}
+
+	/**
+	 * The status.
+	 * @return  the status
+	 */
+	public Status getStatus() {
+		return _status;
+	}
+
+	/**
+	 * The login session key.
+	 * @return  the key
+	 */
+	public String getSessionKey() {
+		return _sessionKey;
+	}
+
+	/**
+	 * Get the error message (if status is <code>error</code>).
+	 * @return  the message
+	 */
+	public String getErrorMessage() {
+		return _errorMessage;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookLogoutHandler.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookLogoutHandler.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook/FacebookLogoutHandler.java	(revision 58)
@@ -0,0 +1,78 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.facebook;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.Authentication;
+import org.springframework.security.ui.logout.LogoutHandler;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Removes cookies at logout.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class FacebookLogoutHandler implements LogoutHandler, InitializingBean {
+
+	private String _apiKey;
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.ui.logout.LogoutHandler#logout(
+	 * 	javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
+	 * 	org.springframework.security.Authentication)
+	 */
+	public void logout(final HttpServletRequest request, final HttpServletResponse response,
+			final Authentication authentication) {
+
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+			String path = StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/";
+			for (Cookie cookie : cookies) {
+				if (cookie.getName().startsWith(_apiKey)) {
+					cancelCookie(cookie.getName(), path, response);
+				}
+			}
+		}
+	}
+
+	private void cancelCookie(final String name, final String path, final HttpServletResponse response) {
+      Cookie cookie = new Cookie(name, null);
+      cookie.setMaxAge(0);
+      cookie.setPath(path);
+      response.addCookie(cookie);
+	}
+
+	/**
+	 * Dependency injection for the API key.
+	 * @param key  the key
+	 */
+	public void setApiKey(final String key) {
+		_apiKey = key;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+	 */
+	public void afterPropertiesSet() {
+      Assert.notNull(_apiKey, "API key must be specified");
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUser.java
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUser.java	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/ldap/GrailsLdapUser.java	(revision 58)
@@ -0,0 +1,87 @@
+/* Copyright 2006-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.grails.plugins.springsecurity.ldap;
+
+import javax.naming.directory.Attributes;
+
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser;
+import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserImpl;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.userdetails.ldap.LdapUserDetails;
+
+/**
+ * A {@link GrailsUser} for use in LDAP authentication.
+ *
+ * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
+ */
+public class GrailsLdapUser extends GrailsUserImpl implements GrailsUser, LdapUserDetails {
+
+	private static final long serialVersionUID = -1557817722745366207L;
+
+	private final Attributes _attributes;
+	private final String _dn;
+
+	/**
+	 * Constructor from {@link LdapUserDetails}.
+	 * @param details  the original details
+	 * @param domainClass  the domain instance
+	 */
+	@SuppressWarnings("deprecation") // just passing along the core impl
+	public GrailsLdapUser(final LdapUserDetails details, final Object domainClass) {
+		super(details.getUsername(), details.getPassword(), details.isEnabled(),
+				details.isAccountNonExpired(), details.isCredentialsNonExpired(),
+				details.isAccountNonLocked(), details.getAuthorities(), domainClass);
+		_attributes = details.getAttributes();
+		_dn = details.getDn();
+	}
+
+	/**
+	 * Full constructor.
+	 * @param username  the username
+	 * @param password  the password
+	 * @param enabled  whether the user is enabled
+	 * @param accountNonExpired  whether the user's account is expired
+	 * @param credentialsNonExpired  whether the user's credentials are locked
+	 * @param accountNonLocked  whether the user's account is locked
+	 * @param authorities  authorities
+	 * @param attributes  attributes
+	 * @param dn  distinguished name
+	 * @param domainClass  the domain instance
+	 */
+	public GrailsLdapUser(final String username, final String password, final boolean enabled,
+			final boolean accountNonExpired, final boolean credentialsNonExpired,
+			final boolean accountNonLocked, final GrantedAuthority[] authorities,
+			final Attributes attributes, final String dn, final Object domainClass) {
+		super(username, password, enabled, accountNonExpired, credentialsNonExpired,
+				accountNonLocked, authorities, domainClass);
+		_attributes = attributes;
+		_dn = dn;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Attributes getAttributes() {
+		return _attributes;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @see org.springframework.security.userdetails.ldap.LdapUserDetails#getDn()
+	 */
+	public String getDn() {
+		return _dn;
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Authority.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Authority.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Authority.groovy	(revision 58)
@@ -0,0 +1,21 @@
+${authorityClassPackage}
+
+${personClassImport}
+
+/**
+ * Authority domain class.
+ */
+class ${authorityClassName} {
+
+	static hasMany = [people: ${personClassName}]
+
+	/** description */
+	String description
+	/** ROLE String */
+	String authority
+
+	static constraints = {
+		authority(blank: false, unique: true)
+		description()
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Person.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Person.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Person.groovy	(revision 58)
@@ -0,0 +1,37 @@
+${personClassPackage}
+
+${authorityClassImport}
+
+/**
+ * User domain class.
+ */
+class ${personClassName} {
+	static transients = ['pass']
+	static hasMany = [authorities: ${authorityClassName}]
+	static belongsTo = ${authorityClassName}
+
+	/** Username */
+	String username
+	/** User Real Name*/
+	String userRealName
+	/** MD5 Password */
+	String passwd
+	/** enabled */
+	boolean enabled
+
+	String email
+	boolean emailShow
+
+	/** description */
+	String description = ''
+
+	/** plain password to create a MD5 password */
+	String pass = '[secret]'
+
+	static constraints = {
+		username(blank: false, unique: true)
+		userRealName(blank: false)
+		passwd(blank: false)
+		enabled()
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Requestmap.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Requestmap.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_Requestmap.groovy	(revision 58)
@@ -0,0 +1,15 @@
+${requestmapClassPackage}
+
+/**
+ * Request Map domain class.
+ */
+class ${requestmapClassName} {
+
+	String url
+	String configAttribute
+
+	static constraints = {
+		url(blank: false, unique: true)
+		configAttribute(blank: false)
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_SecurityConfig.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_SecurityConfig.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/_SecurityConfig.groovy	(revision 58)
@@ -0,0 +1,10 @@
+security {
+
+	// see DefaultSecurityConfig.groovy for all settable/overridable properties
+
+	active = true
+
+	loginUserDomainClass = "$personClass"
+	authorityDomainClass = "$authorityClass"
+	requestMapClass = "$requestmapClass"
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LoginController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LoginController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LoginController.groovy	(revision 58)
@@ -0,0 +1,179 @@
+import org.codehaus.groovy.grails.plugins.springsecurity.RedirectUtils
+import org.grails.plugins.springsecurity.service.AuthenticateService
+
+import org.springframework.security.AuthenticationTrustResolverImpl
+import org.springframework.security.DisabledException
+import org.springframework.security.context.SecurityContextHolder as SCH
+import org.springframework.security.ui.AbstractProcessingFilter
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter
+
+/**
+ * Login Controller (Example).
+ */
+class LoginController {
+
+	/**
+	 * Dependency injection for the authentication service.
+	 */
+	def authenticateService
+
+	/**
+	 * Dependency injection for OpenIDConsumer.
+	 */
+	def openIDConsumer
+
+	/**
+	 * Dependency injection for OpenIDAuthenticationProcessingFilter.
+	 */
+	def openIDAuthenticationProcessingFilter
+
+	private final authenticationTrustResolver = new AuthenticationTrustResolverImpl()
+
+	def index = {
+		if (isLoggedIn()) {
+			redirect uri: '/'
+		}
+		else {
+			redirect action: auth, params: params
+		}
+	}
+
+	/**
+	 * Show the login page.
+	 */
+	def auth = {
+
+		nocache response
+
+		if (isLoggedIn()) {
+			redirect uri: '/'
+			return
+		}
+
+		String view
+		String postUrl
+		def config = authenticateService.securityConfig.security
+		if (config.useOpenId) {
+			view = 'openIdAuth'
+			postUrl = "${request.contextPath}/login/openIdAuthenticate"
+		}
+		else if (config.useFacebook) {
+			view = 'facebookAuth'
+			postUrl = "${request.contextPath}${config.facebook.filterProcessesUrl}"
+		}
+		else {
+			view = 'auth'
+			postUrl = "${request.contextPath}${config.filterProcessesUrl}"
+		}
+
+		render view: view, model: [postUrl: postUrl]
+	}
+
+	/**
+	 * Form submit action to start an OpenID authentication.
+	 */
+	def openIdAuthenticate = {
+		String openID = params['j_username']
+		try {
+			String returnToURL = RedirectUtils.buildRedirectUrl(
+					request, response, openIDAuthenticationProcessingFilter.filterProcessesUrl)
+			String redirectUrl = openIDConsumer.beginConsumption(request, openID, returnToURL)
+			redirect url: redirectUrl
+		}
+		catch (org.springframework.security.ui.openid.OpenIDConsumerException e) {
+			log.error "Consumer error: $e.message", e
+			redirect url: openIDAuthenticationProcessingFilter.authenticationFailureUrl
+		}
+	}
+
+	// Login page (function|json) for Ajax access.
+	def authAjax = {
+		nocache(response)
+		//this is example:
+		render """
+		<script type='text/javascript'>
+		(function() {
+			loginForm();
+		})();
+		</script>
+		"""
+	}
+
+	/**
+	 * The Ajax success redirect url.
+	 */
+	def ajaxSuccess = {
+		nocache(response)
+		render '{success: true}'
+	}
+
+	/**
+	 * Show denied page.
+	 */
+	def denied = {
+		if (isLoggedIn() && authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) {
+			// have cookie but the page is guarded with IS_AUTHENTICATED_FULLY
+			redirect action: full, params: params
+		}
+	}
+
+	/**
+	 * Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page.
+	 */
+	def full = {
+		render view: 'auth', params: params,
+			model: [hasCookie: authenticationTrustResolver.isRememberMe(SCH.context?.authentication)]
+	}
+
+	// Denial page (data|view|json) for Ajax access.
+	def deniedAjax = {
+		//this is example:
+		render "{error: 'access denied'}"
+	}
+
+	/**
+	 * login failed
+	 */
+	def authfail = {
+
+		def username = session[AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
+		def msg = ''
+		def exception = session[AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY]
+		if (exception) {
+			if (exception instanceof DisabledException) {
+				msg = "[$username] is disabled."
+			}
+			else {
+				msg = "[$username] wrong username/password."
+			}
+		}
+
+		if (isAjax()) {
+			render "{error: '${msg}'}"
+		}
+		else {
+			flash.message = msg
+			redirect action: auth, params: params
+		}
+	}
+
+	/**
+	 * Check if logged in.
+	 */
+	private boolean isLoggedIn() {
+		return authenticateService.isLoggedIn()
+	}
+
+	private boolean isAjax() {
+		return authenticateService.isAjax(request)
+	}
+
+	/** cache controls */
+	private void nocache(response) {
+		response.setHeader('Cache-Control', 'no-cache') // HTTP 1.1
+		response.addDateHeader('Expires', 0)
+		response.setDateHeader('max-age', 0)
+		response.setIntHeader ('Expires', -1) //prevents caching at the proxy server
+		response.addHeader('cache-Control', 'private') //IE5.x only
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LogoutController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LogoutController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/controllers/LogoutController.groovy	(revision 58)
@@ -0,0 +1,13 @@
+/**
+ * Logout Controller (Example).
+ */
+class LogoutController {
+
+	/**
+	 * Index action. Redirects to the Spring security logout uri.
+	 */
+	def index = {
+		// TODO  put any pre-logout code here
+		redirect(uri: '/j_spring_security_logout')
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_CaptchaController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_CaptchaController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_CaptchaController.groovy	(revision 58)
@@ -0,0 +1,99 @@
+import java.awt.Color
+import java.awt.Font
+import java.awt.Graphics2D
+import java.awt.RenderingHints
+import java.awt.geom.Rectangle2D
+import java.awt.image.BufferedImage
+import javax.imageio.ImageIO
+
+class CaptchaController {
+
+	private static final String SOURCECHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+	def index = {
+		response.setContentType('image/png')
+		response.setHeader('Cache-control', 'no-cache')
+
+		// Generate and remember the Source Character string (6 characters)
+		int l = SOURCECHARS.length()
+		StringBuilder b = new StringBuilder()
+		6.times {
+		    int r = (int)(Math.random() * l)
+		    b.append(SOURCECHARS.charAt(r))
+		}
+
+		final int height = 200
+		final int width = 200
+		final int space = 8
+
+		System.setProperty('java.awt.headless', 'true')
+		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
+		Graphics2D g2d = bufferedImage.createGraphics()
+		Font font = new Font('Serif', Font.BOLD, 18)
+		g2d.setFont(font)
+		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
+		Rectangle2D fontRect = font.getStringBounds(b.toString(), g2d.getFontRenderContext())
+		// Now, create a graphic 'space' pixels wider and taller than the the font
+		bufferedImage = new BufferedImage((int)fontRect.getWidth() + space,
+				(int)fontRect.getHeight() + space,
+				BufferedImage.TYPE_INT_RGB)
+		g2d = bufferedImage.createGraphics()
+		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
+		g2d.setFont(font)
+
+		// Draw the background
+		g2d.setColor(Color.WHITE)
+		g2d.fillRect(0, 0, width, height)
+
+		// Draw the lines
+		g2d.setColor(Color.GRAY)
+		int x1
+		int y1
+		int x2
+		int y2
+		final int step = 10
+		x1 = 0
+		y1 = step
+		x2 = step
+		y2 = 0
+		while (x1 < width || x2 < width || y1 < height || y2 < height) {
+		    g2d.drawLine(x1, y1, x2, y2)
+		    if (y1 < height) {
+				x1 = 0
+				y1 += step
+		    }
+		    else if (x1 < width) {
+				y1 = height
+				x1 += step
+		    }
+		    else {
+				x1 = width
+				y1 = height
+		    }
+
+		    if (x2 < width) {
+				y2 = 0
+				x2 += step
+		    }
+		    else if (y2 < height) {
+				x2 = width
+				y2 += step
+		    }
+		    else {
+				y2 = height
+				x2 = width
+		    }
+		}
+
+		// Draw the String
+		g2d.setColor(Color.BLACK)
+
+		g2d.drawString(b.toString(), (int)(space/2), (int)(space/4) + (int)fontRect.getHeight())
+
+		OutputStream out = response.getOutputStream()
+		ImageIO.write(bufferedImage, 'PNG', out)
+		out.close()
+
+		session.setAttribute('captcha', b.toString())
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RegisterController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RegisterController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RegisterController.groovy	(revision 58)
@@ -0,0 +1,204 @@
+${personClassImport}
+${authorityClassImport}
+
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken as AuthToken
+import org.springframework.security.context.SecurityContextHolder as SCH
+
+/**
+ * Registration controller.
+ */
+class RegisterController {
+
+	def authenticateService
+	def daoAuthenticationProvider
+	def emailerService
+
+	static Map allowedMethods = [save: 'POST', update: 'POST']
+
+	/**
+	 * User Registration Top page.
+	 */
+	def index = {
+
+		// skip if already logged in
+		if (authenticateService.isLoggedIn()) {
+			redirect action: show
+			return
+		}
+
+		if (session.id) {
+			def person = new ${personClassName}()
+			person.properties = params
+			return [person: person]
+		}
+
+		redirect uri: '/'
+	}
+
+	/**
+	 * User Information page for current user.
+	 */
+	def show = {
+
+		// get user id from session's domain class.
+		def user = authenticateService.userDomain()
+		if (user) {
+			render view: 'show', model: [person: ${personClassName}.get(user.id)]
+		}
+		else {
+			redirect action: index
+		}
+	}
+
+	/**
+	 * Edit page for current user.
+	 */
+	def edit = {
+
+		def person
+		def user = authenticateService.userDomain()
+		if (user) {
+			person = ${personClassName}.get(user.id)
+		}
+
+		if (!person) {
+			flash.message = "[Illegal Access] User not found with id \${params.id}"
+			redirect action: index
+			return
+		}
+
+		[person: person]
+	}
+
+	/**
+	 * update action for current user's edit page
+	 */
+	def update = {
+
+		def person
+		def user = authenticateService.userDomain()
+		if (user) {
+			person = ${personClassName}.get(user.id)
+		}
+		else {
+			redirect action: index
+			return
+		}
+
+		if (!person) {
+			flash.message = "[Illegal Access] User not found with id \${params.id}"
+			redirect action: index, id: params.id
+			return
+		}
+
+		// if user want to change password. leave passwd field blank, passwd will not change.
+		if (params.passwd && params.passwd.length() > 0
+				&& params.repasswd && params.repasswd.length() > 0) {
+			if (params.passwd == params.repasswd) {
+				person.passwd = authenticateService.encodePassword(params.passwd)
+			}
+			else {
+				person.passwd = ''
+				flash.message = 'The passwords you entered do not match.'
+				render view: 'edit', model: [person: person]
+				return
+			}
+		}
+
+		person.userRealName = params.userRealName
+		person.email = params.email
+		if (params.emailShow) {
+			person.emailShow = true
+		}
+		else {
+			person.emailShow = false
+		}
+
+		if (person.save()) {
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'edit', model: [person: person]
+		}
+	 }
+
+	/**
+	 * Person save action.
+	 */
+	def save = {
+
+		// skip if already logged in
+		if (authenticateService.isLoggedIn()) {
+			redirect action: show
+			return
+		}
+
+		def person = new ${personClassName}()
+		person.properties = params
+
+		def config = authenticateService.securityConfig
+		def defaultRole = config.security.defaultRole
+
+		def role = ${authorityClassName}.findByAuthority(defaultRole)
+		if (!role) {
+			person.passwd = ''
+			flash.message = 'Default Role not found.'
+			render view: 'index', model: [person: person]
+			return
+		}
+
+		if (params.captcha.toUpperCase() != session.captcha) {
+			person.passwd = ''
+			flash.message = 'Access code did not match.'
+			render view: 'index', model: [person: person]
+			return
+		}
+
+		if (params.passwd != params.repasswd) {
+			person.passwd = ''
+			flash.message = 'The passwords you entered do not match.'
+			render view: 'index', model: [person: person]
+			return
+		}
+
+		def pass = authenticateService.encodePassword(params.passwd)
+		person.passwd = pass
+		person.enabled = true
+		person.emailShow = true
+		person.description = ''
+		if (person.save()) {
+			role.addToPeople(person)
+			if (config.security.useMail) {
+				String emailContent = """You have signed up for an account at:
+
+ \${request.scheme}://\${request.serverName}:\${request.serverPort}\${request.contextPath}
+
+ Here are the details of your account:
+ -------------------------------------
+ LoginName: \${person.username}
+ Email: \${person.email}
+ Full Name: \${person.userRealName}
+ Password: \${params.passwd}
+"""
+
+				def email = [
+					to: [person.email], // 'to' expects a List, NOT a single email address
+					subject: "[\${request.contextPath}] Account Signed Up",
+					text: emailContent // 'text' is the email body
+				]
+				emailerService.sendEmails([email])
+			}
+
+			person.save(flush: true)
+
+			def auth = new AuthToken(person.username, params.passwd)
+			def authtoken = daoAuthenticationProvider.authenticate(auth)
+			SCH.context.authentication = authtoken
+			redirect uri: '/'
+		}
+		else {
+			person.passwd = ''
+			render view: 'index', model: [person: person]
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RequestmapController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RequestmapController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RequestmapController.groovy	(revision 58)
@@ -0,0 +1,110 @@
+${requestmapClassImport}
+
+import org.springframework.util.StringUtils
+
+/**
+ * Requestmap controller.
+ */
+class ${requestmapClassName}Controller {
+
+	def authenticateService
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[requestmapList: ${requestmapClassName}.list(params)]
+	}
+
+	def show = {
+		def requestmap = ${requestmapClassName}.get(params.id)
+		if (!requestmap) {
+			flash.message = "${requestmapClassName} not found with id \$params.id"
+			redirect action:list
+			return
+		}
+		[requestmap: requestmap]
+	}
+
+	def delete = {
+		def requestmap = ${requestmapClassName}.get(params.id)
+		if (!requestmap) {
+			flash.message = "${requestmapClassName} not found with id \$params.id"
+			redirect action:list
+			return
+		}
+
+		requestmap.delete()
+
+		authenticateService.clearCachedRequestmaps()
+
+		flash.message = "${requestmapClassName} \$params.id deleted."
+		redirect(action: list)
+	}
+
+	def edit = {
+		def requestmap = ${requestmapClassName}.get(params.id)
+		if (!requestmap) {
+			flash.message = "${requestmapClassName} not found with id \$params.id"
+			redirect(action: list)
+			return
+		}
+
+		[requestmap: requestmap]
+	}
+
+	/**
+	 * Update action, called when an existing Requestmap is updated.
+	 */
+	def update = {
+
+		def requestmap = ${requestmapClassName}.get(params.id)
+		if (!requestmap) {
+			flash.message = "${requestmapClassName} not found with id \$params.id"
+			redirect(action: edit, id :params.id)
+			return
+		}
+
+		long version = params.version.toLong()
+		if (requestmap.version > version) {
+			requestmap.errors.rejectValue 'version', "requestmap.optimistic.locking.failure",
+				"Another user has updated this ${requestmapClassName} while you were editing."
+			render view: 'edit', model: [requestmap: requestmap]
+			return
+		}
+
+		requestmap.properties = params
+		if (requestmap.save()) {
+			authenticateService.clearCachedRequestmaps()
+			redirect action: show, id: requestmap.id
+		}
+		else {
+			render view: 'edit', model: [requestmap: requestmap]
+		}
+	}
+
+	def create = {
+		[requestmap: new ${requestmapClassName}(params)]
+	}
+
+	/**
+	 * Save action, called when a new Requestmap is created.
+	 */
+	def save = {
+		def requestmap = new ${requestmapClassName}(params)
+		if (requestmap.save()) {
+			authenticateService.clearCachedRequestmaps()
+			redirect action: show, id: requestmap.id
+		}
+		else {
+			render view: 'create', model: [requestmap: requestmap]
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RoleController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RoleController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_RoleController.groovy	(revision 58)
@@ -0,0 +1,123 @@
+${authorityClassImport}
+${requestmapClassImport}
+
+/**
+ * Authority Controller.
+ */
+class ${authorityClassName}Controller {
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def authenticateService
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	/**
+	 * Display the list authority page.
+	 */
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[authorityList: ${authorityClassName}.list(params)]
+	}
+
+	/**
+	 * Display the show authority page.
+	 */
+	def show = {
+		def authority = ${authorityClassName}.get(params.id)
+		if (!authority) {
+			flash.message = "${authorityClassName} not found with id \$params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Delete an authority.
+	 */
+	def delete = {
+		def authority = ${authorityClassName}.get(params.id)
+		if (!authority) {
+			flash.message = "${authorityClassName} not found with id \$params.id"
+			redirect action: list
+			return
+		}
+
+		authenticateService.deleteRole(authority)
+
+		flash.message = "${authorityClassName} \$params.id deleted."
+		redirect action: list
+	}
+
+	/**
+	 * Display the edit authority page.
+	 */
+	def edit = {
+		def authority = ${authorityClassName}.get(params.id)
+		if (!authority) {
+			flash.message = "${authorityClassName} not found with id \$params.id"
+			redirect action: list
+			return
+		}
+
+		[authority: authority]
+	}
+
+	/**
+	 * Authority update action.
+	 */
+	def update = {
+
+		def authority = ${authorityClassName}.get(params.id)
+		if (!authority) {
+			flash.message = "${authorityClassName} not found with id \$params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (authority.version > version) {
+			authority.errors.rejectValue 'version', 'authority.optimistic.locking.failure',
+				'Another user has updated this ${authorityClassName} while you were editing.'
+			render view: 'edit', model: [authority: authority]
+			return
+		}
+
+		if (authenticateService.updateRole(authority, params)) {
+			authenticateService.clearCachedRequestmaps()
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'edit', model: [authority: authority]
+		}
+	}
+
+	/**
+	 * Display the create new authority page.
+	 */
+	def create = {
+		[authority: new ${authorityClassName}()]
+	}
+
+	/**
+	 * Save a new authority.
+	 */
+	def save = {
+
+		def authority = new ${authorityClassName}()
+		authority.properties = params
+		if (authority.save()) {
+			redirect action: show, id: authority.id
+		}
+		else {
+			render view: 'create', model: [authority: authority]
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_UserController.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_UserController.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/controllers/_UserController.groovy	(revision 58)
@@ -0,0 +1,162 @@
+${personClassImport}
+${authorityClassImport}
+
+/**
+ * User controller.
+ */
+class ${personClassName}Controller {
+
+	def authenticateService
+
+	// the delete, save and update actions only accept POST requests
+	static Map allowedMethods = [delete: 'POST', save: 'POST', update: 'POST']
+
+	def index = {
+		redirect action: list, params: params
+	}
+
+	def list = {
+		if (!params.max) {
+			params.max = 10
+		}
+		[personList: ${personClassName}.list(params)]
+	}
+
+	def show = {
+		def person = ${personClassName}.get(params.id)
+		if (!person) {
+			flash.message = "${personClassName} not found with id \$params.id"
+			redirect action: list
+			return
+		}
+		List roleNames = []
+		for (role in person.authorities) {
+			roleNames << role.authority
+		}
+		roleNames.sort { n1, n2 ->
+			n1 <=> n2
+		}
+		[person: person, roleNames: roleNames]
+	}
+
+	/**
+	 * Person delete action. Before removing an existing person,
+	 * he should be removed from those authorities which he is involved.
+	 */
+	def delete = {
+
+		def person = ${personClassName}.get(params.id)
+		if (person) {
+			def authPrincipal = authenticateService.principal()
+			//avoid self-delete if the logged-in user is an admin
+			if (!(authPrincipal instanceof String) && authPrincipal.username == person.username) {
+				flash.message = "You can not delete yourself, please login as another admin and try again"
+			}
+			else {
+				//first, delete this person from People_Authorities table.
+				${authorityClassName}.findAll().each { it.removeFromPeople(person) }
+				person.delete()
+				flash.message = "${personClassName} \$params.id deleted."
+			}
+		}
+		else {
+			flash.message = "${personClassName} not found with id \$params.id"
+		}
+
+		redirect action: list
+	}
+
+	def edit = {
+
+		def person = ${personClassName}.get(params.id)
+		if (!person) {
+			flash.message = "${personClassName} not found with id \$params.id"
+			redirect action: list
+			return
+		}
+
+		return buildPersonModel(person)
+	}
+
+	/**
+	 * Person update action.
+	 */
+	def update = {
+
+		def person = ${personClassName}.get(params.id)
+		if (!person) {
+			flash.message = "${personClassName} not found with id \$params.id"
+			redirect action: edit, id: params.id
+			return
+		}
+
+		long version = params.version.toLong()
+		if (person.version > version) {
+			person.errors.rejectValue 'version', "person.optimistic.locking.failure",
+				"Another user has updated this ${personClassName} while you were editing."
+				render view: 'edit', model: buildPersonModel(person)
+			return
+		}
+
+		def oldPassword = person.passwd
+		person.properties = params
+		if (!params.passwd.equals(oldPassword)) {
+			person.passwd = authenticateService.encodePassword(params.passwd)
+		}
+		if (person.save()) {
+			${authorityClassName}.findAll().each { it.removeFromPeople(person) }
+			addRoles(person)
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'edit', model: buildPersonModel(person)
+		}
+	}
+
+	def create = {
+		[person: new ${personClassName}(params), authorityList: ${authorityClassName}.list()]
+	}
+
+	/**
+	 * Person save action.
+	 */
+	def save = {
+
+		def person = new ${personClassName}()
+		person.properties = params
+		person.passwd = authenticateService.encodePassword(params.passwd)
+		if (person.save()) {
+			addRoles(person)
+			redirect action: show, id: person.id
+		}
+		else {
+			render view: 'create', model: [authorityList: ${authorityClassName}.list(), person: person]
+		}
+	}
+
+	private void addRoles(person) {
+		for (String key in params.keySet()) {
+			if (key.contains('ROLE') && 'on' == params.get(key)) {
+				${authorityClassName}.findByAuthority(key).addToPeople(person)
+			}
+		}
+	}
+
+	private Map buildPersonModel(person) {
+
+		List roles = ${authorityClassName}.list()
+		roles.sort { r1, r2 ->
+			r1.authority <=> r2.authority
+		}
+		Set userRoleNames = []
+		for (role in person.authorities) {
+			userRoleNames << role.authority
+		}
+		LinkedHashMap<${authorityClassName}, Boolean> roleMap = [:]
+		for (role in roles) {
+			roleMap[(role)] = userRoleNames.contains(role.authority)
+		}
+
+		return [person: person, roleMap: roleMap]
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/services/_EmailerService.groovy
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/services/_EmailerService.groovy	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/services/_EmailerService.groovy	(revision 58)
@@ -0,0 +1,50 @@
+import javax.mail.MessagingException
+
+import org.springframework.mail.MailException
+import org.springframework.mail.SimpleMailMessage
+
+/**
+ * Simple service for sending emails.
+ *
+ * Work is planned in the Grails roadmap to implement first-class email
+ * support, so there's no point in making this code any more sophisticated.
+ *
+ * @author Haotian Sun
+ */
+class EmailerService {
+
+	boolean transactional = false
+
+	def mailSender
+	def mailMessage // a "prototype" email instance
+
+	/**
+	 * Send a list of emails.
+	 *
+	 * @param mails a list of maps
+	 */
+	def sendEmails(mails) {
+
+		// Build the mail messages
+		def messages = []
+		for (mail in mails) {
+			// create a copy of the default message
+			def message = new SimpleMailMessage(mailMessage)
+			message.to = mail.to
+			message.text = mail.text
+			message.subject = mail.subject
+			messages << message
+		}
+
+		// Send them all together
+		try {
+			mailSender.send(messages as SimpleMailMessage[])
+		}
+		catch (MailException e) {
+			log.error "Failed to send emails: $e.message", e
+		}
+		catch (MessagingException e) {
+			log.error "Failed to send emails: $e.message", e
+		}
+	}
+}
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/edit.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/edit.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/edit.gsp	(revision 58)
@@ -0,0 +1,83 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit Profile</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class='home' href="${createLinkTo(dir:'')}">Home</a></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit Profile</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${person}">
+		<div class="errors">
+			<g:renderErrors bean="${person}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<g:form>
+			<input type="hidden" name="id" value="${person.id}" />
+			<input type="hidden" name="version" value="${person.version}" />
+			<div class="dialog">
+			<table>
+				<tbody>
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='username'>Login Name:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'username','errors')}'>
+						<input type="hidden" name='username' value="${person.username?.encodeAsHTML()}"/>
+						<div style="margin:3px">${person.username?.encodeAsHTML()}</div>
+					</td>
+				</tr>
+
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='userRealName'>Full Name:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'userRealName','errors')}'>
+						<input type="text" name='userRealName' value="${person.userRealName?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='passwd'>Password:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'passwd','errors')}'>
+						<input type="password" name='passwd' value=""/>
+					</td>
+				</tr>
+
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='enabled'>Confirm Password:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'passwd','errors')}'>
+						<input type="password" name='repasswd' />
+					</td>
+				</tr>
+
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='email'>Email:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'email','errors')}'>
+						<input type="text" name='email' value="${person.email?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class='prop'>
+					<td valign='top' class='name'><label for='emailShow'>Show Email:</label></td>
+					<td valign='top' class='value ${hasErrors(bean:person,field:'emailShow','errors')}'>
+						<g:checkBox name='emailShow' value="${person.emailShow}" ></g:checkBox>
+					</td>
+				</tr>
+
+				</tbody>
+			</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class='save' value="Update" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/index.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/index.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/index.gsp	(revision 58)
@@ -0,0 +1,83 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>User Registration</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class='home' href="${createLinkTo(dir:'')}">Home</a></span>
+	</div>
+
+	<div class="body">
+		<h1>User Registration</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="${person}">
+		<div class="errors">
+			<g:renderErrors bean="${person}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<g:form action="save">
+		<div class="dialog">
+		<table>
+		<tbody>
+
+			<tr class='prop'>
+				<td valign='top' class='name'><label for='username'>Login Name:</label></td>
+				<td valign='top' class='value ${hasErrors(bean:person,field:'username','errors')}'>
+					<input type="text" name='username' value="${person?.username?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class='prop'>
+				<td valign='top' class='name'><label for='userRealName'>Full Name:</label></td>
+				<td valign='top' class='value ${hasErrors(bean:person,field:'userRealName','errors')}'>
+					<input type="text" name='userRealName' value="${person?.userRealName?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class='prop'>
+				<td valign='top' class='name'><label for='passwd'>Password:</label></td>
+				<td valign='top' class='value ${hasErrors(bean:person,field:'passwd','errors')}'>
+					<input type="password" name='passwd' value="${person?.passwd?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class='prop'>
+				<td valign='top' class='name'><label for='enabled'>Confirm Password:</label></td>
+				<td valign='top' class='value ${hasErrors(bean:person,field:'passwd','errors')}'>
+					<input type="password" name='repasswd' value="${person?.passwd?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class='prop'>
+				<td valign='top' class='name'><label for='email'>Email:</label></td>
+				<td valign='top' class='value ${hasErrors(bean:person,field:'email','errors')}'>
+					<input type="text" name='email' value="${person?.email?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class='prop'>
+				<td valign='bottom' class='name'><label for='code'>Enter Code: </label></td>
+				<td valign='top' class='name'>
+					<input type="text" name="captcha" size="8"/>
+					<img src="${createLink(controller:'captcha', action:'index')}" align="absmiddle"/>
+				</td>
+			</tr>
+
+		</tbody>
+		</table>
+		</div>
+
+		<div class="buttons">
+			<span class="formButton">
+				<input class='save' type="submit" value="Create"></input>
+			</span>
+		</div>
+
+		</g:form>
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/show.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/show.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/register/show.gsp	(revision 58)
@@ -0,0 +1,68 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>User Profile</title>
+</head>
+
+<body>
+	<div class="nav">
+		<span class="menuButton"><a class='home' href="${createLinkTo(dir:'')}">Home</a></span>
+	</div>
+
+	<div class="body">
+		<h1>User Profile</h1>
+		<g:if test="${flash.message}">
+		<div class="message">${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+		<table>
+		<tbody>
+
+			<tr class="prop">
+				<td valign="top" class="name">Login Name:</td>
+				<td valign="top" class="value">${person.username?.encodeAsHTML()}</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name">Full Name:</td>
+				<td valign="top" class="value">${person.userRealName?.encodeAsHTML()}</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name">Enabled:</td>
+				<td valign="top" class="value">${person.enabled}</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name">Email:</td>
+				<td valign="top" class="value">${person.email?.encodeAsHTML()}</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name">Show Email:</td>
+				<td valign="top" class="value">${person.emailShow}</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name">Roles:</td>
+				<td valign="top" class="value">
+					<ul>
+					<g:each var='authority' in="${person.authorities}">
+						<li>${authority.authority}</li>
+					</g:each>
+					</ul>
+				</td>
+			</tr>
+
+		</tbody>
+		</table>
+		</div>
+
+		<div class="buttons">
+		<g:form>
+			<input type="hidden" name="id" value="${person.id}" />
+			<span class="button"><g:actionSubmit class='edit' value="Edit" /></span>
+		</g:form>
+		</div>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/create.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/create.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/create.gsp	(revision 58)
@@ -0,0 +1,53 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Create ${requestmapClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${requestmapClassName} List</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Create ${requestmapClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${requestmap}">
+		<div class="errors">
+			<g:renderErrors bean="\${requestmap}" as="list" />
+		</div>
+		</g:hasErrors>
+		<g:form action="save">
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="url">URL Pattern:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:requestmap,field:'url','errors')}">
+							<input type="text" id="url" name="url" value="\${requestmap.url?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="configAttribute">${authorityClassName} (comma-delimited):</label></td>
+						<td valign="top" class="value \${hasErrors(bean:requestmap,field:'configAttribute','errors')}">
+							<input type="text" id="configAttribute" name="configAttribute" value="\${requestmap.configAttribute?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><input class="save" type="submit" value="Create" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/edit.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/edit.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/edit.gsp	(revision 58)
@@ -0,0 +1,63 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit ${requestmapClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${requestmapClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${requestmapClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit ${requestmapClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${requestmap}">
+		<div class="errors">
+		<g:renderErrors bean="\${requestmap}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<div class="prop">
+			<span class="name">ID:</span>
+			<span class="value">\${requestmap.id}</span>
+		</div>
+
+		<g:form>
+			<input type="hidden" name="id" value="\${requestmap.id}" />
+			<input type="hidden" name="version" value="\${requestmap.version}" />
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="url">URL Pattern:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:requestmap,field:'url','errors')}">
+							<input type="text" id="url" name="url" value="\${requestmap.url?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="configAttribute">Roles (comma-delimited):</label></td>
+						<td valign="top" class="value \${hasErrors(bean:requestmap,field:'configAttribute','errors')}">
+							<input type="text" name='configAttribute'  value="\${requestmap.configAttribute}"/>
+						</td>
+					</tr>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Update" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/list.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/list.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/list.gsp	(revision 58)
@@ -0,0 +1,50 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>${requestmapClassName} List</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${requestmapClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>${requestmapClassName} List</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="list">
+			<table>
+			<thead>
+				<tr>
+					<g:sortableColumn property="id" title="ID" />
+					<g:sortableColumn property="url" title="URL Pattern" />
+					<g:sortableColumn property="configAttribute" title="Roles" />
+					<th>&nbsp;</th>
+				</tr>
+			</thead>
+			<tbody>
+			<g:each in="\${requestmapList}" status="i" var="requestmap">
+				<tr class="\${(i % 2) == 0 ? 'odd' : 'even'}">
+					<td>\${requestmap.id}</td>
+					<td>\${requestmap.url?.encodeAsHTML()}</td>
+					<td>\${requestmap.configAttribute}</td>
+					<td class="actionButtons">
+						<span class="actionButton">
+						<g:link action="show" id="\${requestmap.id}">Show</g:link>
+						</span>
+					</td>
+				</tr>
+				</g:each>
+			</tbody>
+			</table>
+		</div>
+
+		<div class="paginateButtons">
+			<g:paginate total="\${${requestmapClass}.count()}" />
+		</div>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/show.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/show.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/requestmap/show.gsp	(revision 58)
@@ -0,0 +1,51 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Show ${requestmapClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${requestmapClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${requestmapClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Show ${requestmapClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+			<table>
+			<tbody>
+
+				<tr class="prop">
+					<td valign="top" class="name">ID:</td>
+					<td valign="top" class="value">\${requestmap.id}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">URL:</td>
+					<td valign="top" class="value">\${requestmap.url}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Roles:</td>
+					<td valign="top" class="value">\${requestmap.configAttribute}</td>
+				</tr>
+
+			</tbody>
+			</table>
+		</div>
+
+		<div class="buttons">
+			<g:form>
+				<input type="hidden" name="id" value="\${requestmap.id}" />
+				<span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</g:form>
+		</div>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/create.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/create.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/create.gsp	(revision 58)
@@ -0,0 +1,51 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Create ${authorityClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${authorityClassName} List</g:link></span>
+	</div>
+
+	<div class="body">
+
+		<h1>Create ${authorityClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${authority}">
+		<div class="errors">
+		<g:renderErrors bean="\${authority}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<g:form action="save">
+		<div class="dialog">
+		<table>
+		<tbody>
+			<tr class="prop">
+				<td valign="top" class="name"><label for="authority">${authorityClassName} Name:</label></td>
+				<td valign="top" class="value \${hasErrors(bean:authority,field:'authority','errors')}">
+					<input type="text" id="authority" name="authority" value="\${authority?.authority?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+
+			<tr class="prop">
+				<td valign="top" class="name"><label for="description">Description:</label></td>
+				<td valign="top" class="value \${hasErrors(bean:authority,field:'description','errors')}">
+					<input type="text" id="description" name="description" value="\${authority?.description?.encodeAsHTML()}"/>
+				</td>
+			</tr>
+		</tbody>
+		</table>
+		</div>
+
+		<div class="buttons">
+			<span class="button"><input class="save" type="submit" value="Create" /></span>
+		</div>
+		</g:form>
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/edit.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/edit.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/edit.gsp	(revision 58)
@@ -0,0 +1,69 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit ${authorityClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${authorityClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${authorityClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit ${authorityClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${authority}">
+		<div class="errors">
+			<g:renderErrors bean="\${authority}" as="list" />
+		</div>
+		</g:hasErrors>
+		<div class="prop">
+			<span class="name">ID:</span>
+			<span class="value">\${authority.id}</span>
+		</div>
+		<g:form>
+			<input type="hidden" name="id" value="\${authority.id}" />
+			<input type="hidden" name="version" value="\${authority.version}" />
+			<div class="dialog">
+			<table>
+			<tbody>
+				<tr class="prop">
+					<td valign="top" class="name"><label for="authority">${authorityClassName} Name:</label></td>
+					<td valign="top" class="value \${hasErrors(bean:authority,field:'authority','errors')}">
+						<input type="text" id="authority" name="authority" value="\${authority.authority?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name"><label for="description">Description:</label></td>
+					<td valign="top" class="value \${hasErrors(bean:authority,field:'description','errors')}">
+						<input type="text" id="description" name="description" value="\${authority.description?.encodeAsHTML()}"/>
+					</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name"><label for="persons">Persons:</label></td>
+					<td valign="top" class="value \${hasErrors(bean:authority,field:'persons','errors')}">
+						<ul>
+						<g:each var="p" in="\${authority.persons?}">
+							<li>\${p}</li>
+						</g:each>
+						</ul>
+					</td>
+				</tr>
+			</tbody>
+			</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Update" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</div>
+
+		</g:form>
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/list.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/list.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/list.gsp	(revision 58)
@@ -0,0 +1,49 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>${authorityClassName} List</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${authorityClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>${authorityClassName} List</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="list">
+			<table>
+			<thead>
+				<tr>
+					<g:sortableColumn property="id" title="ID" />
+					<g:sortableColumn property="authority" title="${authorityClassName} Name" />
+					<g:sortableColumn property="description" title="Description" />
+					<th>&nbsp;</th>
+				</tr>
+			</thead>
+			<tbody>
+			<g:each in="\${authorityList}" status="i" var="authority">
+				<tr class="\${(i % 2) == 0 ? 'odd' : 'even'}">
+					<td>\${authority.id}</td>
+					<td>\${authority.authority?.encodeAsHTML()}</td>
+					<td>\${authority.description?.encodeAsHTML()}</td>
+					<td class="actionButtons">
+						<span class="actionButton">
+							<g:link action="show" id="\${authority.id}">Show</g:link>
+						</span>
+					</td>
+				</tr>
+			</g:each>
+			</tbody>
+			</table>
+		</div>
+
+		<div class="paginateButtons">
+			<g:paginate total="\${${authorityClass}.count()}" />
+		</div>
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/show.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/show.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/role/show.gsp	(revision 58)
@@ -0,0 +1,57 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Show ${authorityClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${authorityClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${authorityClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Show ${authorityClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+			<table>
+			<tbody>
+
+				<tr class="prop">
+					<td valign="top" class="name">ID:</td>
+					<td valign="top" class="value">\${authority.id}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">${authorityClassName} Name:</td>
+					<td valign="top" class="value">\${authority.authority}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Description:</td>
+					<td valign="top" class="value">\${authority.description}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Persons:</td>
+					<td valign="top" class="value">\${authority.persons}</td>
+				</tr>
+
+			</tbody>
+			</table>
+		</div>
+
+		<div class="buttons">
+			<g:form>
+				<input type="hidden" name="id" value="\${authority?.id}" />
+				<span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</g:form>
+		</div>
+
+	</div>
+
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/create.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/create.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/create.gsp	(revision 58)
@@ -0,0 +1,99 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Create ${personClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${personClassName} List</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Create ${personClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${person}">
+		<div class="errors">
+			<g:renderErrors bean="\${person}" as="list" />
+		</div>
+		</g:hasErrors>
+		<g:form action="save">
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="username">Login Name:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'username','errors')}">
+							<input type="text" id="username" name="username" value="\${person.username?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="userRealName">Full Name:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'userRealName','errors')}">
+							<input type="text" id="userRealName" name="userRealName" value="\${person.userRealName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="passwd">Password:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'passwd','errors')}">
+							<input type="password" id="passwd" name="passwd" value="\${person.passwd?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="enabled">Enabled:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'enabled','errors')}">
+							<g:checkBox name="enabled" value="\${person.enabled}" ></g:checkBox>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="description">Description:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'description','errors')}">
+							<input type="text" id="description" name="description" value="\${person.description?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="email">Email:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'email','errors')}">
+							<input type="text" id="email" name="email" value="\${person.email?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="emailShow">Show Email:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'emailShow','errors')}">
+							<g:checkBox name="emailShow" value="\${person.emailShow}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name" align="left">Assign Roles:</td>
+					</tr>
+
+					<g:each in="\${authorityList}">
+					<tr>
+						<td valign="top" class="name" align="left">\${it.authority.encodeAsHTML()}</td>
+						<td align="left"><g:checkBox name="\${it.authority}"/></td>
+					</tr>
+					</g:each>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><input class="save" type="submit" value="Create" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/edit.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/edit.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/edit.gsp	(revision 58)
@@ -0,0 +1,111 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Edit ${personClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${personClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${personClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Edit ${personClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<g:hasErrors bean="\${person}">
+		<div class="errors">
+			<g:renderErrors bean="\${person}" as="list" />
+		</div>
+		</g:hasErrors>
+
+		<div class="prop">
+			<span class="name">ID:</span>
+			<span class="value">\${person.id}</span>
+		</div>
+
+		<g:form>
+			<input type="hidden" name="id" value="\${person.id}" />
+			<input type="hidden" name="version" value="\${person.version}" />
+			<div class="dialog">
+				<table>
+				<tbody>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="username">Login Name:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'username','errors')}">
+							<input type="text" id="username" name="username" value="\${person.username?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="userRealName">Full Name:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'userRealName','errors')}">
+							<input type="text" id="userRealName" name="userRealName" value="\${person.userRealName?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="passwd">Password:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'passwd','errors')}">
+							<input type="password" id="passwd" name="passwd" value="\${person.passwd?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="enabled">Enabled:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'enabled','errors')}">
+							<g:checkBox name="enabled" value="\${person.enabled}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="description">Description:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'description','errors')}">
+							<input type="text" id="description" name="description" value="\${person.description?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="email">Email:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'email','errors')}">
+							<input type="text" id="email" name="email" value="\${person?.email?.encodeAsHTML()}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="emailShow">Show Email:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'emailShow','errors')}">
+							<g:checkBox name="emailShow" value="\${person.emailShow}"/>
+						</td>
+					</tr>
+
+					<tr class="prop">
+						<td valign="top" class="name"><label for="authorities">Roles:</label></td>
+						<td valign="top" class="value \${hasErrors(bean:person,field:'authorities','errors')}">
+							<ul>
+							<g:each var="entry" in="\${roleMap}">
+								<li>\${entry.key.authority.encodeAsHTML()}
+									<g:checkBox name="\${entry.key.authority}" value="\${entry.value}"/>
+								</li>
+							</g:each>
+							</ul>
+						</td>
+					</tr>
+
+				</tbody>
+				</table>
+			</div>
+
+			<div class="buttons">
+				<span class="button"><g:actionSubmit class="save" value="Update" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</div>
+
+		</g:form>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/list.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/list.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/list.gsp	(revision 58)
@@ -0,0 +1,54 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>${personClassName} List</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${personClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>${personClassName} List</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="list">
+			<table>
+			<thead>
+				<tr>
+					<g:sortableColumn property="id" title="Id" />
+					<g:sortableColumn property="username" title="Login Name" />
+					<g:sortableColumn property="userRealName" title="Full Name" />
+					<g:sortableColumn property="enabled" title="Enabled" />
+					<g:sortableColumn property="description" title="Description" />
+					<th>&nbsp;</th>
+				</tr>
+			</thead>
+			<tbody>
+			<g:each in="\${personList}" status="i" var="person">
+				<tr class="\${(i % 2) == 0 ? 'odd' : 'even'}">
+					<td>\${person.id}</td>
+					<td>\${person.username?.encodeAsHTML()}</td>
+					<td>\${person.userRealName?.encodeAsHTML()}</td>
+					<td>\${person.enabled?.encodeAsHTML()}</td>
+					<td>\${person.description?.encodeAsHTML()}</td>
+					<td class="actionButtons">
+						<span class="actionButton">
+							<g:link action="show" id="\${person.id}">Show</g:link>
+						</span>
+					</td>
+				</tr>
+			</g:each>
+			</tbody>
+			</table>
+		</div>
+
+		<div class="paginateButtons">
+			<g:paginate total="\${${personClass}.count()}" />
+		</div>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/show.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/show.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/manager/views/user/show.gsp	(revision 58)
@@ -0,0 +1,82 @@
+<head>
+	<meta name="layout" content="main" />
+	<title>Show ${personClassName}</title>
+</head>
+
+<body>
+
+	<div class="nav">
+		<span class="menuButton"><a class="home" href="\${createLinkTo(dir:'')}">Home</a></span>
+		<span class="menuButton"><g:link class="list" action="list">${personClassName} List</g:link></span>
+		<span class="menuButton"><g:link class="create" action="create">New ${personClassName}</g:link></span>
+	</div>
+
+	<div class="body">
+		<h1>Show ${personClassName}</h1>
+		<g:if test="\${flash.message}">
+		<div class="message">\${flash.message}</div>
+		</g:if>
+		<div class="dialog">
+			<table>
+			<tbody>
+
+				<tr class="prop">
+					<td valign="top" class="name">ID:</td>
+					<td valign="top" class="value">\${person.id}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Login Name:</td>
+					<td valign="top" class="value">\${person.username?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Full Name:</td>
+					<td valign="top" class="value">\${person.userRealName?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Enabled:</td>
+					<td valign="top" class="value">\${person.enabled}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Description:</td>
+					<td valign="top" class="value">\${person.description?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Email:</td>
+					<td valign="top" class="value">\${person.email?.encodeAsHTML()}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Show Email:</td>
+					<td valign="top" class="value">\${person.emailShow}</td>
+				</tr>
+
+				<tr class="prop">
+					<td valign="top" class="name">Roles:</td>
+					<td valign="top" class="value">
+						<ul>
+						<g:each in="\${roleNames}" var='name'>
+							<li>\${name}</li>
+						</g:each>
+						</ul>
+					</td>
+				</tr>
+
+			</tbody>
+			</table>
+		</div>
+
+		<div class="buttons">
+			<g:form>
+				<input type="hidden" name="id" value="\${person.id}" />
+				<span class="button"><g:actionSubmit class="edit" value="Edit" /></span>
+				<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
+			</g:form>
+		</div>
+
+	</div>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/auth.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/auth.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/auth.gsp	(revision 58)
@@ -0,0 +1,79 @@
+<head>
+<meta name='layout' content='main' />
+<title>Login</title>
+<style type='text/css' media='screen'>
+#login {
+	margin:15px 0px; padding:0px;
+	text-align:center;
+}
+#login .inner {
+	width:260px;
+	margin:0px auto;
+	text-align:left;
+	padding:10px;
+	border-top:1px dashed #499ede;
+	border-bottom:1px dashed #499ede;
+	background-color:#EEF;
+}
+#login .inner .fheader {
+	padding:4px;margin:3px 0px 3px 0;color:#2e3741;font-size:14px;font-weight:bold;
+}
+#login .inner .cssform p {
+	clear: left;
+	margin: 0;
+	padding: 5px 0 8px 0;
+	padding-left: 105px;
+	border-top: 1px dashed gray;
+	margin-bottom: 10px;
+	height: 1%;
+}
+#login .inner .cssform input[type='text'] {
+	width: 120px;
+}
+#login .inner .cssform label {
+	font-weight: bold;
+	float: left;
+	margin-left: -105px;
+	width: 100px;
+}
+#login .inner .login_message {color:red;}
+#login .inner .text_ {width:120px;}
+#login .inner .chk {height:12px;}
+</style>
+</head>
+
+<body>
+	<div id='login'>
+		<div class='inner'>
+			<g:if test='${flash.message}'>
+			<div class='login_message'>${flash.message}</div>
+			</g:if>
+			<div class='fheader'>Please Login..</div>
+			<form action='${postUrl}' method='POST' id='loginForm' class='cssform'>
+				<p>
+					<label for='j_username'>Login ID</label>
+					<input type='text' class='text_' name='j_username' id='j_username' value='${request.remoteUser}' />
+				</p>
+				<p>
+					<label for='j_password'>Password</label>
+					<input type='password' class='text_' name='j_password' id='j_password' />
+				</p>
+				<p>
+					<label for='remember_me'>Remember me</label>
+					<input type='checkbox' class='chk' name='_spring_security_remember_me' id='remember_me'
+					<g:if test='${hasCookie}'>checked='checked'</g:if> />
+				</p>
+				<p>
+					<input type='submit' value='Login' />
+				</p>
+			</form>
+		</div>
+	</div>
+<script type='text/javascript'>
+<!--
+(function(){
+	document.forms['loginForm'].elements['j_username'].focus();
+})();
+// -->
+</script>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/denied.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/denied.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/denied.gsp	(revision 58)
@@ -0,0 +1,6 @@
+<meta name='layout' content='main' />
+<title>Denied</title>
+
+<div class='body'>
+	<div class='errors'>Sorry, you're not authorized to view this page.</div>
+</div>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/facebookAuth.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/facebookAuth.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/facebookAuth.gsp	(revision 58)
@@ -0,0 +1,60 @@
+<head>
+<meta name='layout' content='main' />
+<title>Login</title>
+<style type='text/css' media='screen'>
+#login {
+	margin:15px 0px; padding:0px;
+	text-align:center;
+}
+#login .inner {
+	width:260px;
+	margin:0px auto;
+	text-align:left;
+	padding:10px;
+	border-top:1px dashed #499ede;
+	border-bottom:1px dashed #499ede;
+	background-color:#EEF;
+}
+#login .inner .fheader {
+	padding:4px;margin:3px 0px 3px 0;color:#2e3741;font-size:14px;font-weight:bold;
+}
+#login .inner .cssform p{
+	clear: left;
+	margin: 0;
+	padding: 5px 0 8px 0;
+	padding-left: 105px;
+	border-top: 1px dashed gray;
+	margin-bottom: 10px;
+	height: 1%;
+}
+#login .inner .cssform input[type='text']{ 
+	width: 120px;
+}
+#login .inner .cssform label{
+	font-weight: bold;
+	float: left;
+	margin-left: -105px; 
+	width: 100px;
+}
+#login .inner .login_message {color:red;}
+</style>
+</head>
+
+<body>
+	<div id='login'>
+		<div class='inner'>
+			<g:if test='${flash.message}'>
+			<div class='login_message'>${flash.message}</div>
+			</g:if>
+			<div class='fheader'>Please Login..</div>
+			<form action='${postUrl}' method='POST' id='loginForm' class='cssform'>
+				<input type='submit' value='Login with your Facebook account' />
+			</form>
+		</div>
+	</div>
+<script type='text/javascript'>
+(function(){
+	document.forms['loginForm'].elements['j_username'].focus();
+})();
+</script>
+</body>
Index: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/openIdAuth.gsp
===================================================================
--- branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/openIdAuth.gsp	(revision 58)
+++ branches/TaskRewrite/src/plugins/acegi-0.5.1/src/templates/views/login/openIdAuth.gsp	(revision 58)
@@ -0,0 +1,67 @@
+<head>
+<meta name='layout' content='main' />
+<title>Login</title>
+<style type='text/css' media='screen'>
+#login {
+	margin:15px 0px; padding:0px;
+	text-align:center;
+}
+#login .inner {
+	width:260px;
+	margin:0px auto;
+	text-align:left;
+	padding:10px;
+	border-top:1px dashed #499ede;
+	border-bottom:1px dashed #499ede;
+	background-color:#EEF;
+}
+#login .inner .fheader {
+	padding:4px;margin:3px 0px 3px 0;color:#2e3741;font-size:14px;font-weight:bold;
+}
+#login .inner .cssform p{
+	clear: left;
+	margin: 0;
+	padding: 5px 0 8px 0;
+	padding-left: 105px;
+	border-top: 1px dashed gray;
+	margin-bottom: 10px;
+	height: 1%;
+}
+#login .inner .cssform input[type='text']{ 
+	width: 120px;
+}
+#login .inner .cssform label{
+	font-weight: bold;
+	float: left;
+	margin-left: -105px; 
+	width: 100px;
+}
+#login .inner .login_message {color:red;}
+#login .inner .text_ {width:120px;}
+</style>
+</head>
+
+<body>
+	<div id='login'>
+		<div class='inner'>
+			<g:if test='${flash.message}'>
+			<div class='login_message'>${flash.message}</div>
+			</g:if>
+			<div class='fheader'>Please Login..</div>
+			<form action='${postUrl}' method='POST' id='loginForm' class='cssform'>
+				<p>
+					<label for='j_username'>OpenID Identity</label>
+					<input type='text' class='text_' name='j_username' />
+				</p>
+				<p>
+					<input type='submit' value='Login' />
+				</p>
+			</form>
+		</div>
+	</div>
+<script type='text/javascript'>
+(function(){
+	document.forms['loginForm'].elements['j_username'].focus();
+})();
+</script>
+</body>
