Files
ColaFlow/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommandHandler.cs
Yaojia Wang 5c541ddb79 feat(backend): Activate domain events for user login, role assignment, and tenant removal
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>
2025-11-03 20:41:22 +01:00

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;
}
}