Updated UserModel, UpdatePassword made
This commit is contained in:
parent
bb44d2bc56
commit
f1327571e6
@ -77,9 +77,6 @@ namespace API.Application.Users.Commands
|
||||
CreatedAt = DateTime.UtcNow.AddHours(2),
|
||||
UpdatedAt = DateTime.UtcNow.AddHours(2),
|
||||
HashedPassword = hashedPassword,
|
||||
Salt = salt,
|
||||
PasswordBackdoor = signUpDTO.Password,
|
||||
// Only for educational purposes, not in the final product!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,57 @@
|
||||
namespace API.Application.Users.Commands
|
||||
using API.Models;
|
||||
using API.Persistence.Repositories;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace API.Application.Users.Commands
|
||||
{
|
||||
public class UpdateUserPassword
|
||||
{
|
||||
private readonly IUserRepository _repository;
|
||||
|
||||
public UpdateUserPassword(IUserRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Handle(ChangePasswordDTO changePasswordDTO)
|
||||
{
|
||||
if (!IsPasswordSecure(changePasswordDTO.NewPassword))
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "New Password is not secure." });
|
||||
}
|
||||
|
||||
User currentUser = await _repository.QueryUserByIdAsync(changePasswordDTO.Id);
|
||||
if (currentUser == null || !BCrypt.Net.BCrypt.Verify(changePasswordDTO.OldPassword, currentUser.HashedPassword))
|
||||
{
|
||||
return new UnauthorizedObjectResult(new { message = "Old Password is incorrect" });
|
||||
}
|
||||
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(changePasswordDTO.NewPassword);
|
||||
|
||||
currentUser.HashedPassword = hashedPassword;
|
||||
|
||||
bool success = await _repository.UpdateUserPasswordAsync(currentUser);
|
||||
if (success)
|
||||
return new OkResult();
|
||||
else
|
||||
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
|
||||
private bool IsPasswordSecure(string password)
|
||||
{
|
||||
var hasUpperCase = new Regex(@"[A-Z]+");
|
||||
var hasLowerCase = new Regex(@"[a-z]+");
|
||||
var hasDigits = new Regex(@"[0-9]+");
|
||||
var hasSpecialChar = new Regex(@"[\W_]+");
|
||||
var hasMinimum8Chars = new Regex(@".{8,}");
|
||||
|
||||
return hasUpperCase.IsMatch(password)
|
||||
&& hasLowerCase.IsMatch(password)
|
||||
&& hasDigits.IsMatch(password)
|
||||
&& hasSpecialChar.IsMatch(password)
|
||||
&& hasMinimum8Chars.IsMatch(password);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using API.Models;
|
||||
using API.Persistence.Repositories;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
|
||||
|
||||
namespace API.Application.Users.Queries
|
||||
@ -13,10 +14,15 @@ namespace API.Application.Users.Queries
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<UserDTO> Handle(string id)
|
||||
public async Task<ActionResult<UserDTO>> Handle(string id)
|
||||
{
|
||||
User user = await _repository.QueryUserByIdAsync(id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "No user on given Id" });
|
||||
}
|
||||
|
||||
UserDTO userDTO = new UserDTO
|
||||
{
|
||||
Id = user.Id,
|
||||
|
@ -20,6 +20,7 @@ namespace API.Controllers
|
||||
private readonly QueryUserById _queryUserById;
|
||||
private readonly CreateUser _createUser;
|
||||
private readonly UpdateUser _updateUser;
|
||||
private readonly UpdateUserPassword _updateUserPassword;
|
||||
private readonly DeleteUser _deleteUser;
|
||||
private readonly LoginUser _loginUser;
|
||||
|
||||
@ -28,6 +29,7 @@ namespace API.Controllers
|
||||
QueryUserById queryUserById,
|
||||
CreateUser createUser,
|
||||
UpdateUser updateUser,
|
||||
UpdateUserPassword updateUserPassword,
|
||||
DeleteUser deleteUser,
|
||||
LoginUser loginUser)
|
||||
{
|
||||
@ -35,18 +37,17 @@ namespace API.Controllers
|
||||
_queryUserById = queryUserById;
|
||||
_createUser = createUser;
|
||||
_updateUser = updateUser;
|
||||
_updateUserPassword = updateUserPassword;
|
||||
_deleteUser = deleteUser;
|
||||
_loginUser = loginUser;
|
||||
}
|
||||
|
||||
// POST: api/Users/login
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login(LoginDTO login)
|
||||
{
|
||||
return await _loginUser.Handle(login);
|
||||
}
|
||||
|
||||
// GET: api/Users
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<UserDTO>>> GetUsers()
|
||||
@ -54,8 +55,7 @@ namespace API.Controllers
|
||||
return await _queryAllUsers.Handle();
|
||||
}
|
||||
|
||||
// GET: api/Users/5
|
||||
[Authorize]
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<UserDTO>> GetUser(string id)
|
||||
{
|
||||
@ -63,25 +63,26 @@ namespace API.Controllers
|
||||
|
||||
}
|
||||
|
||||
// PUT: api/Users/5
|
||||
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
|
||||
[Authorize]
|
||||
[HttpPut("{id}")]
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> PutUser(UserDTO userDTO)
|
||||
{
|
||||
return await _updateUser.Handle(userDTO);
|
||||
}
|
||||
|
||||
// POST: api/Users
|
||||
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
|
||||
[Authorize]
|
||||
[HttpPut("password")]
|
||||
public async Task<IActionResult> PutUserPassword(ChangePasswordDTO changePasswordDTO)
|
||||
{
|
||||
return await _updateUserPassword.Handle(changePasswordDTO);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Guid>> PostUser(SignUpDTO signUpDTO)
|
||||
{
|
||||
return await _createUser.Handle(signUpDTO);
|
||||
}
|
||||
|
||||
|
||||
// DELETE: api/Users/5
|
||||
[Authorize]
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteUser(string id)
|
||||
|
54
API/Migrations/20240816102314_removedSaltAndBackdoor.Designer.cs
generated
Normal file
54
API/Migrations/20240816102314_removedSaltAndBackdoor.Designer.cs
generated
Normal file
@ -0,0 +1,54 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using API;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDBContext))]
|
||||
[Migration("20240816102314_removedSaltAndBackdoor")]
|
||||
partial class removedSaltAndBackdoor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
|
||||
|
||||
modelBuilder.Entity("API.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HashedPassword")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
40
API/Migrations/20240816102314_removedSaltAndBackdoor.cs
Normal file
40
API/Migrations/20240816102314_removedSaltAndBackdoor.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class removedSaltAndBackdoor : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PasswordBackdoor",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Salt",
|
||||
table: "Users");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PasswordBackdoor",
|
||||
table: "Users",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Salt",
|
||||
table: "Users",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
@ -35,14 +35,6 @@ namespace API.Migrations
|
||||
b.Property<string>("Password")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordBackdoor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
@ -8,8 +8,6 @@ public class User : BaseModel
|
||||
public string? Username { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string HashedPassword { get; set; }
|
||||
public string PasswordBackdoor { get; set; }
|
||||
public string Salt { get; set; }
|
||||
}
|
||||
|
||||
public class UserDTO
|
||||
@ -30,5 +28,12 @@ public class SignUpDTO
|
||||
public string Email { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class ChangePasswordDTO
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string OldPassword { get; set; }
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
|
||||
|
@ -10,5 +10,6 @@ namespace API.Persistence.Repositories
|
||||
Task<User> QueryUserByIdAsync(string id);
|
||||
Task<User> QueryUserByEmailAsync(string email);
|
||||
Task<bool> UpdateUserAsync(User user);
|
||||
Task<bool> UpdateUserPasswordAsync(User user);
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ namespace API.Persistence.Repositories
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
return new User();
|
||||
}
|
||||
|
||||
@ -58,6 +57,21 @@ namespace API.Persistence.Repositories
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateUserPasswordAsync(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Entry(user).State = EntityState.Modified;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteUserAsync(string id)
|
||||
{
|
||||
var user = await _context.Users.FindAsync(id);
|
||||
|
@ -36,6 +36,7 @@ namespace API
|
||||
builder.Services.AddScoped<QueryUserById>();
|
||||
builder.Services.AddScoped<CreateUser>();
|
||||
builder.Services.AddScoped<UpdateUser>();
|
||||
builder.Services.AddScoped<UpdateUserPassword>();
|
||||
builder.Services.AddScoped<DeleteUser>();
|
||||
builder.Services.AddScoped<LoginUser>();
|
||||
builder.Services.AddScoped<IUserRepository, UserRepository>();
|
||||
|
Loading…
Reference in New Issue
Block a user