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.
Related
I am designing the model of the following business needs :
The application must be able to register Users
The steps of the User registration are :
The user enters an email address and confirm
A verification code is sent to the provided email address.
The user must enter the correct verification code to continue
Repeat steps 1-3 for a phone number with verification code by SMS (optional)
The user then enters some personal information and confirm => the account is created
After registration, the user can update his email address or mobile phone number, but must go through the same verification process (code sent which must be entered to confirm the modification)
I ended up with the following model :
Verifiable (interface)
User (entity)
EmailAddress (value type, is a Verifiable)
MobilePhoneNumber (value type, is a Verifiable)
RandomCode (value type)
VerificationCode (entity containing a Verifiable, a RandomCode and a generationDateTime)
VerificationEmail (aggregate containing a VerificationCode, an EmailAddress and a Locale)
VerificationSms (aggregate containing a VerificationCode, a MobilePhoneNumber and a Locale)
Then here come the questions !!
Is it correct to have the Verifiable interface in order to have a VerificationCode instead of having EmailVerificationCode and SmsVerificationCode ? (Although it's not really a part of the ubiquitous language)
As I must persist somewhere the tuple emailAddress/mobilePhoneNumber + randomCode + generationDateTime to be able to retrieve it for verification, is it ok to have a specific entity for this ?
When the user wants to update his email address I was expecting to do something like :
// In the application service
User u = userRepository.findByUid(uid);
u.updateEmailAddress(newEmailAddress, enteredCode);
userRepository.save(u);
// In the User class
public void updateEmailAddress(EmailAddress newEmailAddress, String code) {
// Here comes the direct repository access
VerificationCode v = verificationCodeRepository.findByVerifiable(newEmailAddress);
if (v != null && v.hasNotExpired() && v.equalsToCode(code)) {
this.emailAddress = newEmailAddress;
verificationCodeRepository.delete(v);
}
else {
throw new IncorrectVerificationCodeException();
}
}
but to prevent my entity accessing a repository I ended up with the following code :
// In the application service
User u = userRepository.findByUid(uid);
VerificationCode v = verificationCodeRepository.findByVerifiable(newEmailAddress);
if (v != null && v.hasNotExpired() && v.equalsToCode(code)) {
verificationCodeRepository.delete(v);
u.updateEmailAddress(newEmailAddress);
userRepository.save(u);
}
else {
throw new IncorrectVerificationCodeException();
}
// In the User class
public void updateEmailAddress(EmailAddress newEmailAddress) {
this.emailAddress = newEmailAddress;
}
But it looks like an anemic model and the business logic is now in the application layer...
I am really struggling to correctly design the model as this is my first DDD project, any advice, modelisation suggestion is welcomed...
There is nothing wrong passing a repository as an argument in your updateEmailAddress() method.
But there is a better alternative, a domain service:
Your domain service depends on the repository and encapsulates the logic bound to your verification. You then pass this service to the user entity which is in charge of calling the correct method.
Here is how it could looks like:
class EmailVerificationService {
VerificationCodeRepository repository;
boolean isCodeVerified(EmailAddress emailAddress, String code) {
// do your things with the repository
// return true or false
}
}
Then in the user class:
class User {
// ...
public void updateEmailAddress(EmailVerificationService service, EmailAddress emailAddress, String code) {
if (service.isCodeVerified(emailAddress, code)) {
this.emailAddress = emailAddress;
} else {
// throw business Exception ?
}
}
}
In your application service, you inject the domain service and wire everything, catching the eventual exception and returning an error message to the user.
This is a suggestion of modeling, if you want to take it into account. Hope it could help you. I would model it this way:
User (aggregate root entity)
id
emailAddress (not null and unique)
mobilePhoneNumber (optional)
personalInfo
enabled (a user is created disabled when the registration process starts, and it is enabled when the process ends successfully)
VerificationCode (aggregate root entity) ===> it is associated to a user
id
randomCode
expirationDate
userId
smsOption (boolean) ===> if sms option is true, this verification code will be sent in a SMS to the user (otherwise it will be sent by email to the user)
Static Factory meethods:
forSendingByEmail ==> creates an instance with smsOption false
forSendingBySMS ===> creates and instance with smsOption true
Domain Service: sendVerificationCodeToUser ( verificationCodeId ) ===> checks smsOption to send either an SMS or an email (to the mobilePhoneNumber/emailAddress of the associated userId)
DomainEvent: VerificationCodeWasCreated ===> it has the id of the verification code that has been created
Raised by the VerificationCode constructor
The listener will call the domain service: sendVerificationCodeToUser(verificationCodeWasCreated.verificationCodeId())
THE REGISTRATION PROCESS (application service methods):
(1) The user enters an email address and confirm
public void registerUser ( String email ):
checks that doesn't exists any enabled user with the given email
if exist a disable user with the email, delete it
creates and persist a new disabled user with the email
creates and persist a new verification code associated to the created user for sending by email
(2) A verification code is sent to the provided email address ===> it is done by the domain event listener
(3) The user must enter the correct verification code to continue ===> the user who was sent the email in step (1) has to enter the email again, and the code he received)
public boolean isARandomCodeCorrectForUserEmail ( String randomCode, String email ) {
User user = userRepository.findByEmail(email);
if (user==null) {
return false;
}
VerificationCode vcode = verificationCodeRepository.findByRandomCodeAndUserId(randomCode,user.id());
if ( vcode==null) {
return false;
}
return vcode.hasNotExpired();
}
(4) Repeat steps 1-3 for a phone number with verification code by SMS (optional)
(4.1) The user of step (3) enters mobile phone number (we know the user id):
public void generateCodeForSendingBySmsToUser ( String mobilePhoneNumber, String userId ):
update user of userId with the given mobilePhoneNumber
creates and persist a new verification code associated to the user for sending by SMS
(4.2) The event listener sends the SMS
(4.3) The user who was sent the SMS in step (4.2) has to enter the email of step (1) again, and the code he received by SMS ===> isARandomCodeCorrectForUserEmail(randomCode,email)
(5) The user then enters some personal information and confirm ===> the account is created ===> what I do is enabling the user, since the user is already created, and we know the userId from step (3) or (4.3)
public void confirmRegistration ( PersonalInfo personalInfo, String userId ):
update user of userId with the given personalInfo
enables de the user
THE EMAIL/MOBILEPHONENUMBER MODIFICATION PROCESS:
It is similar to the registration, but the email/mobilePhoneNumber entered at the beginning must belongs to an existing enabled user, and at the end an update of the user is performed, instead of enabling.
ENABLED/DISABLED USERS:
Having enabled and disabled users, makes you taking it into account in authentication and authorization methods. If you don't want to or you're not allowed to have enabled/disabled users, you would have to model another aggregate that it would be UserCandidate or something like that, just with id, email and mobilePhoneNumber. And at the end of the process, create the real user with those values.
I am working on a custom OIG password management requirement for a client.
I am facing issue while validating the password history in policy definition (eg: shouldn't match last 5 passwords used).
For some reason, PasswordMgmtService API's validatePasswordAgainstPolicy method is bypassing the history validation and returning true if user enters any old password.
Below is the code snippet for reference.
public ValidationResult validatePasswordRACFPolicy(String loggedinUserKey, char[] userPassword)
{
PasswordMgmtService pwdMgmtSvc = oimClient.getService(PasswordMgmtService.class);
User usr = new User(loggedinUserKey); //loggedinUserKey is user key of logged in user
ValidationResult valResult = pwdMgmtSvc.validatePasswordAgainstPolicy(userPassword, usr, <App Instance Name>, Locale.getDefault());
IDMLOGGER.log(ODLLevel.FINEST, "Is Password Valid = " + valResult.isPasswordValid()); //this value is true even if user tries to reset password using any older passwords.
return valResult;
}
Eventually, ending up with exception when I try to update the account password on target.
provSvc.changeAccountPassword(Long.valueOf(accountId), userPassword);
//provSvc is ProvisioningService API object, accountId is oiu_key, userPassword is the password entered by user.
Here are the exception details:
GenericProvisioningException An error occurred in oracle.iam.provisioning.handlers.ChangeAccountPasswordActionHandler/execute while changing the password for account with id 1234 and the casue of error is {2}.[[ at oracle.iam.provisioning.util.ProvisioningUtil.createEventFailedException(ProvisioningUtil.java:175) at oracle.iam.provisioning.handlers.ChangeAccountPasswordActionHandler.execute(ChangeAccountPasswordActionHandler.java:84 ... ... Class/Method: tcOrderItemInfo/validatePassword Error : Password Does Not Satisfy Policy
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)
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)
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.