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>
This commit is contained in:
279
docs/stories/sprint_5/story_5_7.md
Normal file
279
docs/stories/sprint_5/story_5_7.md
Normal file
@@ -0,0 +1,279 @@
|
||||
---
|
||||
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`
|
||||
Reference in New Issue
Block a user