Last active
April 10, 2025 13:54
-
-
Save sadukie/355a0f029bc571655f783e6ee55d5d43 to your computer and use it in GitHub Desktop.
eShopOnWeb - User Management - API + Tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
namespace Microsoft.eShopWeb.PublicApi.Extensions; | |
public static class ApplicationUserExtensions | |
{ | |
public static UserDto ToUserDto(this ApplicationUser user) | |
{ | |
UserDto dto = new UserDto(); | |
if (user != null) | |
{ | |
dto.Id = user.Id; | |
dto.UserName = user.UserName; | |
dto.Email = user.Email; | |
dto.PhoneNumber = user.PhoneNumber; | |
dto.EmailConfirmed = user.EmailConfirmed; | |
dto.PhoneNumberConfirmed = user.PhoneNumberConfirmed; | |
dto.LockoutEnd = user.LockoutEnd; | |
} | |
return dto; | |
} | |
public static void FromUserDto(this ApplicationUser user, UserDto userChanges, bool copyId = true) | |
{ | |
if (user != null) | |
{ | |
if (copyId) | |
user.Id = userChanges.Id; | |
user.UserName = userChanges.UserName; | |
user.Email = userChanges.Email; | |
user.PhoneNumber = userChanges.PhoneNumber; | |
user.EmailConfirmed = userChanges.EmailConfirmed; | |
user.PhoneNumberConfirmed = userChanges.PhoneNumberConfirmed; | |
user.LockoutEnd = userChanges.LockoutEnd; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class CreateUserRequest : BaseRequest | |
{ | |
public UserDto User { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class CreateUserResponse : BaseResponse | |
{ | |
public CreateUserResponse(Guid correlationId) | |
{ | |
} | |
public CreateUserResponse() { } | |
public string UserId { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.ApplicationCore.Constants; | |
using Microsoft.eShopWeb.ApplicationCore.Exceptions; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.Extensions; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class CreateUserEndpoint(UserManager<ApplicationUser> userManager) : Endpoint<CreateUserRequest, CreateUserResponse> | |
{ | |
public override void Configure() | |
{ | |
Post("api/users"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<CreateUserResponse>() | |
.WithTags("UserManagementEndpoints") | |
); | |
} | |
public override async Task HandleAsync(CreateUserRequest request, CancellationToken ct) | |
{ | |
var response = new CreateUserResponse(request.CorrelationId()); | |
if (request is null || request.User is null || request.User.UserName is null) | |
{ | |
await SendErrorsAsync(400, ct); | |
return; | |
} | |
var existingUser = await userManager.FindByNameAsync(request.User.UserName); | |
if (existingUser != null) { | |
throw new DuplicateException($"User already exists."); | |
}; | |
ApplicationUser newUser = new ApplicationUser(); | |
newUser.FromUserDto(request.User, copyId: false); | |
var createUser = await userManager.CreateAsync(newUser); | |
if (createUser.Succeeded) | |
{ | |
var createdUser = await userManager.FindByNameAsync(request.User.UserName); | |
await userManager.AddPasswordAsync(createdUser!, AuthorizationConstants.DEFAULT_PASSWORD); | |
response.UserId = createdUser!.Id; | |
await SendCreatedAtAsync<UserGetByIdEndpoint>(new { UserId = createdUser!.Id }, response, cancellation: ct); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Net.Http; | |
using System.Text; | |
using System.Text.Json; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class CreateUserEndpointTest | |
{ | |
private readonly string _testName = "[email protected]"; | |
[TestMethod] | |
public async Task ReturnsForbiddenGivenNormalUserToken() | |
{ | |
var jsonContent = GetValidNewItemJson(_testName); | |
var client = HttpClientHelper.GetNormalUserClient(); | |
var response = await client.PostAsync("api/users", jsonContent); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken() | |
{ | |
var jsonContent = GetValidNewItemJson(_testName); | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.PostAsync("api/users", jsonContent); | |
response.EnsureSuccessStatusCode(); | |
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); | |
var stringResponse = await response.Content.ReadAsStringAsync(); | |
var model = stringResponse.FromJson<CreateUserResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsTrue(!string.IsNullOrEmpty(model.UserId)); | |
} | |
[TestMethod] | |
public async Task ReturnsConflictForDuplicateUserName() | |
{ | |
var jsonContent = GetValidNewItemJson("[email protected]"); | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.PostAsync("api/users", jsonContent); | |
Assert.AreEqual(HttpStatusCode.Conflict, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsBadRequestForMissingUserName() | |
{ | |
var jsonContent = GetInvalidNewItemJson(); | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.PostAsync("api/users", jsonContent); | |
Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); | |
} | |
private StringContent GetValidNewItemJson(string userName) | |
{ | |
var newUser = new UserDto() | |
{ | |
UserName = userName | |
}; | |
var request = new CreateUserRequest | |
{ | |
User = newUser | |
}; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
return jsonContent; | |
} | |
private StringContent GetInvalidNewItemJson() | |
{ | |
var newUser = new UserDto() | |
{ | |
}; | |
var request = new CreateUserRequest | |
{ | |
User = newUser | |
}; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
return jsonContent; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class DeleteUserEndpoint(UserManager<ApplicationUser> userManager) : Endpoint<DeleteUserRequest, Results<NoContent, NotFound, BadRequest>> | |
{ | |
public override void Configure() | |
{ | |
Delete("api/users/{userId}"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
{ | |
d.Produces(StatusCodes.Status204NoContent); | |
d.Produces(StatusCodes.Status404NotFound); | |
d.Produces(StatusCodes.Status409Conflict); | |
d.WithTags("UserManagementEndpoints"); | |
} | |
); | |
} | |
public override async Task<Results<NoContent, NotFound, BadRequest>> ExecuteAsync(DeleteUserRequest request, CancellationToken ct) | |
{ | |
var userToDelete = await userManager.FindByIdAsync(request.UserId); | |
if (userToDelete is null || string.IsNullOrEmpty(userToDelete.UserName)) | |
{ | |
return TypedResults.NotFound(); | |
} | |
if (userToDelete.UserName == "[email protected]") | |
{ | |
return TypedResults.BadRequest(); | |
} | |
await userManager.DeleteAsync(userToDelete); | |
return TypedResults.NoContent(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class DeleteUserRequest : BaseRequest | |
{ | |
public string UserId { get; init; } | |
public DeleteUserRequest(string userId) | |
{ | |
UserId = userId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class DeleteUserEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.DeleteAsync("api/users/0"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsForbiddenGivenProductManagerToken() | |
{ | |
var client = HttpClientHelper.GetProductManagerClient(); | |
var response = await client.DeleteAsync("api/users/0"); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsForbiddenGivenNormalUserToken() | |
{ | |
var client = HttpClientHelper.GetNormalUserClient(); | |
var response = await client.DeleteAsync("api/users/0"); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsBadRequestTryingToDeleteAdminUser() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var userName = "[email protected]"; | |
var response = await client.GetAsync($"api/users/name/{userName}"); | |
var adminUserResponse = await response.Content.ReadAsStringAsync(); | |
var adminUser = adminUserResponse.FromJson<GetUserResponse>(); | |
var deleteResponse = await client.DeleteAsync($"api/users/{adminUser!.User!.Id}"); | |
Assert.AreEqual(HttpStatusCode.BadRequest, deleteResponse.StatusCode); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
public class GetUserResponse : BaseResponse | |
{ | |
public GetUserResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public GetUserResponse() | |
{ | |
} | |
public UserDto User { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
public class GetUserResponse : BaseResponse | |
{ | |
public GetUserResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public GetUserResponse() | |
{ | |
} | |
public UserDto User { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class SaveRolesForUserEndpoint(UserManager<ApplicationUser> userManager) : Endpoint<SaveRolesForUserRequest, Results<Ok, NotFound, BadRequest>> | |
{ | |
public override void Configure() | |
{ | |
Put("api/users/{userId}/roles"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
{ | |
d.Produces(StatusCodes.Status200OK); | |
d.Produces(StatusCodes.Status404NotFound); | |
d.WithTags("UserManagementEndpoints"); | |
} | |
); | |
} | |
public override async Task<Results<Ok, NotFound, BadRequest>> ExecuteAsync(SaveRolesForUserRequest request, CancellationToken ct) | |
{ | |
var userToUpdate = await userManager.FindByIdAsync(request.UserId); | |
if (userToUpdate is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
if (string.IsNullOrEmpty(userToUpdate.UserName)) | |
{ | |
throw new System.Exception("Unknown user to update"); | |
} | |
if (userToUpdate.UserName == "[email protected]") | |
{ | |
return TypedResults.BadRequest(); | |
} | |
if (request.RolesToAdd.Count > 0) | |
await userManager.AddToRolesAsync(userToUpdate,request.RolesToAdd); | |
if (request.RolesToRemove.Count > 0) | |
await userManager.RemoveFromRolesAsync(userToUpdate,request.RolesToRemove); | |
return TypedResults.Ok(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class SaveRolesForUserRequest : BaseRequest | |
{ | |
public string UserId { get; set; } | |
public List<string> RolesToAdd { get; set; } = []; | |
public List<string> RolesToRemove { get; set; } = []; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.Extensions; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UpdateRoleEndpoint(UserManager<ApplicationUser> userManager) : Endpoint<UpdateUserRequest,Results<Ok<UpdateUserResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Put("api/users"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<UpdateUserResponse>() | |
.WithTags("UserManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<UpdateUserResponse>, NotFound>> ExecuteAsync(UpdateUserRequest request, CancellationToken ct) | |
{ | |
var response = new UpdateUserResponse(request.CorrelationId()); | |
if (request is null || request.User is null || request.User.Id is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
var existingUser = await userManager.FindByIdAsync(request.User.Id); | |
if (existingUser is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
existingUser.FromUserDto(request.User); | |
await userManager.UpdateAsync(existingUser); | |
response.User = (await userManager.FindByIdAsync(existingUser.Id))!; | |
return TypedResults.Ok(response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UpdateUserRequest : BaseRequest | |
{ | |
public UserDto User { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UpdateUserResponse : BaseResponse | |
{ | |
public UpdateUserResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public UpdateUserResponse() | |
{ | |
} | |
public ApplicationUser User { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Net.Http; | |
using System.Text; | |
using System.Text.Json; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class UpdateUserEndpointTest | |
{ | |
[TestMethod] | |
public async Task UpdatesSuccessfullyGivenValidChangesAndAdminUserToken() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var userName = "[email protected]"; | |
var response = await client.GetAsync($"api/users/name/{userName}"); | |
var demoUserResponse = await response.Content.ReadAsStringAsync(); | |
var demoUser = demoUserResponse.FromJson<Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models.GetUserResponse>(); | |
var startingEmailConfirmedValue = demoUser!.User.EmailConfirmed; | |
demoUser.User.EmailConfirmed = !startingEmailConfirmedValue; | |
UpdateUserRequest request = new UpdateUserRequest() | |
{ | |
User = demoUser.User | |
}; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
var updateResponse = await client.PutAsync("api/users", jsonContent); | |
updateResponse.EnsureSuccessStatusCode(); | |
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); | |
var updateResponseJsonString = await updateResponse.Content.ReadAsStringAsync(); | |
var updatedItem = updateResponseJsonString.FromJson<UpdateUserResponse>(); | |
Assert.IsNotNull(updatedItem); | |
Assert.AreEqual(!startingEmailConfirmedValue, updatedItem.User.EmailConfirmed); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundForNullRequest() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
UpdateUserRequest? request = null; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
var updateResponse = await client.PutAsync("api/users", jsonContent); | |
Assert.AreEqual(HttpStatusCode.NotFound, updateResponse.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundForNullUserInRequest() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
UpdateUserRequest request = new(); | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
var updateResponse = await client.PutAsync("api/users", jsonContent); | |
Assert.AreEqual(HttpStatusCode.NotFound, updateResponse.StatusCode); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserDto() | |
{ | |
public string Id { get; set; } | |
public string? UserName { get; set; } | |
public string? Email { get; set; } | |
public bool EmailConfirmed { get; set; } | |
public string? PhoneNumber { get; set; } | |
public bool PhoneNumberConfirmed { get; set; } | |
public bool TwoFactorEnabled { get; set; } | |
public DateTimeOffset? LockoutEnd { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.Extensions; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserGetByIdEndpoint (UserManager<ApplicationUser> userManager) : Endpoint <GetByIdUserRequest, Results<Ok<GetUserResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Get("api/users/{userId}"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<GetUserResponse>() | |
.WithTags("UserManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<GetUserResponse>, NotFound>> ExecuteAsync(GetByIdUserRequest request, CancellationToken ct) | |
{ | |
var response = new GetUserResponse(request.CorrelationId()); | |
var userResponse = await userManager.FindByIdAsync(request.UserId); | |
if (userResponse is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
response.User = userResponse.ToUserDto(); | |
return TypedResults.Ok(response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class GetByIdUserRequest : BaseRequest | |
{ | |
public string UserId { get; init; } | |
public GetByIdUserRequest(string userId) | |
{ | |
UserId = userId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class UserGetByIdEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsItemGivenValidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var userName = "[email protected]"; | |
var response = await client.GetAsync($"api/users/name/{userName}"); | |
var adminUserResponse = await response.Content.ReadAsStringAsync(); | |
var adminUser = adminUserResponse.FromJson<GetUserResponse>(); | |
var getUserById = await client.GetAsync($"api/users/{adminUser!.User!.Id}"); | |
getUserById.EnsureSuccessStatusCode(); | |
var adminUserByIdResponse = await response.Content.ReadAsStringAsync(); | |
var adminModel = adminUserByIdResponse.FromJson<GetUserResponse>(); | |
Assert.IsNotNull(adminModel); | |
Assert.IsNotNull(adminModel.User.UserName); | |
Assert.AreEqual(userName, adminModel.User.UserName); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("api/users/0"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.Extensions; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserGetByUserNameEndpoint (UserManager<ApplicationUser> userManager) : Endpoint <GetByUserNameUserRequest, Results<Ok<GetUserResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Get("api/users/name/{userName}"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<GetUserResponse>() | |
.WithTags("UserManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<GetUserResponse>, NotFound>> ExecuteAsync(GetByUserNameUserRequest request, CancellationToken ct) | |
{ | |
var response = new GetUserResponse(request.CorrelationId()); | |
var userResponse = await userManager.FindByNameAsync(request.UserName); | |
if (userResponse is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
response.User = userResponse.ToUserDto(); | |
return TypedResults.Ok(response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class GetByUserNameUserRequest : BaseRequest | |
{ | |
public string UserName { get; init; } | |
public GetByUserNameUserRequest(string userName) | |
{ | |
UserName = userName; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class UserGetByUserNameEndpointTest | |
{ | |
[TestMethod] | |
[DataRow("[email protected]")] | |
[DataRow("[email protected]")] | |
[DataRow("[email protected]")] | |
public async Task ReturnsItemGivenValidName(string userName) | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync($"api/users/name/{userName}"); | |
response.EnsureSuccessStatusCode(); | |
var adminRoleResponse = await response.Content.ReadAsStringAsync(); | |
var adminModel = adminRoleResponse.FromJson<GetUserResponse>(); | |
Assert.IsNotNull(adminModel); | |
Assert.IsNotNull(adminModel.User); | |
Assert.IsNotNull(adminModel.User.UserName); | |
Assert.AreEqual(userName, adminModel.User.UserName); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidName() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("api/users/name/0"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserGetRolesByIdEndpoint (UserManager<ApplicationUser> userManager) : Endpoint <GetRolesByUserIdRequest, Results<Ok<GetUserRolesResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Get("api/users/{userId}/roles"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<GetUserRolesResponse>() | |
.WithTags("UserManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<GetUserRolesResponse>, NotFound>> ExecuteAsync(GetRolesByUserIdRequest request, CancellationToken ct) | |
{ | |
var response = new GetUserRolesResponse(request.CorrelationId()); | |
var user = await userManager.FindByIdAsync(request.UserId); | |
if (user is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
var rolesForUser = await userManager.GetRolesAsync(user); | |
if (rolesForUser is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
response.Roles = [.. rolesForUser]; | |
return TypedResults.Ok(response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class GetRolesByUserIdRequest : BaseRequest | |
{ | |
public string UserId { get; init; } | |
public GetRolesByUserIdRequest(string userId) | |
{ | |
UserId = userId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class GetUserRolesResponse : BaseResponse | |
{ | |
public GetUserRolesResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public GetUserRolesResponse() | |
{ | |
} | |
public List<string> Roles { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.UserManagementEndpoints.Models; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class UserGetRolesByIdEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsItemGivenValidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var userName = "[email protected]"; | |
var response = await client.GetAsync($"api/users/name/{userName}"); | |
var adminUserResponse = await response.Content.ReadAsStringAsync(); | |
var adminUser = adminUserResponse.FromJson<GetUserResponse>(); | |
var getUserRoles = await client.GetAsync($"api/users/{adminUser!.User!.Id}/roles"); | |
getUserRoles.EnsureSuccessStatusCode(); | |
var userRoles = await getUserRoles.Content.ReadAsStringAsync(); | |
var userRolesList = userRoles.FromJson<GetUserRolesResponse>(); | |
Assert.IsNotNull(userRolesList); | |
Assert.IsNotNull(userRolesList.Roles); | |
Assert.IsTrue(userRolesList.Roles.Count > 0); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("api/users/0/roles"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using Microsoft.eShopWeb.PublicApi.Extensions; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserListEndpoint(UserManager<ApplicationUser> userManager):EndpointWithoutRequest<UserListResponse> | |
{ | |
public override void Configure() | |
{ | |
Get("api/users"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => d.Produces<UserListResponse>() | |
.WithTags("UserManagementEndpoints")); | |
} | |
public override async Task<UserListResponse> ExecuteAsync(CancellationToken ct) | |
{ | |
await Task.Delay(1000, ct); | |
var response = new UserListResponse(); | |
var users = userManager.Users.ToList(); | |
foreach ( var user in users) | |
{ | |
response.Users.Add(user.ToUserDto()); | |
} | |
return response; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace Microsoft.eShopWeb.PublicApi.UserManagementEndpoints; | |
public class UserListResponse : BaseResponse | |
{ | |
public UserListResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public UserListResponse() | |
{ | |
} | |
public List<UserDto> Users{ get; set; } = []; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.UserManagementEndpoints; | |
[TestClass] | |
public class UserListEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsUnauthorizedForAnonymousAccess() | |
{ | |
var client = ProgramTest.NewClient; | |
var response = await client.GetAsync("/api/users"); | |
Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsForbiddenForGeneralAuthorizedAccess() | |
{ | |
var client = HttpClientHelper.GetNormalUserClient(); | |
var response = await client.GetAsync("/api/users"); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsForbiddenForProductManagerAccess() | |
{ | |
var client = HttpClientHelper.GetProductManagerClient(); | |
var response = await client.GetAsync("/api/users"); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsSuccessAndRolesForAdministratorAccess() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("/api/users"); | |
response.EnsureSuccessStatusCode(); | |
var stringResponse = await response.Content.ReadAsStringAsync(); | |
var model = stringResponse.FromJson<UserListResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsNotNull(model.Users); | |
Assert.IsTrue(model.Users.Count > 0); | |
} | |
} |
Comments are disabled for this gist.