Add UserRole handling

This commit is contained in:
lumijiez
2025-06-10 22:10:16 +03:00
parent beaacffb9a
commit a25459d2cb
15 changed files with 92 additions and 143 deletions

View File

@@ -1,5 +1,6 @@
using Imprink.Domain.Repositories;
using Imprink.Domain.Repositories.Products;
using Imprink.Domain.Repositories.Users;
namespace Imprink.Application;

View File

@@ -0,0 +1,7 @@
namespace Imprink.Application.Users.Dtos;
public class RoleDto
{
public required Guid RoleId { get; set; }
public required string RoleName { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace Imprink.Application.Users.Dtos;
public class UserRoleDto
{
public required string UserId { get; set; }
public required Guid RoleId { get; set; }
}

View File

@@ -1,15 +1,22 @@
using Imprink.Domain.Entities.Users;
using Imprink.Application.Users.Dtos;
using MediatR;
namespace Imprink.Application.Users;
public record GetUserRolesCommand(string Sub) : IRequest<IEnumerable<Role>>;
public record GetUserRolesCommand(string Sub) : IRequest<IEnumerable<RoleDto>>;
public class GetUserRolesHandler(IUnitOfWork uw): IRequestHandler<GetUserRolesCommand, IEnumerable<Role>>
public class GetUserRolesHandler(IUnitOfWork uw): IRequestHandler<GetUserRolesCommand, IEnumerable<RoleDto>>
{
public async Task<IEnumerable<Role>> Handle(GetUserRolesCommand request, CancellationToken cancellationToken)
public async Task<IEnumerable<RoleDto>> Handle(GetUserRolesCommand request, CancellationToken cancellationToken)
{
if (await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return [];
return await uw.UserRoleRepository.GetUserRolesAsync(request.Sub, cancellationToken);;
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return [];
var roles = await uw.UserRoleRepository.GetUserRolesAsync(request.Sub, cancellationToken);
return roles.Select(role => new RoleDto
{
RoleId = role.Id,
RoleName = role.RoleName
}).ToList();
}
}

View File

@@ -1,39 +0,0 @@
using Imprink.Domain.Entities.Users;
using Imprink.Domain.Repositories;
using MediatR;
namespace Imprink.Application.Users.Roles;
public record AddUserToRoleCommand(string UserId, Guid RoleId) : IRequest<bool>;
public class AddUserToRoleCommandHandler(
IUserRoleRepository userRoleRepository,
IRoleRepository roleRepository,
IUserRepository userRepository)
: IRequestHandler<AddUserToRoleCommand, bool>
{
public async Task<bool> Handle(AddUserToRoleCommand request, CancellationToken cancellationToken)
{
var userExists = await userRepository.UserExistsAsync(request.UserId, cancellationToken);
if (!userExists)
return false;
var roleExists = await roleRepository.RoleExistsAsync(request.RoleId, cancellationToken);
if (!roleExists)
return false;
var isAlreadyInRole = await userRoleRepository.IsUserInRoleAsync(request.UserId, request.RoleId, cancellationToken);
if (isAlreadyInRole)
return true;
var userRole = new UserRole
{
UserId = request.UserId,
RoleId = request.RoleId
};
await userRoleRepository.AddUserRoleAsync(userRole, cancellationToken);
return true;
}
}

View File

@@ -1,32 +0,0 @@
using Imprink.Domain.Repositories;
using MediatR;
namespace Imprink.Application.Users.Roles;
public record RemoveUserFromRoleCommand(string UserId, Guid RoleId) : IRequest<bool>;
public class RemoveUserFromRoleCommandHandler(
IUserRoleRepository userRoleRepository,
IRoleRepository roleRepository,
IUserRepository userRepository)
: IRequestHandler<RemoveUserFromRoleCommand, bool>
{
public async Task<bool> Handle(RemoveUserFromRoleCommand request, CancellationToken cancellationToken)
{
var userExists = await userRepository.UserExistsAsync(request.UserId, cancellationToken);
if (!userExists)
return false;
var roleExists = await roleRepository.RoleExistsAsync(request.RoleId, cancellationToken);
if (!roleExists)
return false;
var userRole = await userRoleRepository.GetUserRoleAsync(request.UserId, request.RoleId, cancellationToken);
if (userRole == null)
return true;
await userRoleRepository.RemoveUserRoleAsync(userRole, cancellationToken);
return true;
}
}

View File

@@ -0,0 +1,29 @@
using Imprink.Application.Users.Dtos;
using Imprink.Domain.Entities.Users;
using MediatR;
namespace Imprink.Application.Users;
public record SetUserRoleCommand(string Sub, Guid RoleId) : IRequest<UserRoleDto?>;
public class SetUserRoleHandler(IUnitOfWork uw) : IRequestHandler<SetUserRoleCommand, UserRoleDto?>
{
public async Task<UserRoleDto?> Handle(SetUserRoleCommand request, CancellationToken cancellationToken)
{
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return null;
var userRole = new UserRole
{
UserId = request.Sub,
RoleId = request.RoleId
};
var addedRole = await uw.UserRoleRepository.AddUserRoleAsync(userRole, cancellationToken);
return new UserRoleDto
{
UserId = addedRole.UserId,
RoleId = addedRole.RoleId
};
}
}

View File

@@ -1,15 +1,13 @@
using Imprink.Domain.Entities.Users;
namespace Imprink.Domain.Repositories;
namespace Imprink.Domain.Repositories.Users;
public interface IUserRoleRepository
{
Task<IEnumerable<Role>> GetUserRolesAsync(string userId, CancellationToken cancellationToken = default);
Task<IEnumerable<User>> GetUsersInRoleAsync(Guid roleId, CancellationToken cancellationToken = default);
Task<bool> IsUserInRoleAsync(string userId, Guid roleId, CancellationToken cancellationToken = default);
Task<UserRole?> GetUserRoleAsync(string userId, Guid roleId, CancellationToken cancellationToken = default);
Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default);
Task RemoveUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default);
Task<IEnumerable<UserRole>> GetUserRolesByUserIdAsync(string userId, CancellationToken cancellationToken = default);
Task<UserRole> AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default);
Task<UserRole> RemoveUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default);
}

View File

@@ -1,5 +1,6 @@
using Imprink.Domain.Entities.Users;
using Imprink.Domain.Repositories;
using Imprink.Domain.Repositories.Users;
using Imprink.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
@@ -25,12 +26,6 @@ public class UserRoleRepository(ApplicationDbContext context) : IUserRoleReposit
.ToListAsync(cancellationToken);
}
public async Task<bool> IsUserInRoleAsync(string userId, Guid roleId, CancellationToken cancellationToken = default)
{
return await context.UserRole
.AnyAsync(ur => ur.UserId == userId && ur.RoleId == roleId, cancellationToken);
}
public async Task<UserRole?> GetUserRoleAsync(string userId, Guid roleId, CancellationToken cancellationToken = default)
{
return await context.UserRole
@@ -38,23 +33,17 @@ public class UserRoleRepository(ApplicationDbContext context) : IUserRoleReposit
.FirstOrDefaultAsync(ur => ur.UserId == userId && ur.RoleId == roleId, cancellationToken);
}
public async Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default)
public async Task<UserRole> AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default)
{
context.UserRole.Add(userRole);
var ur = context.UserRole.Add(userRole);
await context.SaveChangesAsync(cancellationToken);
return ur.Entity;
}
public async Task RemoveUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default)
public async Task<UserRole> RemoveUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default)
{
context.UserRole.Remove(userRole);
var ur = context.UserRole.Remove(userRole);
await context.SaveChangesAsync(cancellationToken);
}
public async Task<IEnumerable<UserRole>> GetUserRolesByUserIdAsync(string userId, CancellationToken cancellationToken = default)
{
return await context.UserRole
.AsNoTracking()
.Where(ur => ur.UserId == userId)
.ToListAsync(cancellationToken);
return ur.Entity;
}
}

View File

@@ -1,6 +1,7 @@
using Imprink.Application;
using Imprink.Domain.Repositories;
using Imprink.Domain.Repositories.Products;
using Imprink.Domain.Repositories.Users;
using Imprink.Infrastructure.Database;
namespace Imprink.Infrastructure;

View File

@@ -1,6 +1,7 @@
using System.Security.Claims;
using Imprink.Application.Users;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Imprink.WebApi.Controllers.Users;
@@ -9,7 +10,7 @@ namespace Imprink.WebApi.Controllers.Users;
[Route("/api/users/roles")]
public class UserRoleController(IMediator mediator) : ControllerBase
{
//[Authorize]
[Authorize]
[HttpGet("me")]
public async Task<IActionResult> GetMyRoles()
{
@@ -20,4 +21,16 @@ public class UserRoleController(IMediator mediator) : ControllerBase
return Ok(myRoles);
}
[Authorize(Roles = "Admin")]
[HttpPost("set")]
public async Task<IActionResult> SetUserRole(SetUserRoleCommand command)
{
var userRole = await mediator.Send(command);
if (userRole == null)
return BadRequest();
return Ok(userRole);
}
}

View File

@@ -5,6 +5,7 @@ using Imprink.Application.Products.Create;
using Imprink.Application.Validation.Models;
using Imprink.Domain.Repositories;
using Imprink.Domain.Repositories.Products;
using Imprink.Domain.Repositories.Users;
using Imprink.Infrastructure;
using Imprink.Infrastructure.Database;
using Imprink.Infrastructure.Repositories.Products;
@@ -74,6 +75,7 @@ public static class Startup
foreach (var role in roles) identity!.AddClaim(new Claim(ClaimTypes.Role, role));
identity!.AddClaim(new Claim(ClaimTypes.Role, "User"));
return Task.CompletedTask;
}
};
@@ -99,8 +101,9 @@ public static class Startup
Description = "JWT Authorization header using the Bearer scheme.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
@@ -112,12 +115,9 @@ public static class Startup
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "Bearer",
Name = "Bearer",
In = ParameterLocation.Header
}
},
new List<string>()
[]
}
});
});

View File

@@ -10,13 +10,13 @@ export default function Home() {
const fetchAccessToken = async () => {
if (user) {
try {
const v = await fetch('/token');
await fetch('/token');
} catch (error) {
console.error("Error fetching token");
}
} else {
try {
const resp = await fetch('/untoken');
await fetch('/untoken');
} catch (e) {
console.error('Error in /api/untoken:', e);
}

View File

@@ -1,4 +1,3 @@
import {cookies, headers} from 'next/headers';
import { NextResponse } from 'next/server';
import {auth0} from "@/lib/auth0";
import api from "@/lib/api";
@@ -9,20 +8,11 @@ export async function GET() {
if (!token) { return NextResponse.json({ error: 'No access token found' }, { status: 401 }); }
(await cookies()).set('access_token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
domain: process.env.COOKIE_DOMAIN,
maxAge: 3600,
});
await api.post('/users/sync', {}, {
headers: { Cookie: `access_token=${token}` }
});
return NextResponse.json({ message: 'Access token set in cookie' });
return NextResponse.json({ access_token: token });
} catch (error) {
console.error('Error in /api/token:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });

View File

@@ -1,22 +0,0 @@
import {cookies} from 'next/headers';
import {NextResponse} from 'next/server';
export async function GET() {
try {
(await cookies()).set({
name: 'access_token',
value: '',
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/',
domain: process.env.COOKIE_DOMAIN,
maxAge: -1,
});
return NextResponse.json({message: 'Deleted access token'});
} catch (error) {
console.error('Error in /api/untoken:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}