I'm on Grails 2.4.4, using Spring Security Core 2.0-RC4, Spring Security REST 1.5.0.M1 with functional Spock 0.7.
I'm using a functional test to test a controller for the REST API I'm building. When I set up the test, I create a User
, an admin Role
and then create a UserRole
. When I check to see that the User
has been persisted with the appropriate credentials INSIDE of the spock functional test, I see that everything is okay.
However, when authenticating, GormUserDetailsService
doesn't yield anything when calling User.findWhere(username:<appropriate username>)
. The specific method is from GormUserDetailsService
is here:
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
def conf = SpringSecurityUtils.securityConfig
String userClassName = conf.userLookup.userDomainClassName
def dc = grailsApplication.getDomainClass(userClassName)
if (!dc) {
throw new IllegalArgumentException("The specified user domain class '$userClassName' is not a domain class")
}
Class<?> User = dc.clazz
User.withTransaction { status ->
def user = User.findWhere((conf.userLookup.usernamePropertyName): username)
if (!user) {
log.warn "User not found: $username"
throw new NoStackUsernameNotFoundException()
}
Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
createUserDetails user, authorities
}
}
The problem is that the User.findWhere()
call doesn't find anything, even thought I've confirmed in my functional test that I've flushed the User instance to the database. I've tried printin all of the UserRole
s and User
s with findAll()
calls inside of the withTransaction
block of this method and they still return nothing.
Anyone have any thoughts? Should I be mocking something that I'm not? I'm using @TestFor
and @Mock
annotations on the functional test.
UPDATE
Here is a simple version of my functional test:
package com.help
import grails.plugins.rest.client.RestBuilder
import grails.plugins.rest.client.RestResponse
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import groovy.json.JsonBuilder
import org.codehaus.groovy.grails.web.json.JSONObject
import spock.lang.Shared
import spock.lang.Specification
@TestFor(PersonController)
@Mock([Person, Role, PersonRole])
class PersonControllerSpec extends Specification {
@Shared
RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
@Shared
String baseUrl = "http://localhost:8080"
@Shared
int iterationCount = 0
Collection<Person> persons = []
def setup() {
Role adminRole = Role.findByAuthority("ROLE_ADMIN") ?: new Role(authority:"ROLE_ADMIN").save(failOnError:true, flush:true)
Role userRole = Role.findByAuthority("ROLE_USER") ?: new Role(authority:"ROLE_USER").save(failOnError:true, flush:true)
Person admin = new Person(name:"reserved", keyword:"admin", password:"password", email:"email@email.com", phone:"2222222222", personalPhoneNumber:"1112223333").save(failOnError:true, flush:true)
Person user = new Person(name:"reserved", keyword:"user", password:"password", email:"email@email.com", phone:"2222222222", personalPhoneNumber:"1112223333").save(failOnError:true, flush:true)
PersonRole.create(admin, adminRole, true)
PersonRole.create(user, userRole, true)
for (i in 0..20) {
persons << new Person(name:"person-$iterationCount-$i", password:"password", keyword:"p-$iterationCount-$i", email:"email@email.com", phone:"1112223333", personalPhoneNumber:"1112223333")
}
persons*.save(failOnError:true, flush:true)
}
def cleanup() { iterationCount++ }
void "test listing persons"() {
when: "we have an authenticated request"
JsonBuilder jsonBuilder = new JsonBuilder()
JSONObject json = jsonBuilder keyword:"admin", password: "password"
RestResponse authResponse = rest.post("$baseUrl/api/login") {
contentType "application/json"
body(json)
}
then: "can access both restricted and public resources"
...
}
}
Here are the spring security settings in Config.groovy
:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.help.Person'
grails.plugin.springsecurity.userLookup.usernamePropertyName = 'keyword'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.help.PersonRole'
grails.plugin.springsecurity.authority.className = 'com.help.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
'/': ['permitAll'],
'/index': ['permitAll'],
'/index.gsp': ['permitAll'],
'/assets/**': ['permitAll'],
'/**/js/**': ['permitAll'],
'/**/css/**': ['permitAll'],
'/**/images/**': ['permitAll'],
'/**/favicon.ico': ['permitAll'],
'/dbconsole/**': ['ROLE_USER', 'ROLE_ADMIN']
]
grails.plugin.springsecurity.filterChain.chainMap = [
'/v1/public/**': 'anonymousAuthenticationFilter,restTokenValidationFilter,restExceptionTranslationFilter,filterInvocationInterceptor',
'/v1/**': 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // v1 rest api stateless
'/api/**': 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // api utility methods stateless
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'
]
grails {
plugin {
springsecurity {
rest {
login.useJsonCredentials = true
login.usernamePropertyName = "keyword"
token {
rendering.usernamePropertyName = "keyword"
rendering.authoritiesPropertyName = "roles"
rendering.tokenPropertyName = "access_token"
validation.useBearerToken = true
validation.enableAnonymousAccess = true
}
}
}
}
}