Files
ColaFlow/reports/2025-11-03-Day-6-Planning-Document.md
Yaojia Wang 32a25b3b35 In progress
2025-11-03 20:02:41 +01:00

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:

  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)

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)


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:

{
  "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:

  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:

{
  "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 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:

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 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

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):

  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):

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:

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:

  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