34 KiB
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:
- Immediate Business Value: Enables tenant administrators to invite and manage users (critical for multi-tenant SaaS)
- Technical Foundation: Completes the authentication/authorization loop started in Days 4-5
- MCP Readiness: Establishes role assignment patterns needed for AI agents in M2
- Realistic Scope: Can be completed in 6-8 hours with full test coverage
- 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_rolestable exists - ✅
IUserTenantRoleRepositoryimplemented - 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:
POST /api/tenants/{tenantId}/users/{userId}/role- Assign/Update roleDELETE /api/tenants/{tenantId}/users/{userId}/role- Remove user from tenantGET /api/tenants/{tenantId}/users- List all users with rolesGET /api/tenants/{tenantId}/roles- Get available roles
Security Features:
- Only
TenantOwnercan assignTenantOwnerrole TenantAdmincan manageTenantMemberandTenantGuestroles- 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:
- ✅ Application Layer: Commands for role management
- ✅ API Layer: RESTful endpoints for role operations
- ✅ Authorization: Policy-based role assignment rules
- ✅ Integration Tests: Full test coverage (15+ tests)
- ✅ 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_rolestable - 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:
{
"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:
{
"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:
- Cannot modify own role
- Cannot remove last TenantOwner
- Cannot assign role to user in different tenant
- 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:
{
"role": "TenantMember"
}
Response (200 OK):
{
"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 violation403 Forbidden- Insufficient permissions404 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 TenantOwner403 Forbidden- Insufficient permissions or self-removal404 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:
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):
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
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
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
UserTenantRoleentity - 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
UserTenantRoleentity - Save to database
1.3 Create Queries (1 hour)
File: GetTenantUsersQuery.cs
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
[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:
public record AssignRoleRequest(TenantRole Role);
Phase 3: Integration Tests (2-3 hours)
File: tests/ColaFlow.API.IntegrationTests/Controllers/TenantManagementControllerTests.cs
Test Cases (15+ tests):
AssignRole_AsTenantOwner_ReturnsSuccessAssignRole_AsTenantAdmin_CanAssignMemberRoleAssignRole_AsTenantAdmin_CannotAssignOwnerRoleAssignRole_ToSelf_ReturnsForbiddenAssignRole_ToCrossTenantUser_ReturnsForbiddenAssignRole_WithInvalidRole_ReturnsBadRequestAssignRole_ToNonExistentUser_ReturnsNotFoundUpdateRole_ChangesUserRole_ReturnsSuccessRemoveUser_AsTenantOwner_ReturnsSuccessRemoveUser_LastTenantOwner_ReturnsBadRequestRemoveUser_Self_ReturnsForbiddenGetTenantUsers_ReturnsAllUsersGetTenantUsers_WithRoleFilter_ReturnsFilteredUsersGetTenantUsers_WithPagination_ReturnsCorrectPageGetAvailableRoles_AsTenantOwner_ShowsAllRolesGetAvailableRoles_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
- All endpoints require authentication (
[Authorize]attribute) - Role-based authorization (
[Authorize(Policy = "RequireTenantAdmin")]) - Tenant isolation: Validate requester belongs to target tenant
- Claims validation: Extract
user_idandtenant_idfrom JWT
Business Rules Enforcement
-
Privilege escalation prevention:
- TenantAdmin cannot assign TenantOwner role
- Users cannot modify own roles
-
Data integrity:
- At least one TenantOwner must exist per tenant
- Cannot remove self
-
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):
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:
- ✅ Valid role assignment
- ✅ Update existing role
- ❌ Assign role to non-existent user
- ❌ Assign role without permission
- ❌ Self-assignment
- ❌ Cross-tenant assignment
Expected Coverage: 85%+
Integration Tests (API Layer)
Test Strategy: Use Testcontainers for full E2E testing
Setup:
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
# 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
# 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
$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:
- Index usage: Queries use existing indexes on
user_idandtenant_id - Pagination: Limit result sets with
LIMITandOFFSET - Projection: Return only needed fields (avoid
SELECT *) - 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:
- Email service integration (SendGrid or SMTP)
- Email verification flow
- Password reset flow
- Email templates (verification, reset, welcome)
- Integration tests
Blockers: None (independent feature)
Day 8: Project-Level Roles + Audit Logging (8 hours)
Why: Foundation for M1 core project module
Deliverables:
user_project_rolestableProjectRoleenum (ProjectOwner, ProjectManager, ProjectMember, ProjectGuest)- Role inheritance logic (TenantOwner → ProjectOwner)
- Authorization policies for project-level operations
- 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:
- Projects CRUD API (already exists, needs multi-tenant update)
- Add
tenant_idto Projects table - Update EF Core global query filter for Projects
- Project-level authorization (using Day 8 project roles)
- Integration tests
Blockers: Depends on Day 8 (project-level roles)
Day 10: Kanban Workflow + Sprint Management (8 hours)
Why: Complete M1 sprint management
Deliverables:
- Sprint CRUD API
- Kanban board state management
- Drag-and-drop task status updates
- Sprint burndown chart data
- Integration tests
Blockers: Depends on Day 9 (Projects)
Days 11-12: M2 MCP Server Foundation (16 hours)
Why: Begin M2 milestone - MCP integration
Deliverables:
- MCP token generation API
- MCP authentication middleware
- Preview storage and approval workflow
- Basic MCP resources (
projects.search,tasks.list) - 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:
- ✅ Feasible: Can be completed in 6-8 hours
- ✅ High value: Completes tenant management loop
- ✅ Low risk: Builds on existing RBAC infrastructure
- ✅ Well-defined: Clear acceptance criteria and test cases
- ✅ Foundation: Prepares for project-level roles (Day 8) and MCP (M2)
Next Steps:
- Review and approve this planning document
- Assign to backend agent for implementation
- Track progress with hourly check-ins
- Code review before end of day
- Deploy to development environment
Document Status: ✅ Planning Complete - Ready for Implementation
Prepared By: Product Manager Agent Date: 2025-11-03 Version: 1.0