summaryrefslogtreecommitdiffstats
path: root/src/server/Api/Internal/Account
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-01-22 22:43:38 +0100
committerivarlovlie <git@ivarlovlie.no>2022-01-22 22:43:38 +0100
commit88110f536f9c3843ecf5016122e101f8a424af77 (patch)
treee8be4e77ccfb5ad37f49f89adad59ff12b4c85ea /src/server/Api/Internal/Account
downloadbookmark-thing-88110f536f9c3843ecf5016122e101f8a424af77.tar.xz
bookmark-thing-88110f536f9c3843ecf5016122e101f8a424af77.zip
Initial commit
Diffstat (limited to 'src/server/Api/Internal/Account')
-rw-r--r--src/server/Api/Internal/Account/CreateSessionRequest.cs8
-rw-r--r--src/server/Api/Internal/Account/CreateSessionRoute.cs41
-rw-r--r--src/server/Api/Internal/Account/CreateTokenRequest.cs6
-rw-r--r--src/server/Api/Internal/Account/CreateTokenRoute.cs34
-rw-r--r--src/server/Api/Internal/Account/DeleteTokenRoute.cs24
-rw-r--r--src/server/Api/Internal/Account/EndSessionRoute.cs13
-rw-r--r--src/server/Api/Internal/Account/GetArchiveRoute.cs36
-rw-r--r--src/server/Api/Internal/Account/GetProfileDataRoute.cs11
-rw-r--r--src/server/Api/Internal/Account/GetTokensRoute.cs17
-rw-r--r--src/server/Api/Internal/Account/UpdatePasswordRequest.cs6
-rw-r--r--src/server/Api/Internal/Account/UpdatePasswordRoute.cs35
11 files changed, 231 insertions, 0 deletions
diff --git a/src/server/Api/Internal/Account/CreateSessionRequest.cs b/src/server/Api/Internal/Account/CreateSessionRequest.cs
new file mode 100644
index 0000000..24c28b4
--- /dev/null
+++ b/src/server/Api/Internal/Account/CreateSessionRequest.cs
@@ -0,0 +1,8 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class CreateSessionRequest
+{
+ public string Username { get; set; }
+ public string Password { get; set; }
+ public bool Persist { get; set; }
+}
diff --git a/src/server/Api/Internal/Account/CreateSessionRoute.cs b/src/server/Api/Internal/Account/CreateSessionRoute.cs
new file mode 100644
index 0000000..09e05b6
--- /dev/null
+++ b/src/server/Api/Internal/Account/CreateSessionRoute.cs
@@ -0,0 +1,41 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class CreateSessionRoute : RouteBaseInternalAsync.WithRequest<CreateSessionRequest>.WithActionResult
+{
+ private readonly AppDbContext _context;
+
+ public CreateSessionRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [AllowAnonymous]
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpPost("~/v{version:apiVersion}/account/create-session")]
+ public override async Task<ActionResult> HandleAsync(CreateSessionRequest payload, CancellationToken cancellationToken = default) {
+ var user = _context.Users.SingleOrDefault(u => u.Username == payload.Username);
+ if (user == default || !user.VerifyPassword(payload.Password)) {
+ return BadRequest(new ErrorResult("Invalid username or password"));
+ }
+
+ var claims = user.DefaultClaims();
+ var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
+ var principal = new ClaimsPrincipal(identity);
+ var authenticationProperties = new AuthenticationProperties {
+ AllowRefresh = true,
+ IssuedUtc = DateTimeOffset.UtcNow,
+ };
+
+ if (payload.Persist) {
+ authenticationProperties.ExpiresUtc = DateTimeOffset.UtcNow.AddYears(100);
+ authenticationProperties.IsPersistent = true;
+ }
+
+ await HttpContext.SignInAsync(principal, authenticationProperties);
+ // Return new LoggedInInternalUser here, because it is not materialised in AppControllerBase yet.
+ return Ok(new LoggedInInternalUser {
+ Id = user.Id,
+ Username = user.Username
+ });
+ }
+}
diff --git a/src/server/Api/Internal/Account/CreateTokenRequest.cs b/src/server/Api/Internal/Account/CreateTokenRequest.cs
new file mode 100644
index 0000000..399bdfc
--- /dev/null
+++ b/src/server/Api/Internal/Account/CreateTokenRequest.cs
@@ -0,0 +1,6 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class CreateTokenRequest
+{
+ public string Name { get; set; }
+}
diff --git a/src/server/Api/Internal/Account/CreateTokenRoute.cs b/src/server/Api/Internal/Account/CreateTokenRoute.cs
new file mode 100644
index 0000000..ea0e01f
--- /dev/null
+++ b/src/server/Api/Internal/Account/CreateTokenRoute.cs
@@ -0,0 +1,34 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class CreateTokenRoute : RouteBaseInternalSync.WithRequest<CreateTokenRequest>.WithActionResult
+{
+ private readonly AppDbContext _context;
+
+ public CreateTokenRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpPost("~/v{version:apiVersion}/account/create-token")]
+ public override ActionResult Handle(CreateTokenRequest request) {
+ var user = _context.Users.SingleOrDefault(c => c.Id == LoggedInUser.Id);
+ if (user == default) {
+ return NotFound(new ErrorResult("User does not exist"));
+ }
+
+ if (request.Name.IsNullOrWhiteSpace()) {
+ return BadRequest(new ErrorResult("Token name is required"));
+ }
+
+ var token = new AccessToken {
+ Id = Guid.NewGuid(),
+ Name = request.Name,
+ User = user
+ };
+
+ _context.AccessTokens.Add(token);
+ _context.SaveChanges();
+ return Ok(token);
+ }
+}
diff --git a/src/server/Api/Internal/Account/DeleteTokenRoute.cs b/src/server/Api/Internal/Account/DeleteTokenRoute.cs
new file mode 100644
index 0000000..f423b6f
--- /dev/null
+++ b/src/server/Api/Internal/Account/DeleteTokenRoute.cs
@@ -0,0 +1,24 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class DeleteTokenRoute : RouteBaseInternalSync.WithRequest<Guid>.WithActionResult
+{
+ private readonly AppDbContext _context;
+
+ public DeleteTokenRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpDelete("~/v{version:apiVersion}/account/delete-token")]
+ public override ActionResult Handle(Guid id) {
+ var token = _context.AccessTokens.SingleOrDefault(c => c.Id == id);
+ if (token == default) {
+ return NotFound();
+ }
+
+ _context.AccessTokens.Remove(token);
+ _context.SaveChanges();
+ return Ok();
+ }
+}
diff --git a/src/server/Api/Internal/Account/EndSessionRoute.cs b/src/server/Api/Internal/Account/EndSessionRoute.cs
new file mode 100644
index 0000000..4f32168
--- /dev/null
+++ b/src/server/Api/Internal/Account/EndSessionRoute.cs
@@ -0,0 +1,13 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class EndSessionRoute : RouteBaseInternalAsync.WithoutRequest.WithActionResult
+{
+ [AllowAnonymous]
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpGet("~/v{version:apiVersion}/account/end-session")]
+ public override async Task<ActionResult> HandleAsync(CancellationToken cancellationToken = default) {
+ await HttpContext.SignOutAsync();
+ return Ok();
+ }
+}
diff --git a/src/server/Api/Internal/Account/GetArchiveRoute.cs b/src/server/Api/Internal/Account/GetArchiveRoute.cs
new file mode 100644
index 0000000..5dc006e
--- /dev/null
+++ b/src/server/Api/Internal/Account/GetArchiveRoute.cs
@@ -0,0 +1,36 @@
+using IOL.BookmarkThing.Server.Api.Internal.Dtos;
+
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class GetArchiveRoute : RouteBaseInternalSync.WithoutRequest.WithActionResult
+{
+ private readonly AppDbContext _context;
+
+ public GetArchiveRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpGet("~/v{version:apiVersion}/account/archive")]
+ public override ActionResult Handle() {
+ var user = _context.Users.SingleOrDefault(c => c.Id == LoggedInUser.Id);
+ if (user == default) {
+ return NotFound();
+ }
+
+ var entries = _context.Entries.Where(c => c.UserId == user.Id);
+ var archive = new UserArchiveDto() {
+ Created = DateTime.UtcNow,
+ User = new UserArchiveDto.UserArchiveUser(user),
+ Entries = entries.Select(c => new UserArchiveDto.UserArchiveEntry(c)).ToList()
+ };
+ var jsonOptions = new JsonSerializerOptions {
+ WriteIndented = true
+ };
+ var archiveBytes = JsonSerializer.SerializeToUtf8Bytes(archive, jsonOptions);
+ return File(archiveBytes,
+ "application/json",
+ "bookmark-thing-archive-" + user.Username + "-" + DateTime.UtcNow.ToString("yyyyMMddTHHmmss") + ".json");
+ }
+}
diff --git a/src/server/Api/Internal/Account/GetProfileDataRoute.cs b/src/server/Api/Internal/Account/GetProfileDataRoute.cs
new file mode 100644
index 0000000..adf1cba
--- /dev/null
+++ b/src/server/Api/Internal/Account/GetProfileDataRoute.cs
@@ -0,0 +1,11 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class GetProfileDataRoute : RouteBaseInternalSync.WithoutRequest.WithActionResult<LoggedInInternalUser>
+{
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpGet("~/v{version:apiVersion}/account/profile-data")]
+ public override ActionResult<LoggedInInternalUser> Handle() {
+ return Ok(LoggedInUser);
+ }
+}
diff --git a/src/server/Api/Internal/Account/GetTokensRoute.cs b/src/server/Api/Internal/Account/GetTokensRoute.cs
new file mode 100644
index 0000000..7e87bc7
--- /dev/null
+++ b/src/server/Api/Internal/Account/GetTokensRoute.cs
@@ -0,0 +1,17 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class GetTokensRoute : RouteBaseInternalSync.WithoutRequest.WithResult<ActionResult<IList<AccessToken>>>
+{
+ private readonly AppDbContext _context;
+
+ public GetTokensRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpGet("~/v{version:apiVersion}/account/tokens")]
+ public override ActionResult<IList<AccessToken>> Handle() {
+ return Ok(_context.AccessTokens.Where(c => c.User.Id == LoggedInUser.Id));
+ }
+}
diff --git a/src/server/Api/Internal/Account/UpdatePasswordRequest.cs b/src/server/Api/Internal/Account/UpdatePasswordRequest.cs
new file mode 100644
index 0000000..1773bb0
--- /dev/null
+++ b/src/server/Api/Internal/Account/UpdatePasswordRequest.cs
@@ -0,0 +1,6 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class UpdatePasswordRequest
+{
+ public string NewPassword { get; set; }
+}
diff --git a/src/server/Api/Internal/Account/UpdatePasswordRoute.cs b/src/server/Api/Internal/Account/UpdatePasswordRoute.cs
new file mode 100644
index 0000000..d06e850
--- /dev/null
+++ b/src/server/Api/Internal/Account/UpdatePasswordRoute.cs
@@ -0,0 +1,35 @@
+namespace IOL.BookmarkThing.Server.Api.Internal.Account;
+
+public class UpdatePasswordRoute : RouteBaseInternalSync.WithRequest<UpdatePasswordRequest>.WithActionResult
+{
+ private readonly AppDbContext _context;
+
+ public UpdatePasswordRoute(AppDbContext context) {
+ _context = context;
+ }
+
+ [ApiVersionNeutral]
+ [ApiExplorerSettings(IgnoreApi = true)]
+ [HttpPost("~/v{version:apiVersion}/account/update-password")]
+ public override ActionResult Handle(UpdatePasswordRequest payload) {
+ if (payload.NewPassword.IsNullOrWhiteSpace()) {
+ return BadRequest(new ErrorResult("Invalid request",
+ "The new password field is required"));
+ }
+
+ if (payload.NewPassword.Length < 6) {
+ return BadRequest(new ErrorResult("Invalid request",
+ "The new password must contain atleast 6 characters"));
+ }
+
+ var user = _context.Users.SingleOrDefault(c => c.Id == LoggedInUser.Id);
+ if (user == default) {
+ HttpContext.SignOutAsync();
+ return StatusCode(403);
+ }
+
+ user.HashAndSetPassword(payload.NewPassword);
+ _context.SaveChanges();
+ return Ok();
+ }
+}