From 82ade3c31fb17b662feec59e9e654ceb66edbb7a Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Wed, 21 Dec 2022 23:37:23 +0100 Subject: feat: Add initial schema and start login --- code/api/Endpoints/Account/CreateEndpoint.cs | 52 ++++++++++++++++++++++++++++ code/api/Endpoints/Account/LoginEndpoint.cs | 38 ++++++++++++++++++++ code/api/Endpoints/Account/LogoutEndpoint.cs | 16 +++++++++ code/api/Endpoints/Account/create.http | 10 ++++++ code/api/Endpoints/Base.cs | 36 +++++++++++++++++++ code/api/Endpoints/_Root/SessionEndpoint.cs | 9 +++++ 6 files changed, 161 insertions(+) create mode 100644 code/api/Endpoints/Account/CreateEndpoint.cs create mode 100644 code/api/Endpoints/Account/LoginEndpoint.cs create mode 100644 code/api/Endpoints/Account/LogoutEndpoint.cs create mode 100644 code/api/Endpoints/Account/create.http create mode 100644 code/api/Endpoints/Base.cs create mode 100644 code/api/Endpoints/_Root/SessionEndpoint.cs (limited to 'code/api/Endpoints') diff --git a/code/api/Endpoints/Account/CreateEndpoint.cs b/code/api/Endpoints/Account/CreateEndpoint.cs new file mode 100644 index 0000000..41ffe96 --- /dev/null +++ b/code/api/Endpoints/Account/CreateEndpoint.cs @@ -0,0 +1,52 @@ +namespace I2R.Storage.Api.Endpoints.Account; + +public class CreateEndpoint : Base +{ + private readonly AppDatabase _database; + private readonly UserService _userService; + private readonly IStringLocalizer _localizer; + + public CreateEndpoint(AppDatabase database, UserService userService, IStringLocalizer localizer) { + _database = database; + _userService = userService; + _localizer = localizer; + } + + public new class Request + { + public string Username { get; set; } + public string Password { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public new class Response + { + public Guid Id { get; set; } + public string Username { get; set; } + public EUserRole Role { get; set; } + } + + [AllowAnonymous] + [HttpPost("~/account/create")] + public ActionResult Handle([FromBody] Request request) { + if (!_userService.CanCreateAccount(request.Username)) { + return BadRequest(_localizer["That username is already taken"]); + } + + var user = new User() { + Username = request.Username, + Password = PasswordHelper.HashPassword(request.Password), + LastName = request.LastName, + FirstName = request.FirstName, + Role = EUserRole.LEAST_PRIVILEGED, + }; + _database.Users.Add(user); + _database.SaveChanges(); + return Ok(new Response { + Id = user.Id, + Username = user.Username, + Role = user.Role + }); + } +} \ No newline at end of file diff --git a/code/api/Endpoints/Account/LoginEndpoint.cs b/code/api/Endpoints/Account/LoginEndpoint.cs new file mode 100644 index 0000000..0ffed0f --- /dev/null +++ b/code/api/Endpoints/Account/LoginEndpoint.cs @@ -0,0 +1,38 @@ +using I2R.Storage.Api.Endpoints._Root; + +namespace I2R.Storage.Api.Endpoints.Account; + +public class LoginEndpoint : Base +{ + private readonly AppDatabase _database; + private readonly UserService _userService; + private readonly IStringLocalizer _localizer; + + public new class Request + { + public string Username { get; set; } + public string Password { get; set; } + } + + public LoginEndpoint(UserService userService, AppDatabase database, IStringLocalizer localizer) { + _userService = userService; + _database = database; + _localizer = localizer; + } + + [AllowAnonymous] + [HttpPost("~/account/login")] + public async Task Handle([FromBody] Request request) { + var user = _database.Users.FirstOrDefault(c => c.Username == request.Username); + if (user == default) { + return BadRequest(_localizer["Invalid username or password"]); + } + + if (!PasswordHelper.Verify(request.Password, user.Password)) { + return BadRequest(_localizer["Invalid username or password"]); + } + + await _userService.LogInUserAsync(HttpContext, user.DefaultClaims()); + return Ok(); + } +} \ No newline at end of file diff --git a/code/api/Endpoints/Account/LogoutEndpoint.cs b/code/api/Endpoints/Account/LogoutEndpoint.cs new file mode 100644 index 0000000..064fa9f --- /dev/null +++ b/code/api/Endpoints/Account/LogoutEndpoint.cs @@ -0,0 +1,16 @@ +namespace I2R.Storage.Api.Endpoints.Account; + +public class LogoutEndpoint : Base +{ + private readonly UserService _userService; + + public LogoutEndpoint(UserService userService) { + _userService = userService; + } + + [HttpGet("~/account/logout")] + public async Task Handle() { + await _userService.LogOutUserAsync(HttpContext); + return Ok(); + } +} \ No newline at end of file diff --git a/code/api/Endpoints/Account/create.http b/code/api/Endpoints/Account/create.http new file mode 100644 index 0000000..b29352b --- /dev/null +++ b/code/api/Endpoints/Account/create.http @@ -0,0 +1,10 @@ +POST http://localhost:5068/account/create +Content-Type: application/json;charset=utf-8 +Accept: application/json + +{ +"username": "ivar", +"password": "ivar123", +"firstName": "Ivar", +"lastName": "Løvlie" +} \ No newline at end of file diff --git a/code/api/Endpoints/Base.cs b/code/api/Endpoints/Base.cs new file mode 100644 index 0000000..211d1f6 --- /dev/null +++ b/code/api/Endpoints/Base.cs @@ -0,0 +1,36 @@ +using System.Security.Claims; + +namespace I2R.Storage.Api.Endpoints; + +[ApiController] +[Authorize] +public class Base : ControllerBase +{ + public class LoggedInUserModel + { + public string Username { get; set; } + public Guid Id { get; set; } + public EUserRole Role { get; set; } + + public class Public + { + public string Id { get; set; } + public string Username { get; set; } + public string Role { get; set; } + } + + public Public ForThePeople(HttpContext httpContext) { + return new Public() { + Id = httpContext.User.FindFirstValue(AppClaims.USER_ID), + Username = httpContext.User.FindFirstValue(AppClaims.USERNAME), + Role = httpContext.User.FindFirstValue(AppClaims.USER_ROLE) + }; + } + } + + public LoggedInUserModel LoggedInUser => new LoggedInUserModel() { + Id = HttpContext.User.FindFirstValue(AppClaims.USER_ID).AsGuid(), + Username = HttpContext.User.FindFirstValue(AppClaims.USERNAME), + Role = UserRole.FromString(HttpContext.User.FindFirstValue(AppClaims.USER_ROLE)) + }; +} \ No newline at end of file diff --git a/code/api/Endpoints/_Root/SessionEndpoint.cs b/code/api/Endpoints/_Root/SessionEndpoint.cs new file mode 100644 index 0000000..8d6ca56 --- /dev/null +++ b/code/api/Endpoints/_Root/SessionEndpoint.cs @@ -0,0 +1,9 @@ +namespace I2R.Storage.Api.Endpoints._Root; + +public class SessionEndpoint : Base +{ + [HttpGet("~/session")] + public ActionResult Handle() { + return LoggedInUser.ForThePeople(HttpContext); + } +} \ No newline at end of file -- cgit v1.3