I try to set an user object property in my unit test. This property is correctly passed to a custom user validator and was verified to be set during debugging. However, the IdentityResult still shows a blank property error.
I have this custom user validator:
public class CustomUserValidator : UserValidator<AppUser>
{
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> userManager, AppUser user)
{
//Here, the user.UserName property is set from the unit test, but the result still has an error for a blank UserName property
IdentityResult result = await base.ValidateAsync(userManager, user);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
if (user.Email.ToLower().EndsWith("#myDomain.com"))
{
return await Task.FromResult(IdentityResult.Success);
}
else
{
errors.Add(new IdentityError
{
Code = "EmailDomainError",
Description = "Only myDomain.com email addresses are allowed"
});
}
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
And I'm trying to use Moq and xUnit to create a test for the domain check on the email:
[Fact]
public async void Validate_User_Email()
{
//Arrange
var userManager = GetMockUserManager();
var customVal = new CustomUserValidator();
var user = Mock.Of<AppUser>(m => m.UserName == "joe" && m.Email == "joe#example.com");
//Act
//try to validate the user
IdentityResult result = await customVal.ValidateAsync(userManager.Object, user);
//Assert
//demonstrate that there are errors present
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
//Here there are 2 errors, one for a blank UserName and one (expected) error for the email address
Assert.Equal(1, errors.Count);
}
Related
this my code for login :
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = UserManager.FindByNameAsync(model.UserName);
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
if (returnUrl != null)
return RedirectToLocal(returnUrl);
else if (await UserManager.IsInRoleAsync(user.Id, "Admin")) //<= Checking Role and redirecting accordingly.
return Redirect("~/Admin/Home/");
else
return Redirect("~/User/Home");
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
but it show me this error :
Argument 1: cannot convert from 'int' to 'string' GoKhoda
show error in this line :
else if (await UserManager.IsInRoleAsync(user.Id, "Admin"))
this line for find user and role of user for redirect to page .
how can i solve this ?
Edit
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
Edit(2)
When i using the .TosString() show me this error .
IMHO your error is more than just int to String conversion, it is related to FindByNameAsync method. The problem occurs when IsInRoleAsync method requested for UserId property but the property doesn't exist in ApplicationUser class (related to Dapper mapping issue).
According to MVC - InvalidOperationException: UserId not found, ensure FindByNameAsync to include Id property in ApplicationUser like this one (use your EF database context if you have it instead of query statement):
public async Task<ApplicationUser> FindByNameAsync(string userName)
{
ApplicationUser result = null;
using (var conn = await GetOpenDBConnection())
{
try
{
// note that UserId does not exist on ApplicationUser by default
// thus we need to adjust the query statement
var queryResults = await conn.QueryAsync<ApplicationUser>(
"SELECT UserId AS [Id], UserName, Email FROM dbo.Users WHERE UserName = #UserName;",
new { UserName = userName });
result = queryResults.FirstOrDefault();
}
catch (Exception ex)
{
// handle exception
}
}
return result;
}
Alternatively you may try FindByName method instead of FindByNameAsync there.
Regarding the await usage error, the exception explains that only one async operation allowed at a time in given context (see Multi-async in Entity Framework 6?), hence you need to move await outside if-condition or create a new context before executing second await:
var isInRole = await UserManager.IsInRoleAsync(user.Id.ToString(), "Admin");
// inside switch...case
if (returnUrl != null)
return RedirectToLocal(returnUrl);
else if (isInRole) //<= Checking Role and redirecting accordingly.
return Redirect("~/Admin/Home/");
else
return Redirect("~/User/Home");
or change to IsInRole if it still throwing exception:
if (returnUrl != null)
return RedirectToLocal(returnUrl);
else if (UserManager.IsInRole(user.Id.ToString(), "Admin")) //<= Checking Role and redirecting accordingly.
return Redirect("~/Admin/Home/");
else
return Redirect("~/User/Home");
This may or may not solve your entire issue, but may explain what should you do to handle async-related issues.
Related problems:
MVC InvalidOperationException adding role to user
Manually Map column names with class properties
Entity framework async issues context or query?
I am trying to create a user by calling register function within the application.
This function works fine when the function is called as a API.
When Called from Inside the application it throws an error
AccountController ac = new AccountController();
RegisterBindingModel rbm = new RegisterBindingModel();
rbm.Email = UserAccountBase.Email;
rbm.Password = "TestPassword";
rbm.ConfirmPassword = "TestPassword";
var userId = await ac.Register(rbm);
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
try
{
var result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
}
catch(Exception e) {
var message = e.Message;
}
return Ok(user.Id);
}
UserManager.CreateAsync Throws an error
Value cannot be null. Parameter name: request
Did you build this using ASP Identity 2.0 and EF Code First? If so, check to see if your initial DB configuration put a column called 'Discriminator' at the end of AspNetUsers. If this is the case, recreate an initial migration and remove that column that gets generated. Once removed, you can perform an update-database. Does this make any sense? I just had this happen to me.
In the identity model, you will have a Discriminator column. In your model, add migration and update the database.
I've been following the Add Profile Data to User Class part of this tutorial to add more fields to my registration page in MVC 5. So far it's working fine and I have no issues. The problem is now I'm not sure on how that gets displayed on the Manage page where the user can see his profile info like changing his password. For example, I want to add on a first and last name on that page.
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on#ap
Here is a screenshot of the page I'm talking about:
http://puu.sh/dcMmj/82ddd8fc97.PNG
My project is what Visual Studio creates for you with the added first and last name in the registration page. Added this in the following Identity Model like in the tutorial
public class ApplicationUser : IdentityUser
{
public string FirstName{ get; set; }
public string LastName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
I'm thinking I need to add something here in the ManageController.cs but I'm not sure.
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId()),
// I think it goes in here somewhere
};
return View(model);
}
You can replace your var model with this code:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var model = new IndexViewModel {
HasPassword = HasPassword(),
PhoneNumber = user.PhoneNumber,
TwoFactor = user.TwoFactorEnabled,
Logins = await UserManager.GetLoginsAsync(user),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(user),
FirstName = user.FirstName
}
Now you can use model.FirstName in your View.
I am adding a user to my aspnetusers database and that is working fine. Then I am also trying to link them to an existing role. That is when I get the error: "An asynchronous module or handler completed while an asynchronous operation was still pending."
Here is my method with the problem code:
private async void checkOldDB(string email, string password)
{
bool isValidUser = false;
ReportsMvc.App_Code.BLL.FUN.cFunUser user = ReportsMvc.App_Code.DAL.FUN.cFunUserDB.getUser(email);
if (user != null)
{
isValidUser = PasswordHash.PasswordHash.ValidatePassword(password, user.Password);
if (!isValidUser)
{
isValidUser = PasswordHash.PasswordHash.ValidateHashes(password, user.Password);
}
}
if (isValidUser)
{
var user2 = new ApplicationUser { UserName = email, Email = email };
var result = await UserManager.CreateAsync(user2, password);
if (result.Succeeded)
{
string role = user.Role;
if (string.IsNullOrEmpty(role))
{
role = "User";
}
UserManager.AddToRole(user2.Id, role);
await SignInManager.SignInAsync(user2, isPersistent: false, rememberBrowser: false);
}
}
}
The line starting with "await SignInManager" was working fine. Then when I added in that code to AddToRole, I started getting the above error. This identity/authentication stuff is all very new to me.
You should change async void to async Task and await it where you call it.
As a general rule, you should avoid async void; it should only be used for event handlers. I describe this more in an MSDN article.
i create my own database and add user identity table to this by change the connection string.
now my connection string is this:
when i create a new user it worked well.
but when i change the Register(RegisterViewModel model) in RegisterControler to add a user to a role like this code:
public async Task Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//add user to member role******************
if (!Roles.RoleExists("Member"))
Roles.CreateRole("Member");
Roles.AddUserToRole(model.Email, "Member");
//*******************************************
await SignInAsync(user, isPersistent: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
user registerd but dont add to member Role! and it seem there is another connection string for Roles! beacause whene run this code ASPNETDB.MDF created in App_Data!
Please help me to solve this problem
In order to create roles in asp.net identity, you need to use AspNetRoleManager same as you are currently using AspNetUserManager.
The AspNetUserManager may looks like below.
public class AspNetRoleManager : RoleManager<IdentityRole, string>
{
public AspNetRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static AspNetRoleManager Create(IdentityFactoryOptions<AspNetRoleManager> options, IOwinContext context)
{
return new AspNetRoleManager(new RoleStore<IdentityRole, string, IdentityUserRole>(context.Get<YourDataContext>()));
}
}
Then you need to register AspNetRoleManager in the owin startup. Same like the AspNetUserManager.
app.CreatePerOwinContext<AspNetRoleManager>(AspNetRoleManager.Create);
After that you can use it inside the controller to create roles.
var roleManager = HttpContext.GetOwinContext().Get();
// Check for existing roles
var roleManager = HttpContext.GetOwinContext().Get<AspNetRoleManager>();
var roleExists = await roleManager.RoleExistsAsync("Member");
if (!roleExists)
{
var role = new IdentityRole();
role.Name = "Member";
var result = roleManager.CreateAsync(role);
}
Then add new role to the user.
var user = await UserManager.FindByEmailAsync(model.Email);
var roleRsult = UserManager.AddToRole(user.Id, roleName);