---
story_id: story_5_7
sprint_id: sprint_5
phase: Phase 2 - Resources
status: not_started
priority: P0
story_points: 5
assignee: backend
estimated_days: 2
created_date: 2025-11-06
dependencies: [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

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

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

```csharp
[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
- Multi-Tenant Security: https://learn.microsoft.com/en-us/ef/core/querying/filters
- Sprint 5 Plan: `docs/plans/sprint_5.md`
