Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions API.IntegrationTests/Tests/AccountAuthenticatedTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using OpenShock.API.IntegrationTests.Helpers;

Expand All @@ -9,6 +10,44 @@ public sealed class AccountAuthenticatedTests
[ClassDataSource<WebApplicationFactory>(Shared = SharedType.PerTestSession)]
public required WebApplicationFactory WebApplicationFactory { get; init; }

// --- Get Self ---

[Test]
public async Task GetSelf_ReturnsCurrentUserInfo_WithoutLegacyWrapper()
{
var user = await TestHelper.CreateAndLoginUser(WebApplicationFactory, "accself", "accself@test.org", "SecurePassword123#");
using var client = TestHelper.CreateAuthenticatedClient(WebApplicationFactory, user.SessionToken);

var response = await client.GetAsync("/1/account");

await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);

using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var root = doc.RootElement;
await Assert.That(root.TryGetProperty("data", out _)).IsFalse();
await Assert.That(root.GetProperty("name").GetString()).IsEqualTo("accself");
await Assert.That(root.GetProperty("email").GetString()).IsEqualTo("accself@test.org");
await Assert.That(root.GetProperty("hasPassword").GetBoolean()).IsTrue();
await Assert.That(root.TryGetProperty("id", out _)).IsTrue();
await Assert.That(root.TryGetProperty("image", out _)).IsTrue();
await Assert.That(root.TryGetProperty("roles", out _)).IsTrue();
}

[Test]
public async Task GetSelf_WithApiToken_ReturnsUserInfo()
{
var userId = await TestHelper.CreateUserInDb(WebApplicationFactory, "accselfapi", "accselfapi@test.org", "SecurePassword123#");
var (_, rawToken) = await TestHelper.CreateApiTokenInDb(WebApplicationFactory, userId, "AccountSelfApiToken");
using var client = TestHelper.CreateApiTokenClient(WebApplicationFactory, rawToken);

var response = await client.GetAsync("/1/account");

await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);

using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
await Assert.That(doc.RootElement.GetProperty("name").GetString()).IsEqualTo("accselfapi");
}

// --- Change Password ---

[Test]
Expand Down
1 change: 1 addition & 0 deletions API.IntegrationTests/Tests/AuthorizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public sealed class AuthorizationTests
[Arguments("/1/tokens")]
[Arguments("/1/sessions/self")]
[Arguments("/1/users/self")]
[Arguments("/1/account")]
public async Task ProtectedEndpoint_NoAuth_Returns401(string url)
{
using var client = WebApplicationFactory.CreateClient();
Expand Down
29 changes: 29 additions & 0 deletions API/Controller/Account/Authenticated/GetSelf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenShock.API.Models.Response;
using OpenShock.Common.Authentication;
using OpenShock.Common.Extensions;
using OpenShock.Common.OpenShockDb;

namespace OpenShock.API.Controller.Account.Authenticated;

public sealed partial class AuthenticatedAccountController
{
/// <summary>
/// Get the current user's account information.
/// </summary>
/// <response code="200">The account information was successfully retrieved.</response>
[HttpGet]
[Authorize(AuthenticationSchemes = OpenShockAuthSchemes.UserSessionApiTokenCombo)]
[ProducesResponseType<UserSelfResponse>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)]
public UserSelfResponse GetSelf() => new()
{
Id = CurrentUser.Id,
Name = CurrentUser.Name,
Email = CurrentUser.Email,
Image = CurrentUser.GetImageUrl(),
Roles = CurrentUser.Roles,
HasPassword = !string.IsNullOrEmpty(CurrentUser.PasswordHash)
};
}
20 changes: 5 additions & 15 deletions API/Controller/Users/GetSelf.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;
using OpenShock.API.Models.Response;
using OpenShock.Common.Extensions;
using OpenShock.Common.Models;
using OpenShock.Common.OpenShockDb;

namespace OpenShock.API.Controller.Users;

public sealed partial class UsersController
{
/// <summary>
/// Get the current user's information.
/// Get the current user's information. Deprecated: use GET /1/account instead.
/// </summary>
/// <response code="200">The user's information was successfully retrieved.</response>
[Obsolete("Use GET /1/account instead.")]
[HttpGet("self")]
[ProducesResponseType<LegacyDataResponse<UserSelfResponse>>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)]
[ProducesResponseType<LegacyDataResponse<LegacyUserSelfResponse>>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)]
public IActionResult GetSelf()
{
return LegacyDataOk(
new UserSelfResponse
new LegacyUserSelfResponse
{
Id = CurrentUser.Id,
Name = CurrentUser.Name,
Expand All @@ -29,15 +30,4 @@ public IActionResult GetSelf()
}
);
}

public sealed class UserSelfResponse
{
public required Guid Id { get; init; }
public required string Name { get; init; }
public required string Email { get; init; }
public required Uri Image { get; init; }
public required List<RoleType> Roles { get; init; }
public required string Rank { get; init; }
public required bool HasPassword { get; init; }
}
}
17 changes: 17 additions & 0 deletions API/Models/Response/LegacyUserSelfResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using OpenShock.Common.OpenShockDb;

namespace OpenShock.API.Models.Response;

/// <summary>
/// Response shape of the deprecated GET /1/users/self, kept for API token clients that still read <c>rank</c>.
/// </summary>
public sealed class LegacyUserSelfResponse
{
public required Guid Id { get; init; }
public required string Name { get; init; }
public required string Email { get; init; }
public required Uri Image { get; init; }
public required List<RoleType> Roles { get; init; }
public required string Rank { get; init; }
public required bool HasPassword { get; init; }
}
13 changes: 13 additions & 0 deletions API/Models/Response/UserSelfResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using OpenShock.Common.OpenShockDb;

namespace OpenShock.API.Models.Response;

public sealed class UserSelfResponse
{
public required Guid Id { get; init; }
public required string Name { get; init; }
public required string Email { get; init; }
public required Uri Image { get; init; }
public required List<RoleType> Roles { get; init; }
public required bool HasPassword { get; init; }
}
Loading