1189 lines
34 KiB
Markdown
1189 lines
34 KiB
Markdown
# ColaFlow Day 6 Planning Document
|
|
|
|
**Date**: 2025-11-03
|
|
**Prepared By**: Product Manager Agent
|
|
**Sprint**: M1 Sprint 2 - Enterprise-Grade Multi-Tenancy & SSO
|
|
**Status**: Planning Complete - Ready for Implementation
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
After analyzing the project's current state (Day 1-5 complete: JWT authentication, Refresh Token, RBAC with 5 roles, and integration tests), I recommend **Role Management API** as the Day 6 priority. This choice is strategically optimal because:
|
|
|
|
1. **Immediate Business Value**: Enables tenant administrators to invite and manage users (critical for multi-tenant SaaS)
|
|
2. **Technical Foundation**: Completes the authentication/authorization loop started in Days 4-5
|
|
3. **MCP Readiness**: Establishes role assignment patterns needed for AI agents in M2
|
|
4. **Realistic Scope**: Can be completed in 6-8 hours with full test coverage
|
|
5. **Low Risk**: Builds on existing RBAC system (no new architectural decisions)
|
|
|
|
**Recommended Scope for Day 6**: Role Management API (Option 1)
|
|
|
|
---
|
|
|
|
## Day 6 Priority Analysis
|
|
|
|
### Priority Matrix
|
|
|
|
| Option | Business Value | Technical Complexity | MCP Dependency | Risk Level | Time Estimate | Priority Ranking |
|
|
|--------|---------------|---------------------|----------------|-----------|---------------|------------------|
|
|
| **Option 1: Role Management API** | **HIGH** | **MEDIUM** | **MEDIUM** | **LOW** | **6-8 hours** | **1st (RECOMMENDED)** |
|
|
| Option 2: Email Verification | MEDIUM | MEDIUM | LOW | MEDIUM | 8-10 hours | 2nd |
|
|
| Option 3: Password Reset | MEDIUM | MEDIUM | LOW | MEDIUM | 6-8 hours | 3rd |
|
|
| Option 4: Project-Level Roles | HIGH | HIGH | HIGH | HIGH | 10-12 hours | 4th (Defer to Day 7) |
|
|
| Option 5: User Invitations | HIGH | HIGH | MEDIUM | MEDIUM | 10-12 hours | 5th (Combine with Option 1) |
|
|
|
|
---
|
|
|
|
### Option 1: Role Management API (RECOMMENDED)
|
|
|
|
**Priority**: P0 (Must have for Day 6)
|
|
|
|
#### Why This is the Best Choice
|
|
|
|
**1. Business Value (HIGH)**
|
|
- Completes the tenant management loop: Registration → Login → **User Management**
|
|
- Enables self-service user onboarding (critical for SaaS adoption)
|
|
- Reduces customer support burden (tenant admins manage their own users)
|
|
- Foundation for enterprise features (team management, access control)
|
|
|
|
**2. Technical Dependencies (Satisfied)**
|
|
- ✅ RBAC system already implemented (Day 5 Phase 2)
|
|
- ✅ JWT includes role claims
|
|
- ✅ Authorization policies configured
|
|
- ✅ `user_tenant_roles` table exists
|
|
- ✅ `IUserTenantRoleRepository` implemented
|
|
- **Zero new infrastructure needed** - just add API endpoints
|
|
|
|
**3. MCP Integration Readiness (MEDIUM)**
|
|
- Establishes pattern for AI agent role assignment
|
|
- Preview workflow for role changes (human approval)
|
|
- Audit logging for compliance
|
|
- Future: AI can suggest role assignments based on user activity
|
|
|
|
**4. Risk Assessment (LOW)**
|
|
- No database migrations required (schema already exists)
|
|
- No new architectural patterns (reuses existing RBAC)
|
|
- Clear acceptance criteria (CRUD operations)
|
|
- Well-understood domain (role assignment is standard functionality)
|
|
|
|
**5. Development Effort (6-8 hours)**
|
|
- Application Layer Commands: 3 hours
|
|
- API Controllers: 2 hours
|
|
- Integration Tests: 2-3 hours
|
|
- Documentation: 1 hour
|
|
|
|
#### What Gets Delivered
|
|
|
|
**API Endpoints**:
|
|
1. `POST /api/tenants/{tenantId}/users/{userId}/role` - Assign/Update role
|
|
2. `DELETE /api/tenants/{tenantId}/users/{userId}/role` - Remove user from tenant
|
|
3. `GET /api/tenants/{tenantId}/users` - List all users with roles
|
|
4. `GET /api/tenants/{tenantId}/roles` - Get available roles
|
|
|
|
**Security Features**:
|
|
- Only `TenantOwner` can assign `TenantOwner` role
|
|
- `TenantAdmin` can manage `TenantMember` and `TenantGuest` roles
|
|
- Users cannot modify their own roles
|
|
- Role changes logged for audit
|
|
|
|
**Test Coverage**:
|
|
- 15+ integration tests covering all scenarios
|
|
- Security test cases (unauthorized access, privilege escalation)
|
|
- Edge cases (invalid role, non-existent user, cross-tenant access)
|
|
|
|
---
|
|
|
|
### Option 2: Email Verification (2nd Priority)
|
|
|
|
**Priority**: P1 (Nice to have for Day 6, Can defer to Day 7)
|
|
|
|
#### Analysis
|
|
|
|
**Business Value (MEDIUM)**
|
|
- Reduces spam registrations
|
|
- Validates user email addresses
|
|
- Standard security practice
|
|
- Required for password reset flow
|
|
|
|
**Technical Complexity (MEDIUM)**
|
|
- Requires email service integration (SendGrid or SMTP)
|
|
- Verification token generation and storage
|
|
- Email template design
|
|
- Token expiration handling
|
|
|
|
**Why Defer to Day 7**:
|
|
- **Blocking dependency**: Needs email service setup (adds complexity)
|
|
- **Not critical for MVP**: Can launch with unverified emails initially
|
|
- **Better as Day 7**: Combine with Password Reset (both need email service)
|
|
|
|
**Time Estimate**: 8-10 hours
|
|
- Email service integration: 3 hours
|
|
- Token generation: 2 hours
|
|
- API endpoints: 2 hours
|
|
- Email templates: 2 hours
|
|
- Testing: 2-3 hours
|
|
|
|
---
|
|
|
|
### Option 3: Password Reset (3rd Priority)
|
|
|
|
**Priority**: P1 (Defer to Day 7)
|
|
|
|
#### Analysis
|
|
|
|
**Business Value (MEDIUM)**
|
|
- Essential user experience feature
|
|
- Reduces support tickets
|
|
- Security best practice
|
|
|
|
**Technical Complexity (MEDIUM)**
|
|
- Shares email infrastructure with Email Verification
|
|
- Password reset token generation
|
|
- Secure token validation
|
|
- Rate limiting to prevent abuse
|
|
|
|
**Why Defer to Day 7**:
|
|
- **Synergy with Option 2**: Both need email service
|
|
- **Day 7 recommendation**: Implement Email Verification + Password Reset together
|
|
- **Better user flow**: Users expect both features together
|
|
|
|
**Time Estimate**: 6-8 hours (if email service already configured)
|
|
|
|
---
|
|
|
|
### Option 4: Project-Level Roles (4th Priority)
|
|
|
|
**Priority**: P1 (Defer to Day 7-8)
|
|
|
|
#### Analysis
|
|
|
|
**Business Value (HIGH)**
|
|
- Critical for M1 core project module
|
|
- Enables fine-grained access control (per-project permissions)
|
|
- Foundation for collaborative project management
|
|
- Required before implementing Projects CRUD
|
|
|
|
**Technical Complexity (HIGH)**
|
|
- New database table: `user_project_roles`
|
|
- New enum: `ProjectRole` (4 roles)
|
|
- Role inheritance logic (TenantOwner → ProjectOwner)
|
|
- Multi-level authorization (tenant + project)
|
|
- Complex query logic (check both tenant and project roles)
|
|
|
|
**Why Defer to Day 7-8**:
|
|
- **High complexity**: Needs 10-12 hours for proper implementation
|
|
- **Architectural decisions**: Role inheritance rules need careful design
|
|
- **Dependency on M1**: Projects module implementation should come first
|
|
- **Better timing**: After user management is stable
|
|
|
|
**MCP Dependency (HIGH)**:
|
|
- AI agents need project-level permissions for MCP
|
|
- Preview workflow operates at project level
|
|
- Critical for M2 MCP Server
|
|
|
|
**Time Estimate**: 10-12 hours
|
|
- Design role inheritance: 2 hours
|
|
- Domain layer: 2 hours
|
|
- Infrastructure layer: 2 hours
|
|
- Authorization policies: 2 hours
|
|
- API endpoints: 2 hours
|
|
- Testing: 3-4 hours
|
|
|
|
---
|
|
|
|
### Option 5: User Invitation Management (5th Priority)
|
|
|
|
**Priority**: P1 (Combine with Option 1)
|
|
|
|
#### Analysis
|
|
|
|
**Business Value (HIGH)**
|
|
- Essential for team collaboration
|
|
- Reduces friction in user onboarding
|
|
- Allows users to invite colleagues
|
|
- Token-based secure invitation flow
|
|
|
|
**Technical Complexity (HIGH)**
|
|
- New database table: `user_invitations`
|
|
- Invitation token generation
|
|
- Email invitation (depends on email service)
|
|
- Invitation acceptance workflow
|
|
- Invitation expiration and resend
|
|
|
|
**Why This is Complex**:
|
|
- **Email dependency**: Needs email service (same as Option 2)
|
|
- **New workflow**: Invitation → Email → Accept → User created
|
|
- **State management**: Pending, Accepted, Expired, Revoked
|
|
- **Security**: Prevent invitation abuse (rate limiting)
|
|
|
|
**Recommendation**:
|
|
- **Day 6**: Implement basic role assignment (Option 1)
|
|
- **Day 8-9**: Add invitation system after email service is configured
|
|
|
|
**Time Estimate**: 10-12 hours (full invitation flow)
|
|
|
|
---
|
|
|
|
## Day 6 Recommended Scope
|
|
|
|
### Core Feature: Role Management API
|
|
|
|
**Goal**: Enable tenant administrators to manage user roles within their tenant.
|
|
|
|
**Deliverables**:
|
|
1. ✅ Application Layer: Commands for role management
|
|
2. ✅ API Layer: RESTful endpoints for role operations
|
|
3. ✅ Authorization: Policy-based role assignment rules
|
|
4. ✅ Integration Tests: Full test coverage (15+ tests)
|
|
5. ✅ Documentation: API documentation and usage examples
|
|
|
|
**Success Criteria**:
|
|
- TenantOwner can assign any role to any user in tenant
|
|
- TenantAdmin can assign Member/Guest roles (not Owner/Admin)
|
|
- Users cannot modify their own roles
|
|
- All operations logged for audit
|
|
- 100% test pass rate
|
|
- No security vulnerabilities (privilege escalation, cross-tenant access)
|
|
|
|
---
|
|
|
|
## Detailed Requirements: Role Management API
|
|
|
|
### User Stories
|
|
|
|
**US-1: Assign Role to User**
|
|
- **As a** TenantOwner
|
|
- **I want to** assign a role to a user in my tenant
|
|
- **So that** I can control their access level to resources
|
|
|
|
**US-2: Update User Role**
|
|
- **As a** TenantOwner
|
|
- **I want to** change a user's role
|
|
- **So that** I can adjust their permissions as their responsibilities change
|
|
|
|
**US-3: Remove User from Tenant**
|
|
- **As a** TenantOwner
|
|
- **I want to** remove a user from my tenant
|
|
- **So that** I can revoke their access when they leave the organization
|
|
|
|
**US-4: View All Users and Roles**
|
|
- **As a** TenantAdmin
|
|
- **I want to** see all users in my tenant and their assigned roles
|
|
- **So that** I can understand the current access control state
|
|
|
|
**US-5: Prevent Privilege Escalation**
|
|
- **As a** system
|
|
- **I want to** prevent users from elevating their own privileges
|
|
- **So that** security is maintained
|
|
|
|
---
|
|
|
|
### Acceptance Criteria
|
|
|
|
#### AC-RM-1: Assign Role to User (POST /api/tenants/{tenantId}/users/{userId}/role)
|
|
|
|
**Given**: I am authenticated as TenantOwner of "tenant-a"
|
|
**When**: I assign role "TenantMember" to user "user-123"
|
|
**Then**:
|
|
- User "user-123" is assigned "TenantMember" role in "tenant-a"
|
|
- Response returns 200 OK with user details and new role
|
|
- New JWT issued on next login includes the updated role
|
|
- Audit log entry created: "TenantOwner assigned TenantMember to user-123"
|
|
|
|
**Edge Cases**:
|
|
- ❌ Cannot assign role to user in different tenant (403 Forbidden)
|
|
- ❌ Cannot assign role if not TenantOwner or TenantAdmin (403 Forbidden)
|
|
- ❌ Cannot assign TenantOwner role unless requester is TenantOwner (403 Forbidden)
|
|
- ❌ Cannot assign invalid role (400 Bad Request)
|
|
- ❌ Cannot assign role to non-existent user (404 Not Found)
|
|
|
|
---
|
|
|
|
#### AC-RM-2: Update User Role (PUT /api/tenants/{tenantId}/users/{userId}/role)
|
|
|
|
**Given**: User "user-456" currently has role "TenantGuest"
|
|
**When**: TenantOwner updates their role to "TenantMember"
|
|
**Then**:
|
|
- User's role changes from "TenantGuest" to "TenantMember"
|
|
- Response returns 200 OK with updated role
|
|
- Audit log entry created: "TenantOwner changed user-456 role from TenantGuest to TenantMember"
|
|
|
|
**Edge Cases**:
|
|
- ❌ Cannot update own role (403 Forbidden)
|
|
- ❌ TenantAdmin cannot promote user to TenantOwner (403 Forbidden)
|
|
- ❌ Cannot downgrade last TenantOwner (400 Bad Request - "At least one TenantOwner required")
|
|
|
|
---
|
|
|
|
#### AC-RM-3: Remove User from Tenant (DELETE /api/tenants/{tenantId}/users/{userId}/role)
|
|
|
|
**Given**: User "user-789" exists in "tenant-a"
|
|
**When**: TenantOwner removes user "user-789"
|
|
**Then**:
|
|
- User's role entry deleted from `user_tenant_roles` table
|
|
- User can no longer access tenant resources
|
|
- Response returns 204 No Content
|
|
- Audit log entry created: "TenantOwner removed user-789 from tenant"
|
|
|
|
**Edge Cases**:
|
|
- ❌ Cannot remove last TenantOwner (400 Bad Request)
|
|
- ❌ Cannot remove self (403 Forbidden)
|
|
- ❌ TenantAdmin cannot remove TenantOwner (403 Forbidden)
|
|
|
|
---
|
|
|
|
#### AC-RM-4: List Tenant Users (GET /api/tenants/{tenantId}/users)
|
|
|
|
**Given**: I am authenticated as TenantAdmin
|
|
**When**: I request user list for my tenant
|
|
**Then**:
|
|
- Response returns 200 OK with array of users
|
|
- Each user includes: userId, email, fullName, role, status, createdAt
|
|
- Pagination supported: `?page=1&pageSize=20`
|
|
- Filtering supported: `?role=TenantMember&status=Active`
|
|
|
|
**Response Format**:
|
|
```json
|
|
{
|
|
"users": [
|
|
{
|
|
"userId": "uuid",
|
|
"email": "user@example.com",
|
|
"fullName": "John Doe",
|
|
"role": "TenantMember",
|
|
"status": "Active",
|
|
"authProvider": "Local",
|
|
"createdAt": "2025-11-01T10:00:00Z",
|
|
"assignedAt": "2025-11-01T10:00:00Z"
|
|
}
|
|
],
|
|
"totalCount": 15,
|
|
"page": 1,
|
|
"pageSize": 20
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### AC-RM-5: Get Available Roles (GET /api/tenants/{tenantId}/roles)
|
|
|
|
**Given**: I am authenticated as TenantAdmin
|
|
**When**: I request available roles
|
|
**Then**:
|
|
- Response returns 200 OK with array of roles
|
|
- Each role includes: name, description, permissions summary
|
|
|
|
**Response Format**:
|
|
```json
|
|
{
|
|
"roles": [
|
|
{
|
|
"name": "TenantOwner",
|
|
"description": "Full control over tenant",
|
|
"canAssign": true
|
|
},
|
|
{
|
|
"name": "TenantAdmin",
|
|
"description": "Manage users and projects",
|
|
"canAssign": true
|
|
},
|
|
{
|
|
"name": "TenantMember",
|
|
"description": "Create and manage own projects",
|
|
"canAssign": true
|
|
},
|
|
{
|
|
"name": "TenantGuest",
|
|
"description": "Read-only access",
|
|
"canAssign": true
|
|
},
|
|
{
|
|
"name": "AIAgent",
|
|
"description": "AI agent for MCP operations",
|
|
"canAssign": false
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Authorization Rules
|
|
|
|
| Requester Role | Can Assign Roles | Cannot Assign | Special Rules |
|
|
|---------------|-----------------|---------------|---------------|
|
|
| **TenantOwner** | All roles | - | Can assign TenantOwner |
|
|
| **TenantAdmin** | TenantMember, TenantGuest | TenantOwner, TenantAdmin | Cannot elevate privileges |
|
|
| **TenantMember** | None | All | No role management |
|
|
| **TenantGuest** | None | All | No role management |
|
|
| **AIAgent** | None | All | No role management |
|
|
|
|
**Global Rules**:
|
|
1. Cannot modify own role
|
|
2. Cannot remove last TenantOwner
|
|
3. Cannot assign role to user in different tenant
|
|
4. AIAgent role can only be assigned via API key generation (not this endpoint)
|
|
|
|
---
|
|
|
|
## API Design
|
|
|
|
### 1. Assign/Update Role
|
|
|
|
**Endpoint**: `POST /api/tenants/{tenantId}/users/{userId}/role`
|
|
|
|
**Request**:
|
|
```json
|
|
{
|
|
"role": "TenantMember"
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"userId": "uuid",
|
|
"email": "user@example.com",
|
|
"fullName": "John Doe",
|
|
"role": "TenantMember",
|
|
"assignedAt": "2025-11-03T14:30:00Z",
|
|
"assignedBy": "admin-uuid"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `400 Bad Request` - Invalid role or business rule violation
|
|
- `403 Forbidden` - Insufficient permissions
|
|
- `404 Not Found` - User or tenant not found
|
|
|
|
---
|
|
|
|
### 2. Remove User from Tenant
|
|
|
|
**Endpoint**: `DELETE /api/tenants/{tenantId}/users/{userId}/role`
|
|
|
|
**Response** (204 No Content)
|
|
|
|
**Errors**:
|
|
- `400 Bad Request` - Cannot remove last TenantOwner
|
|
- `403 Forbidden` - Insufficient permissions or self-removal
|
|
- `404 Not Found` - User or tenant not found
|
|
|
|
---
|
|
|
|
### 3. List Tenant Users
|
|
|
|
**Endpoint**: `GET /api/tenants/{tenantId}/users`
|
|
|
|
**Query Parameters**:
|
|
- `page` (default: 1)
|
|
- `pageSize` (default: 20, max: 100)
|
|
- `role` (filter by role: TenantOwner, TenantAdmin, TenantMember, TenantGuest)
|
|
- `status` (filter by status: Active, Inactive)
|
|
- `search` (search by email or full name)
|
|
|
|
**Response** (200 OK): See AC-RM-4 above
|
|
|
|
---
|
|
|
|
### 4. Get Available Roles
|
|
|
|
**Endpoint**: `GET /api/tenants/{tenantId}/roles`
|
|
|
|
**Response** (200 OK): See AC-RM-5 above
|
|
|
|
---
|
|
|
|
## Database Schema
|
|
|
|
**Table**: `identity.user_tenant_roles` (Already exists from Day 5)
|
|
|
|
**No new tables or migrations required**. Existing schema:
|
|
|
|
```sql
|
|
CREATE TABLE identity.user_tenant_roles (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID NOT NULL,
|
|
tenant_id UUID NOT NULL,
|
|
role VARCHAR(50) NOT NULL,
|
|
assigned_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
assigned_by_user_id UUID NULL,
|
|
|
|
CONSTRAINT FK_user_tenant_roles_users
|
|
FOREIGN KEY (user_id) REFERENCES identity.users(id) ON DELETE CASCADE,
|
|
CONSTRAINT FK_user_tenant_roles_tenants
|
|
FOREIGN KEY (tenant_id) REFERENCES identity.tenants(id) ON DELETE CASCADE,
|
|
CONSTRAINT UQ_user_tenant_role
|
|
UNIQUE (user_id, tenant_id)
|
|
);
|
|
|
|
CREATE INDEX ix_user_tenant_roles_user_id ON identity.user_tenant_roles(user_id);
|
|
CREATE INDEX ix_user_tenant_roles_tenant_id ON identity.user_tenant_roles(tenant_id);
|
|
CREATE INDEX ix_user_tenant_roles_role ON identity.user_tenant_roles(role);
|
|
```
|
|
|
|
**Existing Repository Methods** (Already implemented):
|
|
- `GetByUserAndTenantAsync(userId, tenantId)` ✅
|
|
- `GetByTenantAsync(tenantId)` ✅
|
|
- `AddAsync()` ✅
|
|
- `UpdateAsync()` ✅
|
|
- `DeleteAsync()` ✅
|
|
|
|
**New Repository Method Needed** (Add to `IUserTenantRoleRepository`):
|
|
```csharp
|
|
Task<int> CountByTenantAndRoleAsync(Guid tenantId, TenantRole role, CancellationToken cancellationToken = default);
|
|
```
|
|
- **Purpose**: Check if at least one TenantOwner exists before removal
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Application Layer (2-3 hours)
|
|
|
|
#### 1.1 Create Commands (1 hour)
|
|
|
|
**File**: `src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/AssignRole/AssignRoleCommand.cs`
|
|
```csharp
|
|
public record AssignRoleCommand(
|
|
Guid TenantId,
|
|
Guid UserId,
|
|
TenantRole Role,
|
|
Guid AssignedByUserId
|
|
) : IRequest<AssignRoleResult>;
|
|
|
|
public record AssignRoleResult(
|
|
Guid UserId,
|
|
string Email,
|
|
string FullName,
|
|
TenantRole Role,
|
|
DateTime AssignedAt,
|
|
Guid AssignedBy
|
|
);
|
|
```
|
|
|
|
**File**: `src/Modules/Identity/ColaFlow.Modules.Identity.Application/Commands/RemoveUserFromTenant/RemoveUserFromTenantCommand.cs`
|
|
```csharp
|
|
public record RemoveUserFromTenantCommand(
|
|
Guid TenantId,
|
|
Guid UserId,
|
|
Guid RemovedByUserId
|
|
) : IRequest<Unit>;
|
|
```
|
|
|
|
#### 1.2 Create Command Handlers (2 hours)
|
|
|
|
**File**: `AssignRoleCommandHandler.cs`
|
|
- Validate user exists in tenant
|
|
- Validate requester has permission to assign role
|
|
- Check business rules (cannot assign TenantOwner unless requester is TenantOwner)
|
|
- Create or update `UserTenantRole` entity
|
|
- Save to database
|
|
- Return result
|
|
|
|
**File**: `RemoveUserFromTenantCommandHandler.cs`
|
|
- Validate user exists in tenant
|
|
- Validate requester has permission to remove
|
|
- Check business rules (cannot remove last TenantOwner, cannot remove self)
|
|
- Delete `UserTenantRole` entity
|
|
- Save to database
|
|
|
|
#### 1.3 Create Queries (1 hour)
|
|
|
|
**File**: `GetTenantUsersQuery.cs`
|
|
```csharp
|
|
public record GetTenantUsersQuery(
|
|
Guid TenantId,
|
|
int Page = 1,
|
|
int PageSize = 20,
|
|
TenantRole? RoleFilter = null,
|
|
UserStatus? StatusFilter = null,
|
|
string? SearchTerm = null
|
|
) : IRequest<GetTenantUsersResult>;
|
|
|
|
public record GetTenantUsersResult(
|
|
List<TenantUserDto> Users,
|
|
int TotalCount,
|
|
int Page,
|
|
int PageSize
|
|
);
|
|
|
|
public record TenantUserDto(
|
|
Guid UserId,
|
|
string Email,
|
|
string FullName,
|
|
TenantRole Role,
|
|
UserStatus Status,
|
|
AuthenticationProvider AuthProvider,
|
|
DateTime CreatedAt,
|
|
DateTime AssignedAt
|
|
);
|
|
```
|
|
|
|
**File**: `GetTenantUsersQueryHandler.cs`
|
|
- Query users by tenant
|
|
- Include role information
|
|
- Apply filters (role, status, search)
|
|
- Paginate results
|
|
- Return DTO
|
|
|
|
---
|
|
|
|
### Phase 2: API Layer (2 hours)
|
|
|
|
**File**: `src/ColaFlow.API/Controllers/TenantManagementController.cs`
|
|
|
|
```csharp
|
|
[ApiController]
|
|
[Route("api/tenants")]
|
|
[Authorize]
|
|
public class TenantManagementController : ControllerBase
|
|
{
|
|
private readonly IMediator _mediator;
|
|
|
|
[HttpPost("{tenantId}/users/{userId}/role")]
|
|
[Authorize(Policy = "RequireTenantAdmin")]
|
|
public async Task<IActionResult> AssignRole(
|
|
Guid tenantId,
|
|
Guid userId,
|
|
[FromBody] AssignRoleRequest request)
|
|
{
|
|
var currentUserId = Guid.Parse(User.FindFirst("user_id")!.Value);
|
|
|
|
var command = new AssignRoleCommand(
|
|
tenantId,
|
|
userId,
|
|
request.Role,
|
|
currentUserId
|
|
);
|
|
|
|
var result = await _mediator.Send(command);
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpDelete("{tenantId}/users/{userId}/role")]
|
|
[Authorize(Policy = "RequireTenantAdmin")]
|
|
public async Task<IActionResult> RemoveUserFromTenant(
|
|
Guid tenantId,
|
|
Guid userId)
|
|
{
|
|
var currentUserId = Guid.Parse(User.FindFirst("user_id")!.Value);
|
|
|
|
var command = new RemoveUserFromTenantCommand(
|
|
tenantId,
|
|
userId,
|
|
currentUserId
|
|
);
|
|
|
|
await _mediator.Send(command);
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpGet("{tenantId}/users")]
|
|
[Authorize(Policy = "RequireTenantMember")]
|
|
public async Task<IActionResult> GetTenantUsers(
|
|
Guid tenantId,
|
|
[FromQuery] int page = 1,
|
|
[FromQuery] int pageSize = 20,
|
|
[FromQuery] TenantRole? role = null,
|
|
[FromQuery] UserStatus? status = null,
|
|
[FromQuery] string? search = null)
|
|
{
|
|
var query = new GetTenantUsersQuery(
|
|
tenantId,
|
|
page,
|
|
pageSize,
|
|
role,
|
|
status,
|
|
search
|
|
);
|
|
|
|
var result = await _mediator.Send(query);
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpGet("{tenantId}/roles")]
|
|
[Authorize(Policy = "RequireTenantMember")]
|
|
public IActionResult GetAvailableRoles(Guid tenantId)
|
|
{
|
|
var currentUserRole = User.FindFirst("tenant_role")?.Value;
|
|
var canAssignOwner = currentUserRole == "TenantOwner";
|
|
|
|
var roles = new[]
|
|
{
|
|
new { Name = "TenantOwner", Description = "Full control", CanAssign = canAssignOwner },
|
|
new { Name = "TenantAdmin", Description = "Manage users", CanAssign = canAssignOwner },
|
|
new { Name = "TenantMember", Description = "Manage own projects", CanAssign = true },
|
|
new { Name = "TenantGuest", Description = "Read-only", CanAssign = true },
|
|
new { Name = "AIAgent", Description = "AI operations", CanAssign = false }
|
|
};
|
|
|
|
return Ok(new { roles });
|
|
}
|
|
}
|
|
```
|
|
|
|
**Request DTOs**:
|
|
```csharp
|
|
public record AssignRoleRequest(TenantRole Role);
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 3: Integration Tests (2-3 hours)
|
|
|
|
**File**: `tests/ColaFlow.API.IntegrationTests/Controllers/TenantManagementControllerTests.cs`
|
|
|
|
**Test Cases** (15+ tests):
|
|
1. `AssignRole_AsTenantOwner_ReturnsSuccess`
|
|
2. `AssignRole_AsTenantAdmin_CanAssignMemberRole`
|
|
3. `AssignRole_AsTenantAdmin_CannotAssignOwnerRole`
|
|
4. `AssignRole_ToSelf_ReturnsForbidden`
|
|
5. `AssignRole_ToCrossTenantUser_ReturnsForbidden`
|
|
6. `AssignRole_WithInvalidRole_ReturnsBadRequest`
|
|
7. `AssignRole_ToNonExistentUser_ReturnsNotFound`
|
|
8. `UpdateRole_ChangesUserRole_ReturnsSuccess`
|
|
9. `RemoveUser_AsTenantOwner_ReturnsSuccess`
|
|
10. `RemoveUser_LastTenantOwner_ReturnsBadRequest`
|
|
11. `RemoveUser_Self_ReturnsForbidden`
|
|
12. `GetTenantUsers_ReturnsAllUsers`
|
|
13. `GetTenantUsers_WithRoleFilter_ReturnsFilteredUsers`
|
|
14. `GetTenantUsers_WithPagination_ReturnsCorrectPage`
|
|
15. `GetAvailableRoles_AsTenantOwner_ShowsAllRoles`
|
|
16. `GetAvailableRoles_AsTenantMember_HidesOwnerRole`
|
|
|
|
**Test Setup**:
|
|
- Use Testcontainers for PostgreSQL
|
|
- Seed test data (tenant, users, roles)
|
|
- Authenticate as different roles
|
|
- Cleanup after tests
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### Authentication & Authorization
|
|
|
|
1. **All endpoints require authentication** (`[Authorize]` attribute)
|
|
2. **Role-based authorization** (`[Authorize(Policy = "RequireTenantAdmin")]`)
|
|
3. **Tenant isolation**: Validate requester belongs to target tenant
|
|
4. **Claims validation**: Extract `user_id` and `tenant_id` from JWT
|
|
|
|
### Business Rules Enforcement
|
|
|
|
1. **Privilege escalation prevention**:
|
|
- TenantAdmin cannot assign TenantOwner role
|
|
- Users cannot modify own roles
|
|
|
|
2. **Data integrity**:
|
|
- At least one TenantOwner must exist per tenant
|
|
- Cannot remove self
|
|
|
|
3. **Cross-tenant protection**:
|
|
- Validate all operations within same tenant
|
|
- Database-level tenant isolation via foreign keys
|
|
|
|
### Audit Logging (Future Enhancement)
|
|
|
|
**Recommendation**: Add audit logging to track:
|
|
- Who assigned role
|
|
- What role was assigned
|
|
- When assignment occurred
|
|
- Previous role (for updates)
|
|
|
|
**Implementation** (Can be added in Day 7):
|
|
```csharp
|
|
await _auditLogger.LogAsync(new AuditEntry
|
|
{
|
|
Action = "AssignRole",
|
|
UserId = currentUserId,
|
|
TenantId = tenantId,
|
|
TargetUserId = userId,
|
|
OldValue = oldRole?.ToString(),
|
|
NewValue = newRole.ToString(),
|
|
Timestamp = DateTime.UtcNow
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Test Plan
|
|
|
|
### Unit Tests (Application Layer)
|
|
|
|
**File**: `tests/ColaFlow.Modules.Identity.Application.Tests/Commands/AssignRoleCommandHandlerTests.cs`
|
|
|
|
**Test Scenarios**:
|
|
1. ✅ Valid role assignment
|
|
2. ✅ Update existing role
|
|
3. ❌ Assign role to non-existent user
|
|
4. ❌ Assign role without permission
|
|
5. ❌ Self-assignment
|
|
6. ❌ Cross-tenant assignment
|
|
|
|
**Expected Coverage**: 85%+
|
|
|
|
---
|
|
|
|
### Integration Tests (API Layer)
|
|
|
|
**Test Strategy**: Use Testcontainers for full E2E testing
|
|
|
|
**Setup**:
|
|
```csharp
|
|
public class TenantManagementControllerTests : IAsyncLifetime
|
|
{
|
|
private PostgreSqlContainer _dbContainer;
|
|
private WebApplicationFactory<Program> _factory;
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
_dbContainer = new PostgreSqlBuilder().Build();
|
|
await _dbContainer.StartAsync();
|
|
|
|
_factory = new WebApplicationFactory<Program>()
|
|
.WithWebHostBuilder(builder =>
|
|
{
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
// Replace DbContext with test container
|
|
});
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
**Test Cases**: See Phase 3 above (15+ tests)
|
|
|
|
---
|
|
|
|
### Manual Testing
|
|
|
|
**Scenario 1: Happy Path - Assign Role**
|
|
|
|
```powershell
|
|
# 1. Register tenant and get TenantOwner token
|
|
$registerBody = @{
|
|
tenantName = "Test Corp"
|
|
tenantSlug = "test-corp"
|
|
subscriptionPlan = "Professional"
|
|
adminEmail = "owner@test.com"
|
|
adminPassword = "Owner@1234"
|
|
adminFullName = "Tenant Owner"
|
|
} | ConvertTo-Json
|
|
|
|
$response = Invoke-RestMethod -Uri "http://localhost:5167/api/tenants/register" `
|
|
-Method Post -ContentType "application/json" -Body $registerBody
|
|
|
|
$ownerToken = $response.accessToken
|
|
$tenantId = $response.tenantId
|
|
|
|
# 2. Create another user (TenantMember by default)
|
|
$createUserBody = @{
|
|
email = "member@test.com"
|
|
password = "Member@1234"
|
|
fullName = "Test Member"
|
|
} | ConvertTo-Json
|
|
|
|
$headers = @{ "Authorization" = "Bearer $ownerToken" }
|
|
$newUser = Invoke-RestMethod -Uri "http://localhost:5167/api/tenants/$tenantId/users" `
|
|
-Method Post -ContentType "application/json" -Headers $headers -Body $createUserBody
|
|
|
|
$newUserId = $newUser.userId
|
|
|
|
# 3. Assign TenantAdmin role to new user
|
|
$assignRoleBody = @{ role = "TenantAdmin" } | ConvertTo-Json
|
|
|
|
$result = Invoke-RestMethod -Uri "http://localhost:5167/api/tenants/$tenantId/users/$newUserId/role" `
|
|
-Method Post -ContentType "application/json" -Headers $headers -Body $assignRoleBody
|
|
|
|
Write-Host "Role assigned: $($result.role)" # Should output: TenantAdmin
|
|
```
|
|
|
|
**Scenario 2: Security Test - Prevent Privilege Escalation**
|
|
|
|
```powershell
|
|
# 1. Login as TenantAdmin
|
|
$loginBody = @{
|
|
tenantSlug = "test-corp"
|
|
email = "admin@test.com"
|
|
password = "Admin@1234"
|
|
} | ConvertTo-Json
|
|
|
|
$loginResponse = Invoke-RestMethod -Uri "http://localhost:5167/api/auth/login" `
|
|
-Method Post -ContentType "application/json" -Body $loginBody
|
|
|
|
$adminToken = $loginResponse.accessToken
|
|
|
|
# 2. Try to assign TenantOwner role (should fail)
|
|
$headers = @{ "Authorization" = "Bearer $adminToken" }
|
|
$assignOwnerBody = @{ role = "TenantOwner" } | ConvertTo-Json
|
|
|
|
try {
|
|
Invoke-RestMethod -Uri "http://localhost:5167/api/tenants/$tenantId/users/$newUserId/role" `
|
|
-Method Post -ContentType "application/json" -Headers $headers -Body $assignOwnerBody
|
|
} catch {
|
|
Write-Host "Correctly blocked: $($_.Exception.Response.StatusCode)" # Should be 403 Forbidden
|
|
}
|
|
```
|
|
|
|
**Scenario 3: List Users**
|
|
|
|
```powershell
|
|
$headers = @{ "Authorization" = "Bearer $ownerToken" }
|
|
$users = Invoke-RestMethod -Uri "http://localhost:5167/api/tenants/$tenantId/users" `
|
|
-Headers $headers
|
|
|
|
$users.users | Format-Table email, role, status
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
### Database Queries
|
|
|
|
**Optimizations**:
|
|
1. **Index usage**: Queries use existing indexes on `user_id` and `tenant_id`
|
|
2. **Pagination**: Limit result sets with `LIMIT` and `OFFSET`
|
|
3. **Projection**: Return only needed fields (avoid `SELECT *`)
|
|
4. **Eager loading**: Use `Include()` for user details
|
|
|
|
**Expected Performance**:
|
|
- Assign role: < 50ms (1 INSERT or UPDATE)
|
|
- Remove role: < 30ms (1 DELETE)
|
|
- List users: < 100ms (1 SELECT with JOIN, up to 100 rows)
|
|
|
|
### Caching Strategy (Future)
|
|
|
|
**Recommendation** (Day 8+):
|
|
- Cache user roles in Redis (5-minute expiration)
|
|
- Invalidate cache on role change
|
|
- Reduces database queries for JWT validation
|
|
|
|
---
|
|
|
|
## Day 6 Timeline
|
|
|
|
**Total Estimated Time**: 6-8 hours
|
|
|
|
### Morning Session (4 hours)
|
|
|
|
- **09:00 - 10:00**: Design review and repository method addition
|
|
- **10:00 - 12:00**: Application layer implementation (commands, queries, handlers)
|
|
- **12:00 - 13:00**: Lunch break
|
|
|
|
### Afternoon Session (4 hours)
|
|
|
|
- **13:00 - 15:00**: API controller implementation and manual testing
|
|
- **15:00 - 17:00**: Integration tests (15+ tests)
|
|
- **17:00 - 18:00**: Documentation and code review
|
|
|
|
### End of Day Deliverables
|
|
|
|
- ✅ 4 API endpoints working
|
|
- ✅ 15+ integration tests passing
|
|
- ✅ Manual testing complete
|
|
- ✅ Documentation updated
|
|
- ✅ Code reviewed and merged
|
|
|
|
---
|
|
|
|
## Day 7-10 Roadmap
|
|
|
|
### Day 7: Email Service + Verification + Password Reset (8 hours)
|
|
|
|
**Why**: Combine email-related features in one day
|
|
|
|
**Deliverables**:
|
|
1. Email service integration (SendGrid or SMTP)
|
|
2. Email verification flow
|
|
3. Password reset flow
|
|
4. Email templates (verification, reset, welcome)
|
|
5. Integration tests
|
|
|
|
**Blockers**: None (independent feature)
|
|
|
|
---
|
|
|
|
### Day 8: Project-Level Roles + Audit Logging (8 hours)
|
|
|
|
**Why**: Foundation for M1 core project module
|
|
|
|
**Deliverables**:
|
|
1. `user_project_roles` table
|
|
2. `ProjectRole` enum (ProjectOwner, ProjectManager, ProjectMember, ProjectGuest)
|
|
3. Role inheritance logic (TenantOwner → ProjectOwner)
|
|
4. Authorization policies for project-level operations
|
|
5. Audit logging infrastructure
|
|
|
|
**Blockers**: None (builds on Day 6 role system)
|
|
|
|
---
|
|
|
|
### Day 9: M1 Core Project Module - Projects CRUD (8 hours)
|
|
|
|
**Why**: Complete M1.1 core features
|
|
|
|
**Deliverables**:
|
|
1. Projects CRUD API (already exists, needs multi-tenant update)
|
|
2. Add `tenant_id` to Projects table
|
|
3. Update EF Core global query filter for Projects
|
|
4. Project-level authorization (using Day 8 project roles)
|
|
5. Integration tests
|
|
|
|
**Blockers**: Depends on Day 8 (project-level roles)
|
|
|
|
---
|
|
|
|
### Day 10: Kanban Workflow + Sprint Management (8 hours)
|
|
|
|
**Why**: Complete M1 sprint management
|
|
|
|
**Deliverables**:
|
|
1. Sprint CRUD API
|
|
2. Kanban board state management
|
|
3. Drag-and-drop task status updates
|
|
4. Sprint burndown chart data
|
|
5. Integration tests
|
|
|
|
**Blockers**: Depends on Day 9 (Projects)
|
|
|
|
---
|
|
|
|
### Days 11-12: M2 MCP Server Foundation (16 hours)
|
|
|
|
**Why**: Begin M2 milestone - MCP integration
|
|
|
|
**Deliverables**:
|
|
1. MCP token generation API
|
|
2. MCP authentication middleware
|
|
3. Preview storage and approval workflow
|
|
4. Basic MCP resources (`projects.search`, `tasks.list`)
|
|
5. Basic MCP tools (`create_task`, `update_status`)
|
|
|
|
**Blockers**: Depends on Days 6-10 (complete RBAC and Projects)
|
|
|
|
---
|
|
|
|
## Risk Assessment
|
|
|
|
### Day 6 Risks
|
|
|
|
| Risk | Probability | Impact | Mitigation |
|
|
|------|------------|--------|------------|
|
|
| Complex authorization logic | MEDIUM | MEDIUM | Reuse existing policies from Day 5 |
|
|
| Edge case bugs | MEDIUM | LOW | Comprehensive test coverage (15+ tests) |
|
|
| Performance issues | LOW | LOW | Use existing indexes, no N+1 queries |
|
|
| Security vulnerabilities | LOW | HIGH | Thorough security testing, code review |
|
|
|
|
**Overall Risk**: **LOW** (well-understood requirements, existing infrastructure)
|
|
|
|
---
|
|
|
|
### Days 7-10 Risks
|
|
|
|
| Risk | Probability | Impact | Mitigation |
|
|
|------|------------|--------|------------|
|
|
| Email service integration delays | MEDIUM | MEDIUM | Use SMTP fallback if SendGrid fails |
|
|
| Project-level roles complexity | HIGH | HIGH | Start with simple design, iterate later |
|
|
| Scope creep (Days 11-12) | HIGH | HIGH | Strictly time-box Day 10, defer MCP to sprint 3 |
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
### Day 6 Success Criteria
|
|
|
|
- ✅ All 4 API endpoints functional
|
|
- ✅ 100% integration test pass rate (15+ tests)
|
|
- ✅ Zero security vulnerabilities found
|
|
- ✅ Manual testing complete
|
|
- ✅ Documentation updated
|
|
- ✅ Code reviewed by architect agent
|
|
- ✅ Deployed to development environment
|
|
- ✅ Ready for frontend integration
|
|
|
|
### KPIs (Key Performance Indicators)
|
|
|
|
- **Development Time**: ≤ 8 hours
|
|
- **Test Coverage**: ≥ 85%
|
|
- **API Response Time**: < 200ms (p95)
|
|
- **Bug Count**: 0 critical, ≤ 2 minor
|
|
- **Code Quality**: No SonarQube violations
|
|
|
|
---
|
|
|
|
## Alternative Approaches Considered
|
|
|
|
### Alternative 1: User Invitation System First
|
|
|
|
**Pros**:
|
|
- More complete user onboarding flow
|
|
- Better user experience
|
|
|
|
**Cons**:
|
|
- Requires email service (adds complexity)
|
|
- 10-12 hours implementation time (exceeds Day 6 budget)
|
|
- Cannot test without email
|
|
|
|
**Decision**: Defer to Day 8-9 after email service is ready
|
|
|
|
---
|
|
|
|
### Alternative 2: Combine Role Management + Email Verification
|
|
|
|
**Pros**:
|
|
- More complete feature set in Day 6
|
|
|
|
**Cons**:
|
|
- 14-16 hours total (too much for one day)
|
|
- Email service setup adds risk
|
|
- Testing complexity increases
|
|
|
|
**Decision**: Split into Day 6 (roles) + Day 7 (email)
|
|
|
|
---
|
|
|
|
### Alternative 3: Project-Level Roles First
|
|
|
|
**Pros**:
|
|
- More aligned with M1 core project module
|
|
- Needed for MCP integration
|
|
|
|
**Cons**:
|
|
- High complexity (10-12 hours)
|
|
- Requires architectural decisions (role inheritance)
|
|
- No immediate business value without Projects module
|
|
|
|
**Decision**: Defer to Day 8 after tenant-level roles are stable
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
**Day 6 Recommendation**: Implement **Role Management API** (Option 1)
|
|
|
|
**Rationale**:
|
|
1. ✅ **Feasible**: Can be completed in 6-8 hours
|
|
2. ✅ **High value**: Completes tenant management loop
|
|
3. ✅ **Low risk**: Builds on existing RBAC infrastructure
|
|
4. ✅ **Well-defined**: Clear acceptance criteria and test cases
|
|
5. ✅ **Foundation**: Prepares for project-level roles (Day 8) and MCP (M2)
|
|
|
|
**Next Steps**:
|
|
1. Review and approve this planning document
|
|
2. Assign to backend agent for implementation
|
|
3. Track progress with hourly check-ins
|
|
4. Code review before end of day
|
|
5. Deploy to development environment
|
|
|
|
---
|
|
|
|
**Document Status**: ✅ Planning Complete - Ready for Implementation
|
|
|
|
**Prepared By**: Product Manager Agent
|
|
**Date**: 2025-11-03
|
|
**Version**: 1.0
|