From 5c541ddb79eaacde84229cc2d8cafbce16d8a7f1 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Mon, 3 Nov 2025 20:41:22 +0100 Subject: [PATCH] feat(backend): Activate domain events for user login, role assignment, and tenant removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../Controllers/AuthController.cs | 19 +++++++++++++++- .../Controllers/TenantUsersController.cs | 18 +++++++++++++-- .../AssignUserRole/AssignUserRoleCommand.cs | 3 ++- .../AssignUserRoleCommandHandler.cs | 18 +++++++++++++-- .../Commands/Login/LoginCommand.cs | 4 +++- .../Commands/Login/LoginCommandHandler.cs | 15 +++++++++---- .../RemoveUserFromTenantCommand.cs | 4 +++- .../RemoveUserFromTenantCommandHandler.cs | 22 +++++++++++++++++-- .../Aggregates/Users/User.cs | 18 +++++++++++++++ 9 files changed, 107 insertions(+), 14 deletions(-) diff --git a/colaflow-api/src/ColaFlow.API/Controllers/AuthController.cs b/colaflow-api/src/ColaFlow.API/Controllers/AuthController.cs index 6b845e3..06f2ac2 100644 --- a/colaflow-api/src/ColaFlow.API/Controllers/AuthController.cs +++ b/colaflow-api/src/ColaFlow.API/Controllers/AuthController.cs @@ -31,8 +31,19 @@ public class AuthController : ControllerBase /// Login with email and password /// [HttpPost("login")] - public async Task Login([FromBody] LoginCommand command) + public async Task Login([FromBody] LoginRequest request) { + var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); + var userAgent = HttpContext.Request.Headers["User-Agent"].ToString(); + + var command = new LoginCommand( + request.TenantSlug, + request.Email, + request.Password, + ipAddress, + userAgent + ); + var result = await _mediator.Send(command); return Ok(result); } @@ -148,3 +159,9 @@ public class AuthController : ControllerBase } } } + +public record LoginRequest( + string TenantSlug, + string Email, + string Password +); diff --git a/colaflow-api/src/ColaFlow.API/Controllers/TenantUsersController.cs b/colaflow-api/src/ColaFlow.API/Controllers/TenantUsersController.cs index 86ffb6d..4a1975b 100644 --- a/colaflow-api/src/ColaFlow.API/Controllers/TenantUsersController.cs +++ b/colaflow-api/src/ColaFlow.API/Controllers/TenantUsersController.cs @@ -64,7 +64,14 @@ public class TenantUsersController : ControllerBase if (userTenantId != tenantId) return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" }); - var command = new AssignUserRoleCommand(tenantId, userId, request.Role); + // Extract current user ID from claims + var currentUserIdClaim = User.FindFirst("user_id")?.Value; + if (currentUserIdClaim == null) + return Unauthorized(new { error = "User ID not found in token" }); + + var currentUserId = Guid.Parse(currentUserIdClaim); + + var command = new AssignUserRoleCommand(tenantId, userId, request.Role, currentUserId); await _mediator.Send(command); return Ok(new { Message = "Role assigned successfully" }); } @@ -87,7 +94,14 @@ public class TenantUsersController : ControllerBase if (userTenantId != tenantId) return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" }); - var command = new RemoveUserFromTenantCommand(tenantId, userId); + // Extract current user ID from claims + var currentUserIdClaim = User.FindFirst("user_id")?.Value; + if (currentUserIdClaim == null) + return Unauthorized(new { error = "User ID not found in token" }); + + var currentUserId = Guid.Parse(currentUserIdClaim); + + var command = new RemoveUserFromTenantCommand(tenantId, userId, currentUserId, null); await _mediator.Send(command); return Ok(new { Message = "User removed from tenant successfully" }); } diff --git a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommand.cs b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommand.cs index b0fac0a..01b40a8 100644 --- a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommand.cs +++ b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommand.cs @@ -5,4 +5,5 @@ namespace ColaFlow.Modules.Identity.Application.Commands.AssignUserRole; public record AssignUserRoleCommand( Guid TenantId, Guid UserId, - string Role) : IRequest; + string Role, + Guid AssignedBy) : IRequest; diff --git a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommandHandler.cs b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommandHandler.cs index c5a7b7c..003892d 100644 --- a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommandHandler.cs +++ b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignUserRole/AssignUserRoleCommandHandler.cs @@ -1,4 +1,5 @@ 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; @@ -47,10 +48,12 @@ public class AssignUserRoleCommandHandler : IRequestHandler; diff --git a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/Login/LoginCommandHandler.cs b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/Login/LoginCommandHandler.cs index 956422c..2e01a89 100644 --- a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/Login/LoginCommandHandler.cs +++ b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/Login/LoginCommandHandler.cs @@ -2,6 +2,7 @@ using ColaFlow.Modules.Identity.Application.Dtos; using ColaFlow.Modules.Identity.Application.Services; using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants; using ColaFlow.Modules.Identity.Domain.Aggregates.Users; +using ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events; using ColaFlow.Modules.Identity.Domain.Repositories; using MediatR; @@ -67,18 +68,24 @@ public class LoginCommandHandler : IRequestHandler; + Guid UserId, + Guid RemovedBy, + string? Reason) : IRequest; diff --git a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/RemoveUserFromTenant/RemoveUserFromTenantCommandHandler.cs b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/RemoveUserFromTenant/RemoveUserFromTenantCommandHandler.cs index e06c178..0042746 100644 --- a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/RemoveUserFromTenant/RemoveUserFromTenantCommandHandler.cs +++ b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/RemoveUserFromTenant/RemoveUserFromTenantCommandHandler.cs @@ -1,4 +1,6 @@ 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; @@ -8,13 +10,16 @@ public class RemoveUserFromTenantCommandHandler : IRequestHandler Handle(RemoveUserFromTenantCommand request, CancellationToken cancellationToken) @@ -42,7 +47,7 @@ public class RemoveUserFromTenantCommandHandler : IRequestHandler !t.RevokedAt.HasValue)) { - token.Revoke("User removed from tenant"); + token.Revoke(request.Reason ?? "User removed from tenant"); } if (userTokens.Any()) @@ -50,6 +55,19 @@ public class RemoveUserFromTenantCommandHandler : IRequestHandler