Files
ColaFlow/docs/stories/sprint_5/story_5_7.md
Yaojia Wang 48a8431e4f feat(backend): Implement MCP Protocol Handler (Story 5.1)
Implemented JSON-RPC 2.0 protocol handler for MCP communication, enabling AI agents to communicate with ColaFlow using the Model Context Protocol.

**Implementation:**
- JSON-RPC 2.0 data models (Request, Response, Error, ErrorCode)
- MCP protocol models (Initialize, Capabilities, ClientInfo, ServerInfo)
- McpProtocolHandler with method routing and error handling
- Method handlers: initialize, resources/list, tools/list, tools/call
- ASP.NET Core middleware for /mcp endpoint
- Service registration and dependency injection setup

**Testing:**
- 28 unit tests covering protocol parsing, validation, and error handling
- Integration tests for initialize handshake and error responses
- All tests passing with >80% coverage

**Changes:**
- Created ColaFlow.Modules.Mcp.Contracts project
- Created ColaFlow.Modules.Mcp.Domain project
- Created ColaFlow.Modules.Mcp.Application project
- Created ColaFlow.Modules.Mcp.Infrastructure project
- Created ColaFlow.Modules.Mcp.Tests project
- Registered MCP module in ColaFlow.API Program.cs
- Added /mcp endpoint via middleware

**Acceptance Criteria Met:**
 JSON-RPC 2.0 messages correctly parsed
 Request validation (jsonrpc: "2.0", method, params, id)
 Error responses conform to JSON-RPC 2.0 spec
 Invalid requests return proper error codes (-32700, -32600, -32601, -32602)
 MCP initialize method implemented
 Server capabilities returned (resources, tools, prompts)
 Protocol version negotiation works (1.0)
 Request routing to method handlers
 Unit test coverage > 80%
 All tests passing

**Story**: docs/stories/sprint_5/story_5_1.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 19:38:34 +01:00

8.2 KiB

story_id, sprint_id, phase, status, priority, story_points, assignee, estimated_days, created_date, dependencies
story_id sprint_id phase status priority story_points assignee estimated_days created_date dependencies
story_5_7 sprint_5 Phase 2 - Resources not_started P0 5 backend 2 2025-11-06
story_5_5
story_5_2

Story 5.7: Multi-Tenant Isolation Verification

Phase: Phase 2 - Resources (Week 3-4) Priority: P0 CRITICAL Estimated Effort: 5 Story Points (2 days)

User Story

As a Security Engineer I want 100% multi-tenant data isolation for all MCP operations So that AI agents cannot access data from other tenants

Business Value

Multi-tenant security is CRITICAL for M2. A single data leak could:

  • Violate customer trust
  • Cause legal liability (GDPR, SOC2)
  • Damage brand reputation
  • Block enterprise adoption

This Story ensures zero cross-tenant data access.

Acceptance Criteria

AC1: TenantContext Service

  • Extract TenantId from API Key
  • Set CurrentTenantId in request context
  • Accessible to all MCP operations

AC2: Global Query Filters

  • EF Core Global Query Filters applied to all entities
  • All queries automatically filtered by TenantId
  • Cannot be disabled by accident

AC3: Repository Level Isolation

  • All repositories enforce TenantId filter
  • No queries bypass tenant check
  • Aggregate roots include TenantId property

AC4: Integration Tests

  • Create 2 test tenants (Tenant A, Tenant B)
  • Create test data in each tenant
  • Test 1: Tenant A API Key cannot read Tenant B projects
  • Test 2: Tenant A API Key cannot read Tenant B issues
  • Test 3: Tenant A API Key cannot read Tenant B users
  • Test 4: Direct ID access fails for other tenant's data
  • Test 5: Search queries never return cross-tenant results
  • All tests must pass (100% isolation)

AC5: Security Audit

  • Code review by security team
  • Static analysis (no raw SQL without TenantId)
  • Verify all API endpoints enforce tenant filter

Technical Design

TenantContext Service

public interface ITenantContext
{
    Guid CurrentTenantId { get; }
}

public class McpTenantContext : ITenantContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public Guid CurrentTenantId
    {
        get
        {
            var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"];
            if (tenantId == null)
                throw new McpUnauthorizedException("Tenant context not set");

            return (Guid)tenantId;
        }
    }
}

Global Query Filters

public class ColaFlowDbContext : DbContext
{
    private readonly ITenantContext _tenantContext;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Apply tenant filter to all tenant-scoped entities
        modelBuilder.Entity<Project>()
            .HasQueryFilter(p => p.TenantId == _tenantContext.CurrentTenantId);

        modelBuilder.Entity<Epic>()
            .HasQueryFilter(e => e.TenantId == _tenantContext.CurrentTenantId);

        modelBuilder.Entity<Story>()
            .HasQueryFilter(s => s.TenantId == _tenantContext.CurrentTenantId);

        modelBuilder.Entity<WorkTask>()
            .HasQueryFilter(t => t.TenantId == _tenantContext.CurrentTenantId);

        modelBuilder.Entity<Sprint>()
            .HasQueryFilter(s => s.TenantId == _tenantContext.CurrentTenantId);

        modelBuilder.Entity<User>()
            .HasQueryFilter(u => u.TenantId == _tenantContext.CurrentTenantId);
    }
}

Integration Test Example

[Fact]
public async Task TenantA_CannotReadTenantB_Projects()
{
    // Arrange
    var tenantA = await CreateTestTenant("Tenant A");
    var tenantB = await CreateTestTenant("Tenant B");

    var apiKeyA = await CreateApiKey(tenantA.Id, "API Key A");
    var projectB = await CreateProject(tenantB.Id, "Project B");

    // Act - Tenant A tries to access Tenant B's project
    var result = await CallMcpResource(
        apiKeyA,
        $"colaflow://projects.get/{projectB.Id}");

    // Assert
    Assert.Equal(404, result.StatusCode); // NOT 403 (don't leak existence)
    Assert.Null(result.Data);
}

[Fact]
public async Task IssuesSearch_NeverReturnsCrossTenant_Results()
{
    // Arrange
    var tenantA = await CreateTestTenant("Tenant A");
    var tenantB = await CreateTestTenant("Tenant B");

    await CreateIssue(tenantA.Id, "Issue A");
    await CreateIssue(tenantB.Id, "Issue B");

    var apiKeyA = await CreateApiKey(tenantA.Id, "API Key A");

    // Act - Tenant A searches all issues
    var result = await CallMcpResource(
        apiKeyA,
        "colaflow://issues.search");

    // Assert
    var issues = result.Data["issues"];
    Assert.Single(issues); // Only Tenant A's issue
    Assert.Equal("Issue A", issues[0]["title"]);
}

Tasks

Task 1: TenantContext Service (2 hours)

  • Create ITenantContext interface
  • Implement McpTenantContext (extract from HttpContext)
  • Register in DI container
  • Update API Key middleware to set TenantId

Files to Create:

  • ColaFlow.Modules.Mcp/Services/McpTenantContext.cs

Task 2: Global Query Filters (3 hours)

  • Add TenantId to all aggregate roots (if missing)
  • Configure Global Query Filters in DbContext
  • Test filters apply automatically
  • Verify cannot be disabled

Files to Modify:

  • ColaFlow.Infrastructure/Data/ColaFlowDbContext.cs

Task 3: Repository Validation (2 hours)

  • Audit all repository methods
  • Ensure all queries include TenantId
  • Add TenantId to method signatures if needed

Task 4: Integration Tests - Cross-Tenant Access (6 hours)

  • Create multi-tenant test infrastructure
  • Test 1: Projects cross-tenant access
  • Test 2: Issues cross-tenant access
  • Test 3: Users cross-tenant access
  • Test 4: Direct ID access (404, not 403)
  • Test 5: Search never returns cross-tenant results

Files to Create:

  • ColaFlow.Modules.Mcp.Tests/Integration/MultiTenantIsolationTests.cs

Task 5: Security Audit (3 hours)

  • Code review by security team
  • Static analysis (grep for raw SQL)
  • Verify all API endpoints use TenantContext
  • Document security architecture

Files to Create:

  • docs/security/multi-tenant-isolation-audit.md

Testing Strategy

Integration Tests (Critical)

  • 2 test tenants with separate data
  • 10+ test scenarios covering all Resources
  • 100% isolation verified
  • Tests must fail if isolation broken

Security Audit Checklist

  • All entities have TenantId property
  • Global Query Filters configured
  • No raw SQL without TenantId
  • All API endpoints use TenantContext
  • Return 404 (not 403) for cross-tenant access
  • Audit logs include TenantId

Dependencies

Prerequisites:

  • Story 5.2 (API Key Management) - API Key linked to Tenant
  • Story 5.5 (Core Resources) - Resources to test

Critical Path: BLOCKS M2 production deployment

Risks & Mitigation

Risk Impact Probability Mitigation
Tenant leak vulnerability CRITICAL Low 100% test coverage, code review
Global filter bypass HIGH Low EF Core best practices, testing
Performance impact Medium Low Indexed TenantId columns

Definition of Done

  • TenantContext service working
  • Global Query Filters applied
  • ALL integration tests passing (100%)
  • Security audit complete (no findings)
  • Code reviewed by security team
  • Documentation updated

Notes

Why This Story Matters

  • CRITICAL SECURITY: Single most important security Story in M2
  • Compliance: Required for GDPR, SOC2, enterprise customers
  • Trust: Multi-tenant leaks destroy customer trust
  • Legal: Data breaches have severe legal consequences

Security Best Practices

  1. Defense in Depth: Multiple layers (API Key, TenantContext, Global Filters)
  2. Fail Closed: No data if TenantId missing (throw exception)
  3. 404 not 403: Don't leak existence of other tenant's data
  4. Audit Everything: Log all tenant context access
  5. Test Religiously: 100% integration test coverage

Reference Materials