Grails Spring Security Plugin - ONE-TO-ONE association with User domain - security

I'm using Spring Security Plugin to manage membership and authentication in my Grails app.
I'm trying to associate the User domain class with a Profile domain by a one-to-one association.
I added these line on User.groovy:
static hasOne = [userProfile:UserProfile]
static constraints = {
//...
userProfile unique:true
}
and to UserProfile.groovy:
User user
Alas, I had error when invoking UseRole.create(user,role).
There is some best practice on how to obtain the same functionality I'm looking for. In particular, I want to associate any user with one profile object to extend it.
I want then also add one-to-many relations with posts and other table...
Thanks
best regards
PS:
I obtain this error:
Configuring Spring Security UI ...
2011-03-08 12:18:51,179 [main] ERROR context.GrailsContextLoader - Error executing bootstraps: null
java.lang.NullPointerException
at $Proxy19.save(Unknown Source)
at com.dromedian.xxxxx.security.UserRole.create(UserRole.groovy:32)
at com.dromedian.xxxxx.security.UserRole$create.call(Unknown Source)
at BootStrap$_closure1.doCall(BootStrap.groovy:20)
at grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.java:251)
at grails.util.Environment.executeForEnvironment(Environment.java:244)
at grails.util.Environment.executeForCurrentEnvironment(Environment.java:220)
at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
at grails.web.container.EmbeddableServer$start.call(Unknown Source)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy:158)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy)
at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:280)
at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)
at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)
at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)
at _GrailsRun_groovy.runInline(_GrailsRun_groovy:116)
at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)
at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)
at RunApp$_run_closure1.doCall(RunApp.groovy:33)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Application context shutting down...
The configuration is:
User.groovy (domain class created by spring security plugin)
static hasOne = [userDetail:UserDetail]
static constraints = {
username blank: false, unique: true
password blank: false
userDetail unique:true
}
UserDetail.groovy
static hasOne = [user:User]
static belongsTo = User
BootStrap.groovy
//TODO temporary added - no for production or persistent db
def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
def userRole = new Role(authority: 'ROLE_USER').save(flush: true)
String password = springSecurityService.encodePassword('password')
def testUser = new User(username: 'me', enabled: true, password: password)
testUser.save(flush: true)
if(testUser != null){
UserRole.create testUser, adminRole, true
}
If I don't call
UserRole.create testUser, adminRole, true
there is no error. I tried to debug, but I can understand where is the error.

As previously explained, your test user is not saved, since a user profile is required. The save method on the user would have returned null, however, you do not check on that.
I usually put a method along those lines in:
def ensureSave(domainObject) {
if(!domainObject.save(flush:true)) {
throw new Exception("not saved successfully: $domainObject");
}
domainObject
}
And then refer to it as follows:
ensureSave(testUser)
or
testUser = ensureSave(new User(...));
HTH

I think you have to set the UserProfile in the constructor. Because you are not providing one, the save() fails, thus giving you the NullPointerException.
UserDetail profile = new UserDetail()
def testUser = new User(username: 'me', enabled: true, password: password, userdetail: profile)
assert testUser.save()
Asserting over the .save() calls has proven quite useful as often when you change some code in your domain class you're constructors won't work in the bootstrap file if you forget to change them. As grails deals fairly gracefully with that, you get weird errors instead getting the message where it would help you most. By placing the asserts it will halt directly where the problem is.

I just ran into a similar issue...and have deduced this:
Your linked class must explicitly be allowed to be null...that is:
static constraints = {
...
userDetail unique:true, nullable: true
...
}
If you don't do this, the constructor call for your class fails (as others have pointed out, and attempting to create a UserRole on null fails)

Related

Bug in ASP.NET Identity's UserManager

MVC5's UserManager seems to have a bug:
My Code:
public void InsertUser(KoreUser user, string password)
{
var appUser = new ApplicationUser
{
UserName = user.UserName,
Email = user.Email,
LockoutEnabled = user.IsLockedOut
};
userManager.Create(appUser, password);
}
THis code works perfectly fine in almost all cases.. the exception being when the UserName contains a space.. for example:
JohnDoe - fine
John Doe - not fine..
In the latter example, it silently fails; no exception thrown.. what's the problem here?
It isn't a bug. An exception isn't thrown because it's reasonable to expect an invalid username in the land of user authentication.
The default check done when creating a username uses regex pattern ^[A-Za-z0-9#_\.]+$ to validate the name before creating the user. A whitespace character doesn't pass this test.
Another note is that the Create method of Usermanager returns an object of type IdentityResult. Using that object you can check for success and see what errors are being returned in the event that a user is not created.

ServiceStack adding roles and permissions with custom AuthUserSession

I'm trying to add roles and permissions when a new user is registered. I'm running into the problem that adding to the session roles and permissions does not get persisted to the database.
I've written a Custom AuthUserSession and overridden OnAuthenticated.
The code below uses the AssignRolesService, and that seems like it would be exactly what I need except for one problem. When the authentication is handled by Facebook auth provider session.UserAuthName is null so I can't call the service.
To clarify, all code snippets below are contained within:
public override void OnAuthenticated(IServiceBase authService, IAuthSession session,
IOAuthTokens tokens, Dictionary<string, string> authInfo)
The suggested way of doing this (from what I found on SO/Google):
using (var assignRoles = authService.ResolveService<AssignRolesService>())
{
assignRoles.Post(new AssignRoles {
UserName = session.UserAuthName,
Roles = { RoleNames.Admin }
});
}
Also tried this but it did not work:
session.Roles.Add("user");
session.Permissions.Add("setup");
authService.SaveSession(session);
The only thing that I found that seems to work, but seems like a hack is:
UserAuth ua = db.GetById<UserAuth>(session.UserAuthId);
ua.UserName = user.Email;
ua.Roles.Add("user");
ua.Permissions.Add("setup");
db.Save(ua);
Hi I just figured it out!
if just by a a coincidence you're using LoadUserInfo that have a try / catch when you are trying to assign the values, hiding a null reference exception and doing the redirect without doing a re-throw
that got fixed just by creating a new List like this:
userSession.Roles = new List<string> {"your-role-here"};

Security model and existing database

I have created a database with code first and DbContext.
However this sit separately to the security model database on a new MVC 4 site.
My question is how do i combine my existing database with the security model or should they be kept separate for a valid reason
For example is this the best solution
http://blog.spontaneouspublicity.com/including-asp-net-simple-membership-tables-as-part-of-your-entity-framework-model
This would recreate the security model and roles when i first ran the application.
Or is there an alternative way of doing this.
I love the new MVC and Simplemembership Provider for this reason. You can very easily combine your models with the asp.net account models.
When you use the default internet template it creates a context called UsersContext. To do something simple like add additional fields to a UserProfile object to track in the database you need to do 3 simple things.
Add the properties to the model (in the account models if you use the default template)
In the register action on the account controller, add the new fields IE:
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
var db = new UsersContext();
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { FirstName = model.FirstName, LastName = model.LastName, Address = model.Address, Company = model.Company, Phone = model.Phone, Country = model.Country, City = model.City, State = model.State, Zip = model.Zip });
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Dashboard");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Note the new keyword where I took values from the model passed and just matched them up to the model. (model binding may or may not work here but I haven't tested that yet)
3) Change the register View and model to pass all the info needed
I almost cried when this worked flawlessly the first time with no strange errors.
Good luck

Spring security integration with open id in grails

I am working on Integrating spring security with openId for my grails Application using springsecurity core and springsecurity openid plugins. I have integrated it, and it works well but I need to access the email for the logged in person. How can I get that, all that I am able to access is a token which is used for identifying the person.
Thanks to Ian Roberts.
He gives me this reply,Which exactly solves my problem.
His reply was:
As it happens I implemented exactly this in one of my applications
yesterday :-) Unfortunately it's not an open-source app so I can't just
point you at my code but I can explain what I did.
The spring-security-openid plugin supports the "attribute exchange"
mechanism of OpenID, although the support is not documented much (if at
all). How well it works depends on the provider at the far end but this
at least worked for me using Google and Yahoo.
In order to request the email address from the provider you need to add
the following to Config.groovy:
grails.plugins.springsecurity.openid.registration.requiredAttributes.email
= "http://axschema.org/contact/email"
Now to wire that into your user registration process you need an email
field in your S2 user domain class, and you need to edit the generated
OpenIdController.groovy in a few places.
add an email property to the OpenIdRegisterCommand
in the createAccount action there's a line
"if(!createNewAccount(...))" which passes the username, password and
openid as parameters. Change this along with the method definition to
pass the whole command object instead of just these two fields.
in createNewAccount pass the email value forward from the command
object to the User domain object constructor.
And finally add an input field for email to your
grails-app/views/openId/createAccount.gsp.
You can do the same with other attributes such as full name.
grails.plugins.springsecurity.openid.registration.requiredAttributes.fullname
= "http://axschema.org/namePerson"
The important thing to wire it together is that the thing after the last
dot following requiredAttributes (fullname in this example) must match
the name of the property on the OpenIdRegisterCommand.
Regards
Charu Jain
I've never used the springsecurity openid plugin, but when using springsecurity core you can expose additional information about the current user by implmenting a custom UserDetails. In my app, I added this implementation, so that I can show the name property of logged-in users. You'll need to change this slightly, so that the email address is exposed instead
/**
* Custom implementation of UserDetails that exposes the user's name
* http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/11%20Custom%20UserDetailsService.html
*/
class CustomUserDetails extends GrailsUser {
// additional property
final String name
CustomUserDetails(String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id,
String displayName) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id)
this.name = displayName
}
}
You then need to create a custom implementation of UserDetailsService which returns instances of the class above
class UserDetailsService implements GrailsUserDetailsService {
/**
* Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
* we give a user with no granted roles this one which gets past that restriction but
* doesn't grant anything.
*/
static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]
UserDetails loadUserByUsername(String username, boolean loadRoles) {
return loadUserByUsername(username)
}
UserDetails loadUserByUsername(String username) {
User.withTransaction { status ->
User user = User.findByUsername(username)
if (!user) {
throw new UsernameNotFoundException('User not found', username)
}
def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}
return new CustomUserDetails(
user.username,
user.password,
user.enabled,
!user.accountExpired,
!user.passwordExpired,
!user.accountLocked,
authorities ?: NO_ROLES,
user.id,
user.name)
}
}
}
You need to register an instance of this class as a Spring bean named userDetailsService. I did this by adding the following to Resources.groovy
userDetailsService(UserDetailsService)

Where to check user email does not already exist?

I have an account object that creates a user like so;
public class Account
{
public ICollection<User> Users { get; set; }
public User CreateUser(string email)
{
User user = new User(email);
user.Account = this;
Users.Add(user);
}
}
In my service layer when creating a new user I call this method. However there is a rule that the users email MUST be unique to the account, so where does this go? To me it should go in the CreateUser method with an extra line that just checks that the email is unique to the account.
However if it were to do this then ALL the users for the account would need to be loaded in and that seems like a bit of an overhead to me. It would be better to query the database for the users email - but doing that in the method would require a repository in the account object wouldn't it? Maybe the answer then is when loading the account from the repository instead of doing;
var accountRepository.Get(12);
//instead do
var accountRepository.GetWithUserLoadedOnEmail(12, "someone#example.com");
Then the account object could still check the Users collection for the email and it would have been eagerly loaded in if found.
Does this work? What would you do?
I'm using NHibernate as an ORM.
First off, I do not think you should use exceptions to handle "normal" business logic like checking for duplicate email addresses. This is a well document anti-pattern and is best avoided. Keep the constraint on the DB and handle any duplicate exceptions because they cannot be avoid, but try to keep them to a minimum by checking. I would not recommend locking the table.
Secondly, you've put the DDD tag on this questions, so I'll answer it in a DDD way. It looks to me like you need a domain service or factory. Once you have moved this code in a domain service or factory, you can then inject a UserRepository into it and make a call to it to see if a user already exists with that email address.
Something like this:
public class CreateUserService
{
private readonly IUserRepository userRepository;
public CreateUserService(IUserRepository userRepository)
{
this.userRepository = userRepository;
}
public bool CreateUser(Account account, string emailAddress)
{
// Check if there is already a user with this email address
User userWithSameEmailAddress = userRepository.GetUserByEmailAddress(emailAddress);
if (userWithSameEmailAddress != null)
{
return false;
}
// Create the new user, depending on you aggregates this could be a factory method on Account
User newUser = new User(emailAddress);
account.AddUser(newUser);
return true;
}
}
This allows you to separate the responsiblities a little and use the domain service to coordinate things. Hope that helps!
If you have properly specified the constraints on the users table, the add should throw an exception telling you that there is already a duplicate value. You can either catch that exception in the CreateUser method and return null or some duplicate user status code, or let it flow out and catch it later.
You don't want to test if it exists in your code and then add, because there is a slight possibility that between the test and the add, someone will come along and add the same email with would cause the exception to be thrown anyway...
public User CreateUser(string email)
{
try
{
User user = new User(email);
user.Account = this;
user.Insert();
catch (SqlException e)
{
// It would be best to check for the exception code from your db...
return null;
}
}
Given that "the rule that the users email MUST be unique to the account", then the most important thing is to specify in the database schema that the email is unique, so that the database INSERT will fail if the email is duplicate.
You probably can't prevent two users adding the same email nearly-simultaneously, so the next thing is that the code should handle (gracefully) an INSERT failure cause by the above.
After you've implemented the above, seeing whether the email is unique before you do the insert is just optional.

Resources