2024-10-03 20:14:04 +00:00
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
using System.Security.Claims;
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2024-10-08 11:31:54 +00:00
|
|
|
using Simple_API.Database;
|
2024-10-03 20:14:04 +00:00
|
|
|
|
|
|
|
namespace Simple_API.Controllers
|
|
|
|
{
|
|
|
|
|
2024-10-03 21:16:50 +00:00
|
|
|
[Route("Auth/")]
|
2024-10-03 20:14:04 +00:00
|
|
|
[ApiController]
|
2024-10-08 11:31:54 +00:00
|
|
|
public class Default(IConfiguration configuration, UserService userService) : ControllerBase
|
2024-10-03 20:14:04 +00:00
|
|
|
{
|
2024-10-03 21:16:50 +00:00
|
|
|
public static class UserRoles
|
|
|
|
{
|
|
|
|
public const string User = "User";
|
|
|
|
public const string Admin = "Admin";
|
|
|
|
}
|
|
|
|
|
2024-10-08 11:31:54 +00:00
|
|
|
public class LoginAuthPayload
|
2024-10-03 20:14:04 +00:00
|
|
|
{
|
|
|
|
[DataType(DataType.EmailAddress)]
|
2024-10-08 11:31:54 +00:00
|
|
|
[StringLength(100, ErrorMessage = "The email must be at max 100 characters long.")]
|
2024-10-03 20:14:04 +00:00
|
|
|
[EmailAddress(ErrorMessage = "Invalid Email Address.")]
|
|
|
|
[Required(ErrorMessage = "Email address is required.")]
|
2024-10-08 11:31:54 +00:00
|
|
|
public string Email { get; init; } = string.Empty;
|
2024-10-03 20:14:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
[Required(ErrorMessage = "Password is required.")]
|
2024-10-08 11:31:54 +00:00
|
|
|
[StringLength(255, ErrorMessage = "Password must be between 8 and 255 characters.")]
|
2024-10-03 20:14:04 +00:00
|
|
|
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$",
|
|
|
|
ErrorMessage = "Password must be at least 8 characters long and contain at least one uppercase letter,"
|
|
|
|
+ " one lowercase letter, one number, and one special character.")]
|
2024-10-08 11:31:54 +00:00
|
|
|
public string Password { get; init; } = string.Empty;
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
2024-10-08 11:31:54 +00:00
|
|
|
|
|
|
|
public class RegisterAuthPayload : LoginAuthPayload
|
|
|
|
{
|
|
|
|
[Required(ErrorMessage = "First name is required.")]
|
|
|
|
[RegularExpression(@"^[A-Z][a-zA-Z]*$", ErrorMessage =
|
|
|
|
"First name must start with a capital letter and contain only letters.")]
|
|
|
|
[StringLength(50, ErrorMessage = "First name cannot exceed 50 characters.")]
|
|
|
|
public string? FirstName { get; init; } = string.Empty;
|
|
|
|
|
|
|
|
[Required(ErrorMessage = "Last name is required.")]
|
|
|
|
[RegularExpression(@"^[A-Z][a-zA-Z]*$", ErrorMessage =
|
|
|
|
"Last name must start with a capital letter and contain only letters.")]
|
|
|
|
[StringLength(50, ErrorMessage = "Last name cannot exceed 50 characters.")]
|
|
|
|
public string? LastName { get; init; } = string.Empty;
|
|
|
|
|
|
|
|
[Required(ErrorMessage = "Birthday is required.")]
|
|
|
|
[DataType(DataType.Date)]
|
|
|
|
public DateTime? Birthday { get; init; }
|
|
|
|
|
|
|
|
[Required(ErrorMessage = "Phone number is required.")]
|
|
|
|
[StringLength(15, ErrorMessage = "Phone number cannot exceed 15 characters.")]
|
|
|
|
[RegularExpression(@"^(\+33|0)[1-9](\d{2}){4}$", ErrorMessage =
|
|
|
|
"Phone number must be a valid French phone number (e.g., +33 6 12 34 56 78 or 06 12 34 56 78).")]
|
|
|
|
public string? PhoneNumber { get; init; } = string.Empty;
|
|
|
|
}
|
|
|
|
|
2024-10-03 20:14:04 +00:00
|
|
|
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpPut("Register")]
|
2024-10-08 11:31:54 +00:00
|
|
|
public async Task<IActionResult> Register([FromBody] RegisterAuthPayload registerAuthPayload)
|
2024-10-03 20:14:04 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
var existingUser = userService.GetUserByEmail(registerAuthPayload.Email);
|
|
|
|
if (existingUser != null)
|
|
|
|
return BadRequest("User already exists.");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
await userService.CreateUserAsync(registerAuthPayload);
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
return BadRequest(ex.Message);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok("User created.");
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
|
|
|
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpPost("Login")]
|
2024-10-08 11:31:54 +00:00
|
|
|
public IActionResult Login([FromBody] LoginAuthPayload loginAuthPayload)
|
2024-10-03 20:14:04 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
var email = loginAuthPayload.Email;
|
|
|
|
var user = userService.GetUserByEmail(email);
|
|
|
|
if (user == null || !LightCrypto.VerifyPassword(user.Password, loginAuthPayload.Password))
|
2024-10-03 20:14:04 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
return Unauthorized();
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
2024-10-08 11:31:54 +00:00
|
|
|
var token =
|
|
|
|
LightCrypto.GenerateJwtToken(email: email,
|
|
|
|
role: user.IsAdmin ? UserRoles.Admin : UserRoles.User, userId: user.Id, configuration);
|
|
|
|
return Ok(new { token = token });
|
|
|
|
}
|
|
|
|
|
|
|
|
[Authorize(Roles = UserRoles.Admin)]
|
|
|
|
[HttpGet("{id}")]
|
|
|
|
public async Task<IActionResult> GetUser(string id)
|
|
|
|
{
|
|
|
|
var user = await userService.GetUserByIdAsync(id);
|
|
|
|
if (user == null)
|
|
|
|
{
|
|
|
|
return NotFound("User not found.");
|
|
|
|
}
|
|
|
|
return Ok(user);
|
|
|
|
}
|
2024-10-03 20:14:04 +00:00
|
|
|
|
2024-10-08 11:31:54 +00:00
|
|
|
|
|
|
|
[Authorize(Roles = UserRoles.Admin)]
|
|
|
|
[HttpGet("benchmark/{numberOfUsers}")]
|
|
|
|
public async Task<IActionResult> GenerateUsers(int numberOfUsers)
|
|
|
|
{
|
|
|
|
var time = await userService.BenchmarkUsers(numberOfUsers);
|
|
|
|
|
|
|
|
return Ok($"Successfully created {numberOfUsers} user; database insertion took {time}.");
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
2024-10-08 11:31:54 +00:00
|
|
|
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
|
|
|
|
2024-10-03 21:30:14 +00:00
|
|
|
[Route("[controller]")]
|
2024-10-03 20:14:04 +00:00
|
|
|
[ApiController]
|
|
|
|
public class Test : ControllerBase
|
|
|
|
{
|
|
|
|
public class TestPayload
|
|
|
|
{
|
|
|
|
[Required(ErrorMessage = "Data field is required.")]
|
|
|
|
public string? Data { get; init; } = string.Empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
private const string ProtocolOk = "Protocol tested successfully.";
|
|
|
|
|
|
|
|
// GET: test/get
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpGet("Get")]
|
2024-10-03 20:14:04 +00:00
|
|
|
public IActionResult TestGet()
|
|
|
|
{
|
|
|
|
return Ok($"GET: {ProtocolOk}");
|
|
|
|
}
|
|
|
|
|
|
|
|
// POST: test/post
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpPost("Post")]
|
2024-10-03 20:14:04 +00:00
|
|
|
public IActionResult TestPost([FromBody] TestPayload testPayload)
|
|
|
|
{
|
|
|
|
return Ok($"POST: {ProtocolOk} Received: {testPayload.Data}");
|
|
|
|
}
|
|
|
|
|
|
|
|
// PUT: test/put
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpPut("Put")]
|
2024-10-03 20:14:04 +00:00
|
|
|
public IActionResult TestPut([FromBody] TestPayload testPayload)
|
|
|
|
{
|
|
|
|
return Ok($"PUT: {ProtocolOk} Updated: {testPayload.Data}");
|
|
|
|
}
|
|
|
|
|
|
|
|
// DELETE: test/delete
|
2024-10-03 21:16:50 +00:00
|
|
|
[HttpDelete("Delete")]
|
2024-10-03 20:14:04 +00:00
|
|
|
public IActionResult TestDelete([FromBody] TestPayload testPayload)
|
|
|
|
{
|
|
|
|
return Ok($"DELETE: {ProtocolOk} Deleted: {testPayload.Data}");
|
|
|
|
}
|
2024-10-03 21:30:14 +00:00
|
|
|
}
|
2024-10-03 21:16:50 +00:00
|
|
|
|
2024-10-03 21:30:14 +00:00
|
|
|
[Route("Test/Protected")]
|
|
|
|
[ApiController]
|
|
|
|
public class ProtectedTest : ControllerBase
|
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
// Authorized GET: Test/Protected/Basic
|
2024-10-03 21:16:50 +00:00
|
|
|
[Authorize]
|
2024-10-03 21:30:14 +00:00
|
|
|
[HttpGet("Basic")]
|
|
|
|
public IActionResult Basic()
|
2024-10-03 21:16:50 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
var userId = User.FindFirst(ClaimTypes.GivenName)!.Value;
|
|
|
|
var role = User.FindFirst(ClaimTypes.Role)!.Value;
|
|
|
|
|
|
|
|
return Ok($"Successfully executed secured request. (Any user) as {role} with id {userId}");
|
2024-10-03 21:16:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-08 11:31:54 +00:00
|
|
|
// Authorized GET: Test/Protected/UserOnly
|
2024-10-03 21:16:50 +00:00
|
|
|
[Authorize(Roles = Default.UserRoles.User)]
|
2024-10-03 21:30:14 +00:00
|
|
|
[HttpGet("UserOnly")]
|
|
|
|
public IActionResult UserOnly()
|
2024-10-03 21:16:50 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
return Ok("Successfully executed secured request. (Users only)");
|
2024-10-03 21:16:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-08 11:31:54 +00:00
|
|
|
// Authorized GET: Test/Protected/AdminOnly
|
2024-10-03 21:16:50 +00:00
|
|
|
[Authorize(Roles = Default.UserRoles.Admin)]
|
2024-10-03 21:30:14 +00:00
|
|
|
[HttpGet("AdminOnly")]
|
|
|
|
public IActionResult AdminOnly()
|
2024-10-03 21:16:50 +00:00
|
|
|
{
|
2024-10-08 11:31:54 +00:00
|
|
|
return Ok("Successfully executed secured request. (Admins only)");
|
2024-10-03 21:16:50 +00:00
|
|
|
}
|
2024-10-03 20:14:04 +00:00
|
|
|
}
|
|
|
|
}
|