diff --git a/PyroFetes/DTO/User/Request/ConnectUserDto.cs b/PyroFetes/DTO/User/Request/ConnectUserDto.cs index 1dc3ec4..1d0894d 100644 --- a/PyroFetes/DTO/User/Request/ConnectUserDto.cs +++ b/PyroFetes/DTO/User/Request/ConnectUserDto.cs @@ -2,6 +2,6 @@ public class ConnectUserDto { - public string? Username { get; set; } + public string? Name { get; set; } public string? Password { get; set; } } \ No newline at end of file diff --git a/PyroFetes/DTO/User/Request/CreateUserDto.cs b/PyroFetes/DTO/User/Request/CreateUserDto.cs index 5c04e00..e83b82f 100644 --- a/PyroFetes/DTO/User/Request/CreateUserDto.cs +++ b/PyroFetes/DTO/User/Request/CreateUserDto.cs @@ -4,7 +4,6 @@ public class CreateUserDto { public string? Name { get; set; } public string? Password { get; set; } - public string? Salt { get; set; } public string? Fonction { get; set; } public string? Email { get; set; } } \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/ConnectUserEndpoint.cs b/PyroFetes/Endpoints/User/ConnectUserEndpoint.cs new file mode 100644 index 0000000..b84c932 --- /dev/null +++ b/PyroFetes/Endpoints/User/ConnectUserEndpoint.cs @@ -0,0 +1,48 @@ +using FastEndpoints; +using FastEndpoints.Security; +using Microsoft.EntityFrameworkCore; +using PyroFetes.DTO.User.Request; +using PyroFetes.DTO.User.Response; + +namespace PyroFetes.Endpoints.User; + +public class ConnectUserEndpoint(PyroFetesDbContext database) : Endpoint +{ + public override void Configure() + { + Post("/api/users/connect"); + AllowAnonymous(); + } + + public override async Task HandleAsync(ConnectUserDto req, CancellationToken ct) + { + var user = await database.Users.SingleOrDefaultAsync(x => x.Name == req.Name, ct); + + if (user == null) + { + await Send.UnauthorizedAsync(ct); + return; + } + + if (BCrypt.Net.BCrypt.Verify(req.Password + user.Salt, user.Password)) + { + var jwtToken = JwtBearer.CreateToken( + o => + { + o.SigningKey = "ThisIsASuperSecretJwtKeyThatIsAtLeast32CharsLong"; + o.ExpireAt = DateTime.UtcNow.AddMinutes(15); + if (user.Fonction != null) o.User.Roles.Add(user.Fonction); + o.User.Claims.Add(("Name", user.Name)!); + o.User.Claims.Add(("Id", user.Id.ToString())!); + }); + + GetTokenDto responseDto = new() + { + Token = jwtToken + }; + + await Send.OkAsync(responseDto, ct); + } + else await Send.UnauthorizedAsync(ct); + } +} \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/CreateUserEndpoint.cs b/PyroFetes/Endpoints/User/CreateUserEndpoint.cs new file mode 100644 index 0000000..466cb8b --- /dev/null +++ b/PyroFetes/Endpoints/User/CreateUserEndpoint.cs @@ -0,0 +1,45 @@ +using FastEndpoints; +using PasswordGenerator; +using PyroFetes.DTO.User.Request; +using PyroFetes.DTO.User.Response; + +namespace PyroFetes.Endpoints.User; + +public class CreateUserEndpoint(PyroFetesDbContext database) : Endpoint +{ + public override void Configure() + { + Post("/api/users"); + AllowAnonymous(); + } + + public override async Task HandleAsync(CreateUserDto req, CancellationToken ct) + { + string? salt = new Password().IncludeLowercase().IncludeUppercase().IncludeNumeric().LengthRequired(24).Next(); + + var user = new Models.User() + { + Name = req.Name, + Password = BCrypt.Net.BCrypt.HashPassword(req.Password + salt), + Salt = salt, + Email = req.Email, + Fonction = req.Fonction + }; + + database.Users.Add(user); + + await database.SaveChangesAsync(ct); + + GetUserDto responseDto = new() + { + Id = user.Id, + Name = user.Name, + Password = user.Password, + Salt = user.Salt, + Email = user.Email, + Fonction = user.Fonction + }; + + await Send.OkAsync(responseDto, ct); + } +} \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/DeleteUserEndpoint.cs b/PyroFetes/Endpoints/User/DeleteUserEndpoint.cs new file mode 100644 index 0000000..c13f7fe --- /dev/null +++ b/PyroFetes/Endpoints/User/DeleteUserEndpoint.cs @@ -0,0 +1,33 @@ +using FastEndpoints; +using Microsoft.EntityFrameworkCore; + +namespace PyroFetes.Endpoints.User; + +public class DeleteUserRequest +{ + public int Id { get; set; } +} + +public class DeleteUserEndpoint(PyroFetesDbContext database) : Endpoint +{ + public override void Configure() + { + Delete("/api/users/{@Id}", x => new {x.Id}); + } + + public override async Task HandleAsync(DeleteUserRequest req, CancellationToken ct) + { + var user = await database.Users.SingleOrDefaultAsync(x => x.Id == req.Id, ct); + + if (user == null) + { + await Send.NotFoundAsync(ct); + return; + } + + database.Users.Remove(user); + await database.SaveChangesAsync(ct); + + await Send.NoContentAsync(ct); + } +} \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/GetAllUsersEndpoint.cs b/PyroFetes/Endpoints/User/GetAllUsersEndpoint.cs new file mode 100644 index 0000000..5b027b5 --- /dev/null +++ b/PyroFetes/Endpoints/User/GetAllUsersEndpoint.cs @@ -0,0 +1,30 @@ +using FastEndpoints; +using Microsoft.EntityFrameworkCore; +using PyroFetes.DTO.User.Response; + +namespace PyroFetes.Endpoints.User; + +public class GetAllUsersEndpoint(PyroFetesDbContext database) : EndpointWithoutRequest> +{ + public override void Configure() + { + Get("/api/users"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + var users = await database.Users + .Select(users => new GetUserDto() + { + Id = users.Id, + Name = users.Name, + Password = users.Password, + Salt = users.Salt, + Email = users.Email, + Fonction = users.Fonction + }) + .ToListAsync(ct); + + await Send.OkAsync(users, ct); + } +} \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/GetUserEndpoint.cs b/PyroFetes/Endpoints/User/GetUserEndpoint.cs new file mode 100644 index 0000000..f373674 --- /dev/null +++ b/PyroFetes/Endpoints/User/GetUserEndpoint.cs @@ -0,0 +1,42 @@ +using FastEndpoints; +using Microsoft.EntityFrameworkCore; +using PyroFetes.DTO.User.Response; + +namespace PyroFetes.Endpoints.User; + +public class GetUserRequest +{ + public int Id { get; set; } +} + +public class GetUserEndpoint(PyroFetesDbContext database) : Endpoint +{ + public override void Configure() + { + Get("/api/users/{@Id}", x => new {x.Id}); + } + + public override async Task HandleAsync(GetUserRequest req, CancellationToken ct) + { + var user = await database.Users + .SingleOrDefaultAsync(x => x.Id == req.Id, ct); + + if (user == null) + { + await Send.NotFoundAsync(ct); + return; + } + + GetUserDto responseDto = new() + { + Id = user.Id, + Name = user.Name, + Password = user.Password, + Salt = user.Salt, + Email = user.Email, + Fonction = user.Fonction + }; + + await Send.OkAsync(responseDto, ct); + } +} \ No newline at end of file diff --git a/PyroFetes/Endpoints/User/UpdateUserEndpoint.cs b/PyroFetes/Endpoints/User/UpdateUserEndpoint.cs new file mode 100644 index 0000000..13a8935 --- /dev/null +++ b/PyroFetes/Endpoints/User/UpdateUserEndpoint.cs @@ -0,0 +1,54 @@ +using FastEndpoints; +using Microsoft.EntityFrameworkCore; +using PasswordGenerator; +using PyroFetes.DTO.User.Request; +using PyroFetes.DTO.User.Response; + +namespace PyroFetes.Endpoints.User; + +public class UpdateUserEndpoint(PyroFetesDbContext database) : Endpoint +{ + public override void Configure() + { + Put("/api/users/{@Id}", x => new {x.Id}); + } + + public override async Task HandleAsync(UpdateUserDto req, CancellationToken ct) + { + var user = await database.Users.SingleOrDefaultAsync(x => x.Id == req.Id, ct); + var ckeckName = await database.Users.SingleOrDefaultAsync(x => x.Name == req.Name, ct); + + if (user == null) + { + await Send.NotFoundAsync(ct); + return; + } + + if (ckeckName != null) + { + await Send.StringAsync("Ce nom d'utilisateur existe déjà.",409, cancellation: ct); + return; + } + + string? salt = new Password().IncludeLowercase().IncludeUppercase().IncludeNumeric().LengthRequired(24).Next(); + + user.Name = req.Name; + user.Password = BCrypt.Net.BCrypt.HashPassword(req.Password + salt); + user.Salt = salt; + user.Email = req.Email; + user.Fonction = req.Fonction; + await database.SaveChangesAsync(ct); + + GetUserDto responseDto = new() + { + Id = user.Id, + Name = user.Name, + Password = user.Password, + Salt = user.Salt, + Email = user.Email, + Fonction = user.Fonction + }; + + await Send.OkAsync(responseDto, ct); + } +} \ No newline at end of file diff --git a/PyroFetes/PyroFetes.csproj b/PyroFetes/PyroFetes.csproj index ed5ce50..ec6d069 100644 --- a/PyroFetes/PyroFetes.csproj +++ b/PyroFetes/PyroFetes.csproj @@ -18,6 +18,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive +