0

I'm using Grails Spring Security Core and the Grails Spring Security REST plugin and I'm just starting to get things set up. I initialized the plugins with a User class and an Authority class (defaults) and went to write an integration test, following a guide I found on the Grails website.

It said to put the following in an integration test:

def "test a user with the role ROLE_BOSS is able to access /api/announcements url"() {
    when: 'login with the sherlock'
    RestBuilder rest = new RestBuilder()
    def resp = rest.post("http://localhost:${serverPort}/api/login") { 
        accept('application/json')
        contentType('application/json')
        json {
            username = 'sherlock'
            password = 'elementary'
        }
    }

    then:
    resp.status == 200
    resp.json.roles.find { it == 'ROLE_BOSS' }
}

I went ahead and did something similar and it worked with a bootstrapped User, but when I tried to do the exact same test with a User created in the test method itself, it would fail with a 401 HTTP response code.

The code I'm trying to run:

void "check get access token"() {
    given:
    RestBuilder rest = new RestBuilder()
    new User(username: "securitySpecTestUserName", password: "securitySpecTestPassword").save(flush: true)
    assert User.count == 2

    when:
    def resp = rest.post("http://localhost:${serverPort}/api/login") {
        accept('application/json')
        contentType('application/json')
        json {
            username = "securitySpecTestUserName"
            password = "securitySpecTestPassword"
        }
    }

    then:
    resp.status == 200
}

Note that the User.count == 2 assertion passes because there is one User in Bootstrap.groovy and the one create in the test method.

Why does this work and pass with the bootstrapped User without any issues at all but not the one created in the method? Is there a way I can write this integration test so that I can test the /api/login endpoint included in the grails-spring-security-rest plugin in this way?

Joshua S.
  • 133
  • 9
  • could you check if the user is enabled, accountLocked, accountExpired is set to true? – dynamo Jul 29 '17 at 08:41
  • @dynamo unfortunately `enabled = true`, `accountExpired = false`, `accountLocked = false`, `passwordExpired = false`, just like the bootstrapped user. – Joshua S. Jul 29 '17 at 14:18

1 Answers1

0

The User you create in the given section is in a transaction that has not been committed. When you make the REST call, the api/login controller will be run in a new transaction that cannot see your un-committed User.

A few options (there are others)...

  1. Create User in BootStrap.groovy

    def init = { servletContext ->
      environments {
        test {
          new User(username: "securitySpecTestUserName", password: "securitySpecTestPassword").save(flush: true)
        }
      }
    }
    
  2. Make REST calls to create the User - assuming you have such functionality

  3. Create User in setup

    @Integration
    @Rollback
    class UserIntSpec extends Specification {
    
      def setup() {
        new User(username: "securitySpecTestUserName", password: "securitySpecTestPassword").save(flush: true)
      }
    
      void "check get access token"() {
        given:
        RestBuilder rest = new RestBuilder()
    
        when:
        def response = rest.post("http://localhost:${serverPort}/api/login") {
          accept('application/json')
          contentType('application/json')
          json {
            username = "securitySpecTestUserName"
            password = "securitySpecTestPassword"
          }
        }
    
       then:
       response.status == HttpServletResponse.SC_OK
    
       when:
       def token = response.json.access_token
    
       then:
       token
      }
    }
    

Note: In Grails >= 3.0, setup() is run in a separate transaction and persisted (why it solves your problem) which is not rolled back. Any data will need to be cleaned up manually.

I suggest you read the grails documentation on testing: Integration Testing

  • Thank you! I had read the documentation thoroughly but I didn't realize that the transaction not being committed means that the user will not be able to be authenticated by Spring Security. I thought that it would be setup for the user upon `.save()` even if it's not persisted to the database. I've now moved the code to the `setup()` method and I can now perform the test. – Joshua S. Jul 30 '17 at 05:17