MVC5, Entity Framework, Custom registration with encryption - c#

I'm new to .Net and I need your help. I'm using code first approach for my project and I'm trying to build custom register/login with encryption. By some reason I cannot save the user in my database when trying to encrypt the password. Saving the user in the database works when I remove the 2 lines for encryption and salting. Here is my User model:
public class User
{
[Key]
public int UserId { get; set; }
[Required(ErrorMessage = "First Name is required !")]
[Display(Name = "First Name: ")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is required !")]
[Display(Name = "Last Name: ")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email is required !")]
[EmailAddress]
[StringLength(50)]
[RegularExpression(#"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$", ErrorMessage = "Please enter valid email")]
[Display(Name = "Email Address: ")]
public string Email { get; set; }
[StringLength(50)]
[Display(Name = "Company Name: ")]
public string CompanyName { get; set; }
[Required(ErrorMessage = "Password is required !")]
[DataType(DataType.Password)]
[Display(Name = "Password: ")]
public string Password { get; set; }
[NotMapped]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords don't match !")]
public string ConfirmPassword { get; set; }
public string PasswordSalt { get; set; }
public int AdminCode { get; set; }
}
And here is my Register method in my controller:
[HttpPost]
public ActionResult Register(Models.User user)
{
if (ModelState.IsValid)
{
using (AppContext db = new AppContext())
{
var crypto = new SimpleCrypto.PBKDF2();
var encrypPass = crypto.Compute(user.Password);
//var newUser = db.Users.Create();
//newUser.FirstName = user.FirstName;
//newUser.LastName = user.LastName;
//newUser.Email = user.Email;
//newUser.CompanyName = user.CompanyName;
//newUser.Password = encrypPass;
//newUser.PasswordSalt = crypto.Salt;
//newUser.AdminCode = 0;
user.Password = encrypPass;
user.PasswordSalt = crypto.Salt;
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Data is not correct");
}
return View();
}
Here is my table.. When trying to encrypt I get Error "Validation failed for one or more entities.", but I have no idea what validation is failing. I've checked my DB table and the User model and I don't see the problem. I've really tried to figure it out by my own, but I can't! Any ideas what am I missing ? Thanks in advance.

Related

Regex attribute not throwing error when testing

I'm implementing some integration tests and there is an entity with certain attributes that must be respected. The entity is the following:
[Table("ManualClassifiers")]
public class ManualClassifier : Classifier
{
[RegularExpression(#"^([a-zA-Z]+?)([-\s'][a-zA-Z]+)*?")]
[Required(ErrorMessage = "First name field is mandatory")]
[Column(name: "first_name")]
public string FirstName { get; set; }
[RegularExpression(#"^([a-zA-Z]+?)([-\s'][a-zA-Z]+)*?")]
[Required(ErrorMessage = "Last name field is mandatory")]
[Column(name: "last_name")]
public string LastName { get; set; }
[EmailAddress]
[Required(ErrorMessage = "Email field is mandatory")]
[Column(name: "email")]
public string Email { get; set; }
[RegularExpression(#"^[a-zA-Z0-9._]+$")]
[Required(ErrorMessage = "Username field is mandatory")]
[Column(name: "username")]
public string Username { get; set; }
[Required(ErrorMessage = "Password hash field is mandatory")]
[Column(name: "passwordHash")]
public string PasswordHash { get; set; }
[Required(ErrorMessage = "Password salt field is mandatory")]
[Column(name: "passwordSalt")]
public string PasswordSalt { get; set; }
}
When trying to validate the first regular expression, my test is always passing (even when my input is a white space, and hence, should fail or throw some error). This is the test:
[TestMethod]
public async Task Insert_ManualClassifier_With_Empty_Field_Fails()
{
using var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<DataContext>()
.UseSqlite(connection) // Set the connection explicitly, so it won't be closed automatically by EF
.Options;
// Create the dabase schema
// MigrateAsync if using Migrations
using (var context = new DataContext(options))
{
await context.Database.EnsureCreatedAsync();
} // The connection is not closed, so the database still exists
using (var context = new DataContext(options))
{
var user = new ManualClassifier()
{
FirstName = " ", //this is where it should not match the regular expression and fail
LastName = "Last",
Email = "example#gmail.com",
Username = "firstlast123"
};
IRepositoryService repoService = new RepositoryService(context, NullLogger<RepositoryService>.Instance);
string passwordHash = "pwhash";
string passwordSalt = "pwsalt";
int userID = await repoService.CreateUser(user, passwordHash, passwordSalt);
}
}
This is the repository method I'm invoking:
public async Task<int> CreateUser(ManualClassifier user, string passwordHash, string passwordSalt)
{
//TO DO: CREATE UPPER LAYER IN REPO PROJECT TO MAP RESULTS
if (_context.ManualClassifiers.Any(u => u.Username == user.Username))
{
_logger.LogDebug($"User with email {user.Username} already exists");
throw new UsernameAlreadyExistsException(user.Username);
}
if (_context.ManualClassifiers.Any(u => u.Email == user.Email))
{
_logger.LogDebug($"User with email {user.Email} already exists");
throw new EmailAlreadyExistsException(user.Email);
}
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
user.Email = user.Email.ToLower();
_logger.LogDebug("New user created successfully");
await _context.ManualClassifiers.AddAsync(user);
await _context.SaveChangesAsync();
return user.Id;
}
Your regular expression validation for FirstName and LastName are missed end string anchor $. You should add the $ anchor to the regex validation for these 2 fields.
[Table("ManualClassifiers")]
public class ManualClassifier : Classifier
{
[RegularExpression(#"^([a-zA-Z]+?)([-\s'][a-zA-Z]+)*?$")]
[Required(ErrorMessage = "First name field is mandatory")]
[Column(name: "first_name")]
public string FirstName { get; set; }
[RegularExpression(#"^([a-zA-Z]+?)([-\s'][a-zA-Z]+)*?$")]
[Required(ErrorMessage = "Last name field is mandatory")]
[Column(name: "last_name")]
public string LastName { get; set; }
[EmailAddress]
[Required(ErrorMessage = "Email field is mandatory")]
[Column(name: "email")]
public string Email { get; set; }
[RegularExpression(#"^[a-zA-Z0-9._]+$")]
[Required(ErrorMessage = "Username field is mandatory")]
[Column(name: "username")]
public string Username { get; set; }
[Required(ErrorMessage = "Password hash field is mandatory")]
[Column(name: "passwordHash")]
public string PasswordHash { get; set; }
[Required(ErrorMessage = "Password salt field is mandatory")]
[Column(name: "passwordSalt")]
public string PasswordSalt { get; set; }
}

How do I send data to the pre-generated Asp.net database?

I am using Visual Studio 2015. I would like to add a first and last name to the already pre-generated Asp.net database. These columns are in the database. I have added on to the RegisterViewModel...
public class RegisterViewModel
{
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name ="Last Name")]
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
And here is my controller, which I have barely modified...
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
//string EncodedResponse = Request.Form["g-Recaptcha-Response"];
//bool IsCaptchaValid=(ReCaptcha.Validate(EncodedResponse)=="True" ? true: false);
//if (IsCaptchaValid)
//{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
user.FirstName = model.FirstName;
user.LastName = model.LastName;
etc... I'm not sure why FirstName and LastName are not getting sent to the database. I thought this was the way to do it, and I have seen other ways, but I am not sure. Once I understand this, and get it to work I should be fine. Thank you.
Change your controller to do this:
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName };
var result = await UserManager.CreateAsync(user, model.Password);

Save additional profile data during "Register" in ASP.Net Identity (MVC)

I need to store additional information during user registration such as: First Name, Last Name and etc.
My question has two parts:
How can I save my additional information during Register?
My current method throws error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
I have already checked "Watch" inside VS and also used try-catch but it didn't help.
Thanks in advance for your help!
IdentityModels.cs
public class ApplicationIdentityAccount : IdentityUser
{
public virtual ICollection<AccountProfile> AccountProfiles { get; set; }
}
public class AccountProfile
{
[Key]
public string AccountProfileID { get; set; }
[DisplayName("First Name")]
[StringLength(50)]
[Required(ErrorMessage = "First name is required")]
public string FirstName { get; set; }
[DisplayName("Middle Name")]
[StringLength(50)]
public string MiddleName { get; set; }
[DisplayName("Last Name")]
[StringLength(50)]
[Required(ErrorMessage = "Last name is required")]
public string LastName { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationIdentityAccount User { get; set; }
}
public class ApplicationIdentityDbContext : IdentityDbContext<ApplicationIdentityAccount>
{
public ApplicationIdentityDbContext()
: base("ApplicationIdentity", throwIfV1Schema: false)
{
}
public static ApplicationIdentityDbContext Create()
{
return new ApplicationIdentityDbContext();
}
public System.Data.Entity.DbSet<AccountProfile> AccountProfile { get; set; }
}
AccountViewModels.cs > RegisterViewModel
public class RegisterViewModel
{
[Required]
[Display(Name = "Username")]
public string UserName { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationIdentityAccount
{
UserName = model.UserName,
Email = model.Email,
AccountProfile = new[] {new AccountProfile()
{
FirstName = model.FirstName,
LastName = model.LastName
}}
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser: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");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I know that I should place FirstName and LastName inside:
var user = new ApplicationIdentityAccount
{
UserName = model.UserName,
Email = model.Email,
};
Since your question has two parts:
Your method for storing additional information is correct
You can't proceed unless you see the actual error, in order to see the details you need to add this where you create the user.
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}

Particular field getting NULL after form post

Hi I'm using Angulajs as front end and ASP.NET WebAPI as backend.
This is my Model class:
public class UserModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 7)]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "Phone")]
public string PhoneNumber { get; set; }
}
Controller:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
This is my Angular Service:
var _saveRegistration = function (registration) {
return $http.post('http://localhost:26264/api/account/register', registration).then(function (response) {
return response;
});
};
I'm sending payload
P.S This is made up payload
registration:{
userName:"UserName",
email: "eamil#emal.com",
password:"password",
confirmPassword:"password",
phone: "9898989898"
}
When I debug I see all the values in payload but when I set debugger at API Controller "register" method field "PhoneNumber" is null.
I don't know what wrong I'm doing, please help
Change phone to phoneNumber
registration:{
userName:"UserName",
email: "eamil#emal.com",
password:"password",
confirmPassword:"password",
phoneNumber: "9898989898"
}

MVC DropDownList item issue

I am quiet new with MVC. I am creating the user management module for authenticating and authorizing users withing that application. Hence, I created 2 models one for user roles and the other for user details.
I created two models like this:
public class RolesModels
{
public RolesModels()
{
this.Users = new HashSet<UserModels>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RoleId { get; set; }
[Required]
[DataType(DataType.Text)]
[StringLength(20, ErrorMessage = "The {0} must be at least 6 characters long.", MinimumLength = 6)]
[Display(Name = "Caption")]
public string Caption { get; set; }
[Display(Name = "Can Create")]
public bool createRole { get; set; }
[Display(Name = "Can View")]
public bool viewRole { get; set; }
[Display(Name = "Can Modify")]
public bool modifyRole { get; set; }
[Display(Name = "Can Delete")]
public bool deleteRole { get; set; }
public virtual ICollection<UserModels> Users { get; set; }
}
public class UserModels
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int user_id { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "User Name")]
[StringLength(20, ErrorMessage = "The {0} must be at least 3 characters long.", MinimumLength = 3)]
public string user_name { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
[StringLength(10, ErrorMessage = "The {0} must be at least 4 characters long.", MinimumLength = 4)]
public string user_pass { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name = "Email")]
[StringLength(50, ErrorMessage = "The {0} must be at least 6 characters long.", MinimumLength = 6)]
public string UserEmail { get; set; }
[Display(Name = "Registeration Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
public DateTime RegDate { get; set; }
[Display(Name = "Enable")]
public bool status { get; set; }
[Required]
public int RoleId { get; set; }
[Display(Name = "Roles")]
public virtual RolesModels Roles { get; set; }
}
and I have the registration controller action like this:
[HttpGet]
public ActionResult registration()
{
// var md = from m in db.RolesModels
// orderby m.Caption descending
// select m;
// Html.DropDownList("Roles", new SelectList(ViewBag.Roles), new { #id = "dropdown" });
ViewBag.Roles = db.RolesModels.Select(r => r.Caption);
ViewBag.RolesId = db.RolesModels.Select(r => r.RoleId);
return View();
}
I created a view for the registration with a dropdownlist like this:
#Html.DropDownList("RoleId", new SelectList(ViewBag.Roles), new { #id = "dropdown" })
My HTTPPost is here
[HttpPost]
public ActionResult registration(Solnet_HotelSuite.Models.UserModels user)
{
if (ModelState.IsValid)
{
using (var db = new DBEntity())
{
var sysUser = db.UserModels.Create();
sysUser.user_name = user.user_name;
sysUser.user_pass = user.user_pass;
sysUser.UserEmail = user.UserEmail;
sysUser.RegDate = user.RegDate;
sysUser.RoleId = user.RoleId;
//sysUser.Roles = user.Roles;
sysUser.status = user.status;
db.UserModels.Add(sysUser);
db.SaveChanges();
}
}
return View(user);
}
But whenever I clicked on Register user, I get the error :
Value cannot be null. Parameter name: items
What am I doing wrong?
The items you put in the viewbag are missing after the postback. Try changing the end of your HttpPost code to:
ViewBag.Roles = db.RolesModels.Select(r => r.Caption);
ViewBag.RolesId = db.RolesModels.Select(r => r.RoleId);
return View(user);
You may need to adjust this code as the db object may be out of scope at this point.

Categories