public async Task <ActionResult <UserDTO> > CreateUser([FromBody] UserLoginRequestDTO dto, CancellationToken ct) { if (dto == null) { return(BadRequest()); } if (!await _userSvc.CheckUsernameAvailableAsync(dto.Username, ct)) { var modelErrors = new ModelStateDictionary(); modelErrors.AddModelError("details", "That username is already in use."); return(Conflict(modelErrors)); } if (Validators.CheckForbiddenName(dto.Username)) { var modelErrors = new ModelStateDictionary(); modelErrors.AddModelError("details", "That username is forbidden or reserved for system use."); return(Conflict(modelErrors)); } var obj = _mapper.Map <User>(dto); obj.Id = Guid.NewGuid(); obj.Salt = PasswordUtils.GenerateSalt(); obj.Hash = PasswordUtils.GenerateHash(dto.Password, obj.Salt); obj = await _userSvc.AddUserAsync(obj, ct); return(CreatedAtAction(nameof(GetUser), new { id = obj.Id }, _mapper.Map <UserDTO>(obj))); }
/// <summary> /// Check if the provided password matches the current User object's password. /// </summary> /// <param name="password">Password string to test</param> /// <returns>True if hash matches, false otherwise</returns> public bool CheckPassword(string password) { if (Salt != null && Hash != null) { return(Hash.Equals(PasswordUtils.GenerateHash(password, Salt), StringComparison.InvariantCulture)); } else { return(false); } }
public async Task <ActionResult <UserDTO> > UpdateUser(Guid id, [FromBody] UserUpdateDTO dto, CancellationToken ct) { var obj = await _userSvc.GetUserAsync(id, ct : ct); if (obj == null) { return(NotFound()); } obj.Salt = PasswordUtils.GenerateSalt(); obj.Hash = PasswordUtils.GenerateHash(dto.Password, obj.Salt); obj = await _userSvc.UpdateUserAsync(obj, ct); return(Ok(_mapper.Map <UserDTO>(obj))); }
protected override void OnModelCreating(ModelBuilder model) { #region Model configuration // Set certain columns to be *case insensitive* model.Entity <User>() .Property(u => u.Username) .HasColumnType("TEXT COLLATE NOCASE"); model.Entity <SettingsEntry>() .Property(s => s.Key) .HasColumnType("TEXT COLLATE NOCASE"); // Set default values model.Entity <Slide>() .Property(s => s.Order) .ValueGeneratedNever() .HasDefaultValue(999); model.Entity <Slide>() .Property(s => s.DisplaySeconds) .ValueGeneratedNever() .HasDefaultValue(20); #endregion #region Settings seeding #endregion #region Users seeding // Generate an admin user with a default known password. var adminUserSalt = PasswordUtils.GenerateSalt(); model.Entity <User>().HasData( new User() { Id = Guid.NewGuid(), Username = "******", Salt = adminUserSalt, Hash = PasswordUtils.GenerateHash("letmeslaps", adminUserSalt) }); #endregion #region DateTimeOffset converter if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite") { // NB: Taken from https://blog.dangl.me/archive/handling-datetimeoffset-in-sqlite-with-entity-framework-core/ // SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations // here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations // To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset // use the DateTimeOffsetToBinaryConverter // Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754 // This only supports millisecond precision, but should be sufficient for most use cases. foreach (var entityType in model.Model.GetEntityTypes()) { var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset) || p.PropertyType == typeof(DateTimeOffset?)); foreach (var property in properties) { model.Entity(entityType.Name) .Property(property.Name) .HasConversion(new DateTimeOffsetToBinaryConverter()); } } } #endregion }