#48 added basic role features and some cleanUp
This commit is contained in:
@@ -1,21 +1,17 @@
|
|||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
using Hutopy.Domain.Models;
|
|
||||||
|
|
||||||
namespace Hutopy.Application.Common.Interfaces;
|
namespace Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
public interface IIdentityService
|
public interface IIdentityService
|
||||||
{
|
{
|
||||||
Task<string?> GetUserNameAsync(string userId);
|
Task<string?> GetUserNameAsync(string userId);
|
||||||
|
Task<Result> CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
||||||
Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
|
||||||
|
|
||||||
Task<UserModel?> FindUserByIdAsync(string id);
|
Task<UserModel?> FindUserByIdAsync(string id);
|
||||||
Task<UserModel?> GetCurrentUserAsync();
|
Task<UserModel?> GetCurrentUserAsync();
|
||||||
Task<UserModel?> FindUserByEmailAsync(string id);
|
Task<UserModel?> FindUserByEmailAsync(string id);
|
||||||
|
|
||||||
Task<bool> IsInRoleAsync(string userId, string role);
|
Task<bool> IsInRoleAsync(string userId, string role);
|
||||||
|
|
||||||
Task<bool> AuthorizeAsync(string userId, string policyName);
|
Task<bool> AuthorizeAsync(string userId, string policyName);
|
||||||
|
Task<Result> AddRoleAsync(string userId, string role);
|
||||||
|
Task<IList<string>> GetCurrentUserRolesAsync();
|
||||||
Task<Result> DeleteUserAsync(string userId);
|
Task<Result> DeleteUserAsync(string userId);
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/Application/Common/Interfaces/IRoleService.cs
Normal file
10
src/Application/Common/Interfaces/IRoleService.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
|
namespace Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
public interface IRoleService
|
||||||
|
{
|
||||||
|
public Task<Result> CreateRoleAsync(string roleName);
|
||||||
|
public Task<Result> DeleteRoleAsync(string roleName);
|
||||||
|
public Task<RoleModel?> FindRoleByIdAsync(string roleId);
|
||||||
|
}
|
||||||
7
src/Application/Common/Models/RoleModel.cs
Normal file
7
src/Application/Common/Models/RoleModel.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
|
public class RoleModel
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Hutopy.Domain.Models;
|
namespace Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
public class UserModel
|
public class UserModel
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Hutopy.Application.Common.Behaviours;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Hutopy.Application;
|
namespace Hutopy.Application;
|
||||||
@@ -15,7 +16,7 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
|
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
|
||||||
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(AuthorizationBehaviour<,>));
|
cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(AuthorizationBehaviour<,>));
|
||||||
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
|
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
|
||||||
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));
|
//cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Application.Common.Mappings;
|
using Hutopy.Application.Common.Mappings;
|
||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Common.Security;
|
||||||
|
|
||||||
namespace Hutopy.Application.FutureCreators.Queries;
|
namespace Hutopy.Application.FutureCreators.Queries;
|
||||||
|
|
||||||
|
[Authorize(Roles = "Administrator")]
|
||||||
public record GetFutureCreatorListQuery : IRequest<PaginatedList<FutureCreatorListDto>>
|
public record GetFutureCreatorListQuery : IRequest<PaginatedList<FutureCreatorListDto>>
|
||||||
{
|
{
|
||||||
public int PageNumber { get; init; } = 1;
|
public int PageNumber { get; init; } = 1;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Guid>
|
|||||||
|
|
||||||
public async Task<Guid> Handle(CreateUserCommand request, CancellationToken cancellationToken)
|
public async Task<Guid> Handle(CreateUserCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
await _identityService.CreateUserAsync(request.EmailAddress, request.UserName, request.FirstName, request.LastName, request.Password);
|
||||||
|
|
||||||
var user = await _identityService.FindUserByEmailAsync(request.EmailAddress);
|
var user = await _identityService.FindUserByEmailAsync(request.EmailAddress);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
@@ -23,14 +23,17 @@ public class GetCurrentUserQueryHandler(
|
|||||||
.Where(x => x.IsConfirmed == true)
|
.Where(x => x.IsConfirmed == true)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
var user = new UserDto()
|
var roles = await identityService.GetCurrentUserRolesAsync();
|
||||||
|
|
||||||
|
var user = new UserDto
|
||||||
{
|
{
|
||||||
Id = currentUserId,
|
Id = currentUserId,
|
||||||
FirstName = identityUser?.FirstName ?? "",
|
FirstName = identityUser?.FirstName ?? "",
|
||||||
LastName = identityUser?.LastName ?? "",
|
LastName = identityUser?.LastName ?? "",
|
||||||
UserName =identityUser?.UserName ?? "",
|
UserName =identityUser?.UserName ?? "",
|
||||||
UserTransactions = transactions,
|
UserTransactions = transactions,
|
||||||
TotalBalance = transactions.Sum(x => x.Amount)
|
TotalBalance = transactions.Sum(x => x.Amount),
|
||||||
|
UserRoles = roles
|
||||||
};
|
};
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public class UserDto
|
|||||||
public required string LastName { get; init; }
|
public required string LastName { get; init; }
|
||||||
public string UserName { get; init; } = String.Empty;
|
public string UserName { get; init; } = String.Empty;
|
||||||
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
||||||
|
public IList<string> UserRoles { get; init; } = [];
|
||||||
|
|
||||||
public required decimal TotalBalance { get; init; }
|
public required decimal TotalBalance { get; init; }
|
||||||
|
|
||||||
|
|||||||
7
src/Infrastructure/Identity/ApplicationRole.cs
Normal file
7
src/Infrastructure/Identity/ApplicationRole.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Hutopy.Infrastructure.Identity;
|
||||||
|
|
||||||
|
public class ApplicationRole : IdentityRole
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
using Hutopy.Domain.Models;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -36,7 +35,7 @@ public class IdentityService(
|
|||||||
return (result.ToApplicationResult(), user.Id);
|
return (result.ToApplicationResult(), user.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
public async Task<Result> CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
||||||
{
|
{
|
||||||
var applicationUser = new ApplicationUser
|
var applicationUser = new ApplicationUser
|
||||||
{
|
{
|
||||||
@@ -46,13 +45,9 @@ public class IdentityService(
|
|||||||
LastName = lastName
|
LastName = lastName
|
||||||
};
|
};
|
||||||
|
|
||||||
//todo: Need to handle errors better for the user.
|
|
||||||
var response = await userManager.CreateAsync(applicationUser, password);
|
var response = await userManager.CreateAsync(applicationUser, password);
|
||||||
|
|
||||||
if (response.Errors.Any())
|
return response.ToApplicationResult();
|
||||||
{
|
|
||||||
throw new InvalidOperationException(response.Errors.First().Description);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserModel?> FindUserByIdAsync(string id)
|
public async Task<UserModel?> FindUserByIdAsync(string id)
|
||||||
@@ -108,6 +103,14 @@ public class IdentityService(
|
|||||||
|
|
||||||
return user != null && await userManager.IsInRoleAsync(user, role);
|
return user != null && await userManager.IsInRoleAsync(user, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CurrentUserIsInRoleAsync(string role)
|
||||||
|
{
|
||||||
|
var currentUserModel = await GetCurrentUserAsync();
|
||||||
|
var currentUser = await userManager.FindByIdAsync(currentUserModel?.Id ?? "");
|
||||||
|
|
||||||
|
return currentUser != null && await userManager.IsInRoleAsync(currentUser, role);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> AuthorizeAsync(string userId, string policyName)
|
public async Task<bool> AuthorizeAsync(string userId, string policyName)
|
||||||
{
|
{
|
||||||
@@ -138,4 +141,32 @@ public class IdentityService(
|
|||||||
|
|
||||||
return result.ToApplicationResult();
|
return result.ToApplicationResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Result> AddRoleAsync(string userId, string role)
|
||||||
|
{
|
||||||
|
var hasAdminAccess = await CurrentUserIsInRoleAsync("Administrator");
|
||||||
|
|
||||||
|
if (!hasAdminAccess) return Result.Failure(new []{"Only administrator can assign new roles to a user."});
|
||||||
|
|
||||||
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
|
||||||
|
if (user is null) return Result.Failure(new []{"User not found."});
|
||||||
|
|
||||||
|
var result = await userManager.AddToRoleAsync(user, role);
|
||||||
|
|
||||||
|
return result.ToApplicationResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IList<string>> GetCurrentUserRolesAsync()
|
||||||
|
{
|
||||||
|
var currentUserModel = await GetCurrentUserAsync();
|
||||||
|
|
||||||
|
var currentUser = await userManager.FindByIdAsync(currentUserModel?.Id ?? "");
|
||||||
|
|
||||||
|
if (currentUser is null) return [];
|
||||||
|
|
||||||
|
var userRoles = await userManager.GetRolesAsync(currentUser);
|
||||||
|
|
||||||
|
return userRoles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/Infrastructure/Identity/RoleService.cs
Normal file
49
src/Infrastructure/Identity/RoleService.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Hutopy.Infrastructure.Identity;
|
||||||
|
|
||||||
|
public class RoleService(
|
||||||
|
RoleManager<ApplicationRole> roleManager
|
||||||
|
)
|
||||||
|
: IRoleService
|
||||||
|
{
|
||||||
|
public async Task<Result> CreateRoleAsync(string roleName)
|
||||||
|
{
|
||||||
|
var role = new ApplicationRole { Name = roleName, Id = Guid.NewGuid().ToString()};
|
||||||
|
var result = await roleManager.CreateAsync(role);
|
||||||
|
|
||||||
|
return result.ToApplicationResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result> DeleteRoleAsync(string roleName)
|
||||||
|
{
|
||||||
|
var role = new ApplicationRole { Name = roleName };
|
||||||
|
var result = await roleManager.DeleteAsync(role);
|
||||||
|
|
||||||
|
return result.ToApplicationResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RoleModel?> FindRoleByIdAsync(string roleId)
|
||||||
|
{
|
||||||
|
var result = await roleManager.FindByIdAsync(roleId);
|
||||||
|
|
||||||
|
if (result is null) return null;
|
||||||
|
|
||||||
|
var roleModel = new RoleModel { Id = result.Id, Name = result.Name };
|
||||||
|
|
||||||
|
return roleModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RoleModel?> FindRoleByNameAsync(string roleName)
|
||||||
|
{
|
||||||
|
var result = await roleManager.FindByNameAsync(roleName);
|
||||||
|
|
||||||
|
if (result is null) return null;
|
||||||
|
|
||||||
|
var roleModel = new RoleModel { Id = result.Id, Name = result.Name };
|
||||||
|
|
||||||
|
return roleModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Users.Commands;
|
||||||
using Hutopy.Application.Users.Commands;
|
|
||||||
using Hutopy.Application.Users.Queries.GetMinimalUser;
|
using Hutopy.Application.Users.Queries.GetMinimalUser;
|
||||||
using Hutopy.Infrastructure.Identity;
|
using Hutopy.Infrastructure.Identity;
|
||||||
|
|
||||||
@@ -15,9 +14,8 @@ public class Users : EndpointGroupBase
|
|||||||
.MapIdentityApi<ApplicationUser>();
|
.MapIdentityApi<ApplicationUser>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Guid> CreateUser(ISender sender, CreateUserCommand command, IIdentityService identityService)
|
private static async Task<Guid> CreateUser(ISender sender, CreateUserCommand command)
|
||||||
{
|
{
|
||||||
await identityService.CreateUserAsync(command.EmailAddress, command.UserName, command.FirstName, command.LastName, command.Password);
|
|
||||||
return await sender.Send(command);
|
return await sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -711,6 +711,12 @@
|
|||||||
"$ref": "#/components/schemas/UserTransactionDto"
|
"$ref": "#/components/schemas/UserTransactionDto"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"userRoles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
"totalBalance": {
|
"totalBalance": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "decimal"
|
"format": "decimal"
|
||||||
|
|||||||
Reference in New Issue
Block a user