I generated Role, User and UserRole class using the Spring Security Core Plugin. I want to set the users role directly in the user-creation-process. I added a "Role" field in User but don't know how and where I should set the entry in UserRole.
Is there anything else to implement like reauthentication to update a users role afterwards?
You should delete link to Role from User and use next code, after creating User and Role:
UserRole.create(user,role,true)
Where user your created user, role your created role, and true is indicated that userRole should create with flush:true
Good luck!
Yes its works!!! thanks, this is my code in a Service:
public String updateUser(long userId, String username, String password, long roleId){
Object[] args = [messageSource.getMessage('spring.security.ui.login.username',null, null),username];
def user = User.get(userId);
def userTemp = User.findAllByUsername(username);
if(userTemp.isEmpty() || userTemp.get(0).id == userId){
def role = Role.get(roleId);
user.username = username;
user.roleId = roleId;
if (password != ''){
user.password = password;
}
user.save(flush:true);
UserRole.create(user,role,true);
return "<span class='successMessage'><strong>" + messageSource.getMessage("message.common.record.saved.successfully", args, null) + "</strong></span>";
} else {
return "<span class='warnMessage'><strong>" + messageSource.getMessage("message.common.register.exist", args,null) + "</strong></span>";
}
}
Related
void "test saveSupervisor action"() {
setup:
def testUser = new User()
controller.userAuthService = [currentUser: testUser]
expect:
//User supervisor= new User()
User supervisor= new User(username: "sp123456",password: "pwd")
supervisor.save()
println "spv************ : "+ supervisor
when:"When User NAME Given"
controller.params.username = "sp123456"
controller.saveSupervisor()
then:
response.redirectUrl.endsWith '/showsupervisor/id'
}`---controller code----`
#Secured("hasRole('ROLE_ADMIN') and (hasRole('ROLE_SUPERVISOR') )")
#Transactional
def saveSupervisor(User userInstance) {
if (userInstance == null) {
notFound()
return
}
if (userInstance.hasErrors()) {
respond userInstance.errors, view:'create'
return
}
userInstance.save flush:true
def spvRole = Role.findByAuthority('ROLE_SUPERVISOR')
UserRole.create userInstance, spvRole , true
}
But I think I am doingf some thing wrong
I have these user roles, ROLE_SUPERVISOR, ROLE_ADMIN
when a user with having both role_supervisor and role_admin logged in to application then only he is given access to action savSupervisor(). How can I capture the logged in user's roles in the spock test?? I want to test if a user logged in with the roles ROLE_SUPERVISOR "and" ROLE_ADMIN then ONLY he is given access to saveSupervisor() method. I am using spring security, Please help me.
I know that we can initialise the database when doing a Entity Framework migration using the Seed method, as below.
protected override void Seed(CraigSheppardSoftware.Models.ApplicationDbContext context)
{
context.Users.AddOrUpdate(p => p.UserName,
new ApplicationUser { FullName = "System Admin", UserName="CssOp", UserRole = UserType.SystemAdmin,
LandlinePhone=new ComplexDataTypes.PhoneNumber(), MobilePhone= new ComplexDataTypes.PhoneNumber()
} );
context.SaveChanges();
}
The problem is that the when I try to use that user to login with, I can't as I dont know what the password is.
There is no password field on the database. I notice that there is a PasswordHash field and suspect that this is an encrypted password. How do I create a password hash ?
Use UserManager to create / update the user.
var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(context));
var user = manager.Find("CssOp", "ThePassword");
if (user == null)
{
user = new ApplicationUser { UserName = "CssOp", Email = "email#mail.com" };
manager.Create(user, "ThePassword");
}
else
{
user.Email = "newemail#mail.com";
manager.Update(user);
}
I've been noticing that a lot of the tutorials I'm following use this:
def springSecurityService
and since I want to get records only by current logged in user I use:
def user = params.id ? User.findByUserId(params.id) : User.get(springSecurityService.principal.id)
and also in my Bootstrap I want to create a username and password, so for instance
def user = new User(
username: username,
password: springSecurityService.encodePassword("tops3kr17"),
enabled: true)
However I noticed that the password is not being created, and Spring Source Tools does not find the method .principal.id or .encodePassword (they stay underlined in STS) and wants to use SpringSecurityService with a capital S when hitting CTL+SPACE (and doesn't complete .principal.id or .encodePassword).
So i'm a little lost because it seems that the tutorials are out of date
So how can I do what I described with what the current supported methods are? Or am I missing something really simple? : )
class BootStrap {
def springSecurityService
def init = { servletContext ->
def demo = [
'jack' : [ fullName: 'Jack Demo Salesman'],
'jill' : [ fullName: 'Jill Demo Saleswoman']]
def now = new Date()
def random = new Random()
def userRole = SecRole.findByAuthority("ROLE_SALES") ?: new SecRole(authority: "ROLE_SALES").save()
def adminRole = SecRole.findByAuthority("ROLE_ADMIN") ?: new SecRole(authority: "ROLE_ADMIN").save()
def users = User.list() ?: []
if (!users) {
demo.each { username, password, userAttrs ->
def user = new User(
username: username,
password: springSecurityService.encodePassword('secret'),
enabled: true)
if (user.validate()) {
println "DEBUG: Creating user ${username}..."
println "DEBUG: and their password is ${password}"
user.save(flush:true)
SecUserSecRole.create user, userRole
users << user
}
else {
println("\n\n\nError in account bootstrap for ${username}!\n\n\n")
user.errors.each {err ->
println err
}
}
Using the injected instance of SpringSecurityService is the right approach.
def springSecurityService
def foo() {
springSecurityService.principal
springSecurityService.encodePassword('fdsfads')
....
}
If the IDE isn't recognizing it, there is an issue with your IDE.
I just added a registration functionality to my new grails project. For testing it, I registered by giving an email and a password. I am using bcrypt algorithm for hashing the password before saving it to the database.
However when I try to login with the same email and password that I gave while registering, login fails. I debugged the application and found out that the hash that is generated for the same password is different when I try to compare with the already hashed one from database and hence the login is failing (Registration.findByEmailAndPassword(params.email,hashPassd) in LoginController.groovy returns null).
Here's my domain class Registration.groovy:
class Registration {
transient springSecurityService
String fullName
String password
String email
static constraints = {
fullName(blank:false)
password(blank:false, password:true)
email(blank:false, email:true, unique:true)
}
def beforeInsert = {
encodePassword()
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
}
Here's my LoginController.groovy:
class LoginController {
/**
* Dependency injection for the springSecurityService.
*/
def springSecurityService
def index = {
if (springSecurityService.isLoggedIn()) {
render(view: "../homepage")
}
else {
render(view: "../index")
}
}
/**
* Show the login page.
*/
def handleLogin = {
if (springSecurityService.isLoggedIn()) {
render(view: "../homepage")
return
}
def hashPassd = springSecurityService.encodePassword(params.password)
// Find the username
def user = Registration.findByEmailAndPassword(params.email,hashPassd)
if (!user) {
flash.message = "User not found for email: ${params.email}"
render(view: "../index")
return
} else {
session.user = user
render(view: "../homepage")
}
}
}
Here's a snippet from my Config.groovy telling grails to use bcrypt algorithm to hash passwords and the number of rounds of keying:
grails.plugins.springsecurity.password.algorithm = 'bcrypt'
grails.plugins.springsecurity.password.bcrypt.logrounds = 16
Jan is correct - bcrypt by design doesn't generate the same hash for each input string. But there's a way to check that a hashed password is valid, and it's incorporated into the associated password encoder. So add a dependency injection for the passwordEncoder bean in your controller (def passwordEncoder) and change the lookup to
def handleLogin = {
if (springSecurityService.isLoggedIn()) {
render(view: "../homepage")
return
}
def user = Registration.findByEmail(params.email)
if (user && !passwordEncoder.isPasswordValid(user.password, params.password, null)) {
user = null
}
if (!user) {
flash.message = "User not found for email: ${params.email}"
render(view: "../index")
return
}
session.user = user
render(view: "../homepage")
}
Note that you don't encode the password for the isPasswordValid call - pass in the cleartext submitted password.
Also - completely unrelated - it's a bad idea to store the user in the session. The auth principal is readily available and stores the user id to make it easy to reload the user as needed (e.g. User.get(springSecurityService.principal.id). Storing disconnected potentially large Hibernate objects works great in dev mode when you're the only user of your server, but can be a significant waste of memory and forces you to work around the objects being disconnected (e.g. having to use merge, etc.).
A BCrypt hash includes salt and as a result this algorithm returns different hashes for the same input. Allow me to demonstrate it in Ruby.
> require 'bcrypt'
> p = BCrypt::Password.create "foobar"
=> "$2a$10$DopJPvHidYqWVKq.Sdcy5eTF82MvG1btPO.81NUtb/4XjiZa7ctQS"
> r = BCrypt::Password.create "foobar"
=> "$2a$10$FTHN0Dechb/IiQuyeEwxaOCSdBss1KcC5fBKDKsj85adOYTLOPQf6"
> p == "foobar"
=> true
> r == "foobar"
=> true
Consequently, BCrypt cannot be used for finding users in the way presented in your example. An alternative unambiguous field should be used instead, e.g. user's name or e-mail address.
Upon creating new users in my system, I am sending them a temporary password via email and setting an property of changePasswordNextLogin=true. When they come to log in for the first time, I would like to intercept the flow upon a successful login, check for this this value, and if it is true, redirect them to a change password action. Once the password change has been completed, ideally I would like to send them to their intended destination.
I have been pouring through the default settings and am not seeing - or more likely not interpreting properly - any way to make that happen. It seems that almost every time that I try to cobble some solution together in Grails, I find that someone has already made a much more elegant approach to do the same thing. Is there any functionality built in that would allow this?
If not, I would really appreciate any suggestions on the best approach to make it so.
There is some support for this directly with Spring Security and the grails plugin, but you also have to do some work yourself :)
The domain class that was created when you installed grails-spring-security plugin (and ran the S2Quickstart script) has a property on it named 'passwordExpired'. Set this to true when you create your new user domain instance.
Once that user logs in for the first time, the Spring Security core libs will throw an exception which you can catch in your login controller's authfail closure, re-directing them to the change password form (that you need to supply yourself).
Here's an example from one of my apps, a skeleton version of this closure should already be included in your login controller:
/**
* Callback after a failed login.
*/
def authfail = {
def msg = ''
def username =
session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof CredentialsExpiredException) {
msg = g.message(code: "springSecurity.errors.login.passwordExpired")
if (!springSecurityService.isAjax(request))
redirect (action:'changePassword') // <-- see below
}
// other failure checks omitted
}
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect controller: 'login', action:'auth', params: params
}
}
/**
* render the change pwd form
*/
def changePassword = {
[username: session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name]
}
From your 'changePasssword' view, submit the form back to another controller closure (I call mine 'updatePassword' that checks whatever constraints you want for passwords and either saves the updated password on the domain object or not..
def updatePassword = {
String username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name
if (!username) {
flash.message = 'Sorry, an error has occurred'
redirect controller: 'login', action:'auth'
return
}
String password = params.password
String newPassword = params.password_new
String newPassword2 = params.password_new_2
if (!password || !newPassword || !newPassword2 || newPassword != newPassword2) {
flash.message = 'Please enter your current password and a new password'
render view: 'changePassword', model: [username: username]
return
}
SecUser user = SecUser.findByUsername(username)
if (!passwordEncoder.isPasswordValid(user.password, password, null /*salt*/)) {
flash.message = 'Current password is incorrect'
render view: 'changePassword', model: [username: username]
return
}
if (passwordEncoder.isPasswordValid(user.password, newPassword, null /*salt*/)) {
flash.message = 'Please choose a different password from your current one'
render view: 'changePassword', model: [username: username]
return
}
if (!newPassword.matches(PASSWORD_REGEX)) {
flash.message = 'Password does not meet minimum requirements'
render view: 'changePassword', model: [username: username]
return
}
// success if we reach here!
user.password = springSecurityService.encodePassword(newPassword)
user.passwordExpired = false
user.save()
flash.message = 'Password changed successfully' + (springSecurityService.loggedIn ? '' : ', you can now login')
redirect uri: '/'
}
If you are using Spring Secuirty 3.0 and later, You can refer to the spring security plugin documentation 11.3 Account Locking and Forcing Password Change.
Remember that you should set
grails.plugin.springsecurity.apf.storeLastUsername=true in Config.groovy.