Cooltrooper
Cooltrooper

Reputation: 119

Adding Custom Fields to Spring Security Users

Having generated a User, Role, and UserRole classes using the s2-quickstart script of spring security I now want to add fields to the user class, such as email.

My User class contains:

//User.groovy
String username
String password
String email
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired

All seems to work until as I followed the tutorial in the documention I did the following

//BootStrap.goovy
def adminRole = new Role(authority:'ROLE_ADMIN').save(flush: true)
def adminUser = new User(username:'admin', password:'password')
adminUser.save(flush: true)
UserRole.create adminUser, adminRole, true

This yielded the output:

|Loading Grails 2.3.8
|Configuring classpath
.
|Environment set to development
.................................
|Packaging Grails application
...........
|Compiling 1 source files
...................................
|Running Grails application
Configuring Spring Security Core ...
... finished configuring Spring Security Core
| Error 2014-05-29 21:59:59,728 [localhost-startStop-1] ERROR context.GrailsContextLoader  - Error initializing the application: No signature of method: uk.co.xonos.bookings.UserRole.exists() is applicable for argument types: (null, java.lang.Long) values: [null, 1]
Possible solutions: exists(long, long), exists(java.io.Serializable), list(), first(), wait(), last()
Message: No signature of method: uk.co.xonos.bookings.UserRole.exists() is applicable for argument types: (null, java.lang.Long) values: [null, 1]
Possible solutions: exists(long, long), exists(java.io.Serializable), list(), first(), wait(), last()
   Line | Method
->>  91 | methodMissing                    in org.grails.datastore.gorm.GormStaticApi
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    86 | doCall                           in uk.co.xonos.bookings.UserRole$__clinit__closure9_closure14_closure15
|    85 | doCall . . . . . . . . . . . . . in uk.co.xonos.bookings.UserRole$__clinit__closure9_closure14
|    44 | create                           in uk.co.xonos.bookings.UserRole
|    39 | doCall . . . . . . . . . . . . . in BootStrap$_closure1
|   308 | evaluateEnvironmentSpecificBlock in grails.util.Environment
|   301 | executeForEnvironment . . . . .  in     ''
|   277 | executeForCurrentEnvironment     in     ''
|   303 | innerRun . . . . . . . . . . . . in java.util.concurrent.FutureTask$Sync
|   138 | run                              in java.util.concurrent.FutureTask
|   895 | runTask . . . . . . . . . . . .  in java.util.concurrent.ThreadPoolExecutor$Worker
|   918 | run                              in     ''
^   695 | run . . . . . . . . . . . . . .  in java.lang.Thread
Error |
Forked Grails VM exited with error
|Server running. Browse to http://localhost:8080/Bookings

Cheers

==Edit==

As requested by dmahapatro here is my UserRole Class, not edited from the generated model

package uk.co.xonos.bookings

import org.apache.commons.lang.builder.HashCodeBuilder

class UserRole implements Serializable {

    private static final long serialVersionUID = 1

    User user
    Role role

    boolean equals(other) {
        if (!(other instanceof UserRole)) {
            return false
        }

        other.user?.id == user?.id &&
        other.role?.id == role?.id
    }

    int hashCode() {
        def builder = new HashCodeBuilder()
        if (user) builder.append(user.id)
        if (role) builder.append(role.id)
        builder.toHashCode()
    }

    static UserRole get(long userId, long roleId) {
        UserRole.where {
            user == User.load(userId) &&
            role == Role.load(roleId)
        }.get()
    }

    static boolean exists(long userId, long roleId) {
        UserRole.where {
            user == User.load(userId) &&
            role == Role.load(roleId)
        }.count() > 0
    }

    static UserRole create(User user, Role role, boolean flush = false) {
        def instance = new UserRole(user: user, role: role)
        instance.save(flush: flush, insert: true)
        instance
    }

    static boolean remove(User u, Role r, boolean flush = false) {
        if (u == null || r == null) return false

        int rowCount = UserRole.where {
            user == User.load(u.id) &&
            role == Role.load(r.id)
        }.deleteAll()

        if (flush) { UserRole.withSession { it.flush() } }

        rowCount > 0
    }

    static void removeAll(User u, boolean flush = false) {
        if (u == null) return

        UserRole.where {
            user == User.load(u.id)
        }.deleteAll()

        if (flush) { UserRole.withSession { it.flush() } }
    }

    static void removeAll(Role r, boolean flush = false) {
        if (r == null) return

        UserRole.where {
            role == Role.load(r.id)
        }.deleteAll()

        if (flush) { UserRole.withSession { it.flush() } }
    }

    static constraints = {
        role validator: { Role r, UserRole ur ->
            if (ur.user == null) return
            boolean existing = false
            UserRole.withNewSession {
                existing = UserRole.exists(ur.user.id, r.id)
            }
            if (existing) {
                return 'userRole.exists'
            }
        }
    }

    static mapping = {
        id composite: ['role', 'user']
        version false
    }
}

==Edit 2==

By request of Sérgio Michels, having done his sugestions it prints the following:

grails.validation.ValidationErrors: 1 errors
Field error in object 'uk.co.xonos.bookings.User' on field 'email': rejected value [null]; codes [uk.co.xonos.bookings.User.email.nullable.error.uk.co.xonos.bookings.User.email,uk.co.xonos.bookings.User.email.nullable.error.email,uk.co.xonos.bookings.User.email.nullable.error.java.lang.String,uk.co.xonos.bookings.User.email.nullable.error,user.email.nullable.error.uk.co.xonos.bookings.User.email,user.email.nullable.error.email,user.email.nullable.error.java.lang.String,user.email.nullable.error,uk.co.xonos.bookings.User.email.nullable.uk.co.xonos.bookings.User.email,uk.co.xonos.bookings.User.email.nullable.email,uk.co.xonos.bookings.User.email.nullable.java.lang.String,uk.co.xonos.bookings.User.email.nullable,user.email.nullable.uk.co.xonos.bookings.User.email,user.email.nullable.email,user.email.nullable.java.lang.String,user.email.nullable,nullable.uk.co.xonos.bookings.User.email,nullable.email,nullable.java.lang.String,nullable]; arguments [email,class uk.co.xonos.bookings.User]; default message [Property [{0}] of class [{1}] cannot be null]

Upvotes: 0

Views: 1496

Answers (2)

thelastworm
thelastworm

Reputation: 597

That's probably because your domain class User or Role contains au property id. This id property is used in UserRole. This is not allow because Grails Spring Plugin used a parameter name id. In Grails spring plugin the type of this parameter is long.

Solution : Change the name of your property from id to uuid. or delete the property.

Exemple : def role1 = Role.findOrSaveWhere(authority: 'ROLE_ADMIN') def defaultUser = User.findOrSaveWhere(username: 'admin', password: 'password', ) if(!defaultUser.authorities.contains(role1)){ UserRole.create(defaultUser,role1,true) } hope that help.

Upvotes: 0

user800014
user800014

Reputation:

That's probably because your user creation failed, and you're not validating it. Try to change your BootStrap to:

def adminRole = new Role(authority:'ROLE_ADMIN')
assert adminRole.save(flush: true)
def adminUser = new User(username:'admin', password:'password')
assert adminUser.save(flush: true)

If it fails, change to print the errors:

adminUser.validate()
println adminUser.errors

My guess is that you didn't add the email in the validations block, making this mandatory.

Upvotes: 2

Related Questions