feat(backend): Implement complete RBAC system (Day 5 Phase 2)
Implemented Role-Based Access Control (RBAC) with 5 tenant-level roles following Clean Architecture principles. Changes: - Created TenantRole enum (TenantOwner, TenantAdmin, TenantMember, TenantGuest, AIAgent) - Created UserTenantRole entity with repository pattern - Updated JWT service to include role claims (tenant_role, role) - Updated RegisterTenant to auto-assign TenantOwner role - Updated Login to query and include user role in JWT - Updated RefreshToken to preserve role claims - Added authorization policies in Program.cs (RequireTenantOwner, RequireTenantAdmin, etc.) - Updated /api/auth/me endpoint to return role information - Created EF Core migration for user_tenant_roles table - Applied database migration successfully Database: - New table: identity.user_tenant_roles - Columns: id, user_id, tenant_id, role, assigned_at, assigned_by_user_id - Indexes: user_id, tenant_id, role, unique(user_id, tenant_id) - Foreign keys: CASCADE on user and tenant deletion Testing: - Created test-rbac.ps1 PowerShell script - All RBAC tests passing - JWT tokens contain role claims - Role persists across login and token refresh Documentation: - DAY5-PHASE2-RBAC-IMPLEMENTATION-SUMMARY.md with complete implementation details 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ public class JwtService : IJwtService
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public string GenerateToken(User user, Tenant tenant)
|
||||
public string GenerateToken(User user, Tenant tenant, TenantRole tenantRole)
|
||||
{
|
||||
var securityKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("JWT SecretKey not configured")));
|
||||
@@ -36,7 +36,10 @@ public class JwtService : IJwtService
|
||||
new("tenant_plan", tenant.Plan.ToString()),
|
||||
new("full_name", user.FullName.Value),
|
||||
new("auth_provider", user.AuthProvider.ToString()),
|
||||
new(ClaimTypes.Role, "User") // TODO: Implement real roles
|
||||
|
||||
// Role claims (both standard and custom)
|
||||
new("tenant_role", tenantRole.ToString()), // Custom claim for application logic
|
||||
new(ClaimTypes.Role, tenantRole.ToString()) // Standard ASP.NET Core role claim
|
||||
};
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
|
||||
@@ -14,6 +14,7 @@ public class RefreshTokenService : IRefreshTokenService
|
||||
private readonly IRefreshTokenRepository _refreshTokenRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ITenantRepository _tenantRepository;
|
||||
private readonly IUserTenantRoleRepository _userTenantRoleRepository;
|
||||
private readonly IJwtService _jwtService;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<RefreshTokenService> _logger;
|
||||
@@ -22,6 +23,7 @@ public class RefreshTokenService : IRefreshTokenService
|
||||
IRefreshTokenRepository refreshTokenRepository,
|
||||
IUserRepository userRepository,
|
||||
ITenantRepository tenantRepository,
|
||||
IUserTenantRoleRepository userTenantRoleRepository,
|
||||
IJwtService jwtService,
|
||||
IConfiguration configuration,
|
||||
ILogger<RefreshTokenService> logger)
|
||||
@@ -29,6 +31,7 @@ public class RefreshTokenService : IRefreshTokenService
|
||||
_refreshTokenRepository = refreshTokenRepository;
|
||||
_userRepository = userRepository;
|
||||
_tenantRepository = tenantRepository;
|
||||
_userTenantRoleRepository = userTenantRoleRepository;
|
||||
_jwtService = jwtService;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
@@ -127,8 +130,20 @@ public class RefreshTokenService : IRefreshTokenService
|
||||
throw new UnauthorizedAccessException("Tenant not found or inactive");
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
var newAccessToken = _jwtService.GenerateToken(user, tenant);
|
||||
// Get user's tenant role
|
||||
var userTenantRole = await _userTenantRoleRepository.GetByUserAndTenantAsync(
|
||||
user.Id,
|
||||
tenant.Id,
|
||||
cancellationToken);
|
||||
|
||||
if (userTenantRole == null)
|
||||
{
|
||||
_logger.LogWarning("User {UserId} has no role assigned for tenant {TenantId}", user.Id, tenant.Id);
|
||||
throw new UnauthorizedAccessException("User role not found");
|
||||
}
|
||||
|
||||
// Generate new access token with role
|
||||
var newAccessToken = _jwtService.GenerateToken(user, tenant, userTenantRole.Role);
|
||||
|
||||
// Generate new refresh token (token rotation)
|
||||
var newRefreshToken = await GenerateRefreshTokenAsync(user, ipAddress, userAgent, cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user