Implemented domain event raising in command handlers to enable audit logging and event-driven architecture for key Identity module operations. Changes: - Updated LoginCommand to include IpAddress and UserAgent fields for audit trail - Updated AuthController to extract and pass IP address and user agent from HTTP context - Modified LoginCommandHandler to raise UserLoggedInEvent on successful login - Updated AssignUserRoleCommand to include AssignedBy field for audit purposes - Modified AssignUserRoleCommandHandler to raise UserRoleAssignedEvent with previous role tracking - Updated RemoveUserFromTenantCommand to include RemovedBy and Reason fields - Modified RemoveUserFromTenantCommandHandler to raise UserRemovedFromTenantEvent before deletion - Added domain methods to User aggregate: RecordLoginWithEvent, RaiseRoleAssignedEvent, RaiseRemovedFromTenantEvent - Updated TenantUsersController to extract current user ID from JWT claims and pass to commands Technical Details: - All event raising follows aggregate root encapsulation pattern - Domain events are persisted through repository UpdateAsync calls - Event handlers will automatically log these events for audit trail - Maintains backward compatibility with existing login flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
85 lines
3.1 KiB
C#
85 lines
3.1 KiB
C#
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
|
|
using ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events;
|
|
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
|
|
using ColaFlow.Modules.Identity.Domain.Repositories;
|
|
using MediatR;
|
|
|
|
namespace ColaFlow.Modules.Identity.Application.Commands.AssignUserRole;
|
|
|
|
public class AssignUserRoleCommandHandler : IRequestHandler<AssignUserRoleCommand, Unit>
|
|
{
|
|
private readonly IUserTenantRoleRepository _userTenantRoleRepository;
|
|
private readonly IUserRepository _userRepository;
|
|
private readonly ITenantRepository _tenantRepository;
|
|
|
|
public AssignUserRoleCommandHandler(
|
|
IUserTenantRoleRepository userTenantRoleRepository,
|
|
IUserRepository userRepository,
|
|
ITenantRepository tenantRepository)
|
|
{
|
|
_userTenantRoleRepository = userTenantRoleRepository;
|
|
_userRepository = userRepository;
|
|
_tenantRepository = tenantRepository;
|
|
}
|
|
|
|
public async Task<Unit> Handle(AssignUserRoleCommand request, CancellationToken cancellationToken)
|
|
{
|
|
// Validate user exists
|
|
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
|
|
if (user == null)
|
|
throw new InvalidOperationException("User not found");
|
|
|
|
// Validate tenant exists
|
|
var tenant = await _tenantRepository.GetByIdAsync(TenantId.Create(request.TenantId), cancellationToken);
|
|
if (tenant == null)
|
|
throw new InvalidOperationException("Tenant not found");
|
|
|
|
// Parse and validate role
|
|
if (!Enum.TryParse<TenantRole>(request.Role, out var role))
|
|
throw new ArgumentException($"Invalid role: {request.Role}");
|
|
|
|
// Prevent manual assignment of AIAgent role
|
|
if (role == TenantRole.AIAgent)
|
|
throw new InvalidOperationException("AIAgent role cannot be assigned manually");
|
|
|
|
// Check if user already has a role in this tenant
|
|
var existingRole = await _userTenantRoleRepository.GetByUserAndTenantAsync(
|
|
request.UserId,
|
|
request.TenantId,
|
|
cancellationToken);
|
|
|
|
TenantRole? previousRole = existingRole?.Role;
|
|
|
|
if (existingRole != null)
|
|
{
|
|
// Update existing role
|
|
existingRole.UpdateRole(role, request.AssignedBy);
|
|
await _userTenantRoleRepository.UpdateAsync(existingRole, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
// Create new role assignment
|
|
var userTenantRole = UserTenantRole.Create(
|
|
UserId.Create(request.UserId),
|
|
TenantId.Create(request.TenantId),
|
|
role,
|
|
UserId.Create(request.AssignedBy));
|
|
|
|
await _userTenantRoleRepository.AddAsync(userTenantRole, cancellationToken);
|
|
}
|
|
|
|
// Raise domain event for role assignment
|
|
user.RaiseRoleAssignedEvent(
|
|
TenantId.Create(request.TenantId),
|
|
role,
|
|
previousRole,
|
|
request.AssignedBy
|
|
);
|
|
|
|
// Save changes to persist event
|
|
await _userRepository.UpdateAsync(user, cancellationToken);
|
|
|
|
return Unit.Value;
|
|
}
|
|
}
|