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:
@@ -0,0 +1,33 @@
|
||||
namespace ColaFlow.Modules.Identity.Domain.Aggregates.Users;
|
||||
|
||||
/// <summary>
|
||||
/// Defines tenant-level roles for users
|
||||
/// </summary>
|
||||
public enum TenantRole
|
||||
{
|
||||
/// <summary>
|
||||
/// Tenant owner - Full control over tenant, billing, and all resources
|
||||
/// </summary>
|
||||
TenantOwner = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Tenant administrator - Can manage users, projects, but not billing
|
||||
/// </summary>
|
||||
TenantAdmin = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Tenant member - Default role, can create and manage own projects
|
||||
/// </summary>
|
||||
TenantMember = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Tenant guest - Read-only access to assigned resources
|
||||
/// </summary>
|
||||
TenantGuest = 4,
|
||||
|
||||
/// <summary>
|
||||
/// AI Agent - Read access + Write with preview (requires human approval)
|
||||
/// Special role for MCP integration
|
||||
/// </summary>
|
||||
AIAgent = 5
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using ColaFlow.Shared.Kernel.Common;
|
||||
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
|
||||
|
||||
namespace ColaFlow.Modules.Identity.Domain.Aggregates.Users;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a user's role within a specific tenant
|
||||
/// </summary>
|
||||
public sealed class UserTenantRole : Entity
|
||||
{
|
||||
public UserId UserId { get; private set; } = null!;
|
||||
public TenantId TenantId { get; private set; } = null!;
|
||||
public TenantRole Role { get; private set; }
|
||||
|
||||
public DateTime AssignedAt { get; private set; }
|
||||
public Guid? AssignedByUserId { get; private set; }
|
||||
|
||||
// Navigation properties (optional, for EF Core)
|
||||
public User User { get; private set; } = null!;
|
||||
public Tenant Tenant { get; private set; } = null!;
|
||||
|
||||
// Private constructor for EF Core
|
||||
private UserTenantRole() : base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory method to create a user-tenant-role assignment
|
||||
/// </summary>
|
||||
public static UserTenantRole Create(
|
||||
UserId userId,
|
||||
TenantId tenantId,
|
||||
TenantRole role,
|
||||
Guid? assignedByUserId = null)
|
||||
{
|
||||
return new UserTenantRole
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
TenantId = tenantId,
|
||||
Role = role,
|
||||
AssignedAt = DateTime.UtcNow,
|
||||
AssignedByUserId = assignedByUserId
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the user's role (e.g., promote Member to Admin)
|
||||
/// </summary>
|
||||
public void UpdateRole(TenantRole newRole, Guid updatedByUserId)
|
||||
{
|
||||
if (Role == newRole)
|
||||
return;
|
||||
|
||||
Role = newRole;
|
||||
AssignedByUserId = updatedByUserId;
|
||||
// Note: AssignedAt is NOT updated to preserve original assignment timestamp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if user has permission (extensible for future fine-grained permissions)
|
||||
/// </summary>
|
||||
public bool HasPermission(string permission)
|
||||
{
|
||||
// Future implementation: Check permission against role-permission mapping
|
||||
// For now, this is a placeholder for fine-grained permission checks
|
||||
return Role switch
|
||||
{
|
||||
TenantRole.TenantOwner => true, // Owner has all permissions
|
||||
TenantRole.AIAgent when permission.StartsWith("read") => true,
|
||||
TenantRole.AIAgent when permission.StartsWith("write_preview") => true,
|
||||
_ => false // Implement specific permission checks as needed
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
|
||||
|
||||
namespace ColaFlow.Modules.Identity.Domain.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for managing user-tenant-role assignments
|
||||
/// </summary>
|
||||
public interface IUserTenantRoleRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Get user's role for a specific tenant
|
||||
/// </summary>
|
||||
Task<UserTenantRole?> GetByUserAndTenantAsync(
|
||||
Guid userId,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get all roles for a specific user (across all tenants)
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<UserTenantRole>> GetByUserAsync(
|
||||
Guid userId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get all user-role assignments for a specific tenant
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<UserTenantRole>> GetByTenantAsync(
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new user-tenant-role assignment
|
||||
/// </summary>
|
||||
Task AddAsync(UserTenantRole role, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Update an existing user-tenant-role assignment
|
||||
/// </summary>
|
||||
Task UpdateAsync(UserTenantRole role, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a user-tenant-role assignment (remove user from tenant)
|
||||
/// </summary>
|
||||
Task DeleteAsync(UserTenantRole role, CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user