up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-28 20:55:22 +02:00
parent d040c001ac
commit 2548abc56f
231 changed files with 47468 additions and 68 deletions

View File

@@ -0,0 +1,495 @@
# Phase 1: Authority Module Conversion
**Sprint:** 2
**Duration:** 1 sprint
**Status:** TODO
**Dependencies:** Phase 0 (Foundations)
---
## Objectives
1. Create `StellaOps.Authority.Storage.Postgres` project
2. Implement full Authority schema in PostgreSQL
3. Implement all repository interfaces
4. Enable dual-write mode for validation
5. Switch Authority to PostgreSQL-only after verification
---
## Deliverables
| Deliverable | Acceptance Criteria |
|-------------|---------------------|
| Authority schema | All tables created with indexes |
| Repository implementations | All 9 interfaces implemented |
| Dual-write wrapper | Optional, for safe rollout |
| Integration tests | 100% coverage of CRUD operations |
| Verification report | MongoDB vs PostgreSQL comparison passed |
---
## Schema Reference
See [SPECIFICATION.md](../SPECIFICATION.md) Section 5.1 for complete Authority schema.
**Tables:**
- `authority.tenants`
- `authority.users`
- `authority.roles`
- `authority.user_roles`
- `authority.service_accounts`
- `authority.clients`
- `authority.scopes`
- `authority.tokens`
- `authority.revocations`
- `authority.login_attempts`
- `authority.licenses`
- `authority.license_usage`
---
## Task Breakdown
### T1.1: Create Authority.Storage.Postgres Project
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Create the PostgreSQL storage project for Authority module.
**Subtasks:**
- [ ] T1.1.1: Create project `src/Authority/__Libraries/StellaOps.Authority.Storage.Postgres/`
- [ ] T1.1.2: Add reference to `StellaOps.Infrastructure.Postgres`
- [ ] T1.1.3: Add reference to `StellaOps.Authority.Core`
- [ ] T1.1.4: Create `AuthorityDataSource` class
- [ ] T1.1.5: Create `AuthorityPostgresOptions` class
- [ ] T1.1.6: Create `ServiceCollectionExtensions.cs`
**Project Structure:**
```
src/Authority/__Libraries/StellaOps.Authority.Storage.Postgres/
├── StellaOps.Authority.Storage.Postgres.csproj
├── AuthorityDataSource.cs
├── AuthorityPostgresOptions.cs
├── Repositories/
│ ├── PostgresUserRepository.cs
│ ├── PostgresRoleRepository.cs
│ ├── PostgresServiceAccountRepository.cs
│ ├── PostgresClientRepository.cs
│ ├── PostgresScopeRepository.cs
│ ├── PostgresTokenRepository.cs
│ ├── PostgresRevocationRepository.cs
│ ├── PostgresLoginAttemptRepository.cs
│ └── PostgresLicenseRepository.cs
├── Migrations/
│ └── V001_CreateAuthoritySchema.cs
└── ServiceCollectionExtensions.cs
```
**Verification:**
- [ ] Project builds without errors
- [ ] Can be referenced from Authority.WebService
---
### T1.2: Implement Schema Migrations
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1 day
**Description:**
Create PostgreSQL schema migration for Authority tables.
**Subtasks:**
- [ ] T1.2.1: Create `V001_CreateAuthoritySchema` migration
- [ ] T1.2.2: Include all tables from SPECIFICATION.md
- [ ] T1.2.3: Include all indexes
- [ ] T1.2.4: Add seed data for system roles/permissions
- [ ] T1.2.5: Test migration idempotency
**Migration Implementation:**
```csharp
public sealed class V001_CreateAuthoritySchema : IPostgresMigration
{
public string Id => "V001_CreateAuthoritySchema";
public string Description => "Create Authority schema with all tables and indexes";
public async Task UpAsync(NpgsqlConnection connection, CancellationToken ct)
{
await using var cmd = connection.CreateCommand();
cmd.CommandText = AuthoritySchemaSql;
await cmd.ExecuteNonQueryAsync(ct);
}
public Task DownAsync(NpgsqlConnection connection, CancellationToken ct)
=> throw new NotSupportedException("Rollback not supported for schema creation");
private const string AuthoritySchemaSql = """
CREATE SCHEMA IF NOT EXISTS authority;
CREATE TABLE IF NOT EXISTS authority.tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active', 'suspended', 'trial', 'terminated')),
settings JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ... rest of schema from SPECIFICATION.md
""";
}
```
**Verification:**
- [ ] Migration creates all tables
- [ ] Migration is idempotent
- [ ] Indexes created correctly
---
### T1.3: Implement User Repository
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1 day
**Description:**
Implement `IUserRepository` for PostgreSQL.
**Subtasks:**
- [ ] T1.3.1: Implement `GetByIdAsync`
- [ ] T1.3.2: Implement `GetByUsernameAsync`
- [ ] T1.3.3: Implement `GetBySubjectIdAsync`
- [ ] T1.3.4: Implement `ListAsync` with pagination
- [ ] T1.3.5: Implement `CreateAsync`
- [ ] T1.3.6: Implement `UpdateAsync`
- [ ] T1.3.7: Implement `DeleteAsync`
- [ ] T1.3.8: Implement `GetRolesAsync`
- [ ] T1.3.9: Implement `AssignRoleAsync`
- [ ] T1.3.10: Implement `RevokeRoleAsync`
- [ ] T1.3.11: Write integration tests
**Interface Reference:**
```csharp
public interface IUserRepository
{
Task<User?> GetByIdAsync(string tenantId, Guid userId, CancellationToken ct);
Task<User?> GetByUsernameAsync(string tenantId, string username, CancellationToken ct);
Task<User?> GetBySubjectIdAsync(Guid subjectId, CancellationToken ct);
Task<PagedResult<User>> ListAsync(string tenantId, UserQuery query, CancellationToken ct);
Task<User> CreateAsync(User user, CancellationToken ct);
Task<User> UpdateAsync(User user, CancellationToken ct);
Task<bool> DeleteAsync(string tenantId, Guid userId, CancellationToken ct);
Task<IReadOnlyList<Role>> GetRolesAsync(string tenantId, Guid userId, CancellationToken ct);
Task AssignRoleAsync(string tenantId, Guid userId, Guid roleId, CancellationToken ct);
Task RevokeRoleAsync(string tenantId, Guid userId, Guid roleId, CancellationToken ct);
}
```
**Verification:**
- [ ] All methods implemented
- [ ] Integration tests pass
- [ ] Tenant isolation verified
---
### T1.4: Implement Service Account Repository
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Implement `IServiceAccountRepository` for PostgreSQL.
**Subtasks:**
- [ ] T1.4.1: Implement `GetByIdAsync`
- [ ] T1.4.2: Implement `GetByAccountIdAsync`
- [ ] T1.4.3: Implement `ListAsync`
- [ ] T1.4.4: Implement `CreateAsync`
- [ ] T1.4.5: Implement `UpdateAsync`
- [ ] T1.4.6: Implement `DeleteAsync`
- [ ] T1.4.7: Write integration tests
**Verification:**
- [ ] All methods implemented
- [ ] Integration tests pass
---
### T1.5: Implement Client Repository
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Implement `IClientRepository` for PostgreSQL (OpenIddict compatible).
**Subtasks:**
- [ ] T1.5.1: Implement `GetByIdAsync`
- [ ] T1.5.2: Implement `GetByClientIdAsync`
- [ ] T1.5.3: Implement `ListAsync`
- [ ] T1.5.4: Implement `CreateAsync`
- [ ] T1.5.5: Implement `UpdateAsync`
- [ ] T1.5.6: Implement `DeleteAsync`
- [ ] T1.5.7: Write integration tests
**Verification:**
- [ ] All methods implemented
- [ ] Integration tests pass
---
### T1.6: Implement Token Repository
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1 day
**Description:**
Implement `ITokenRepository` for PostgreSQL.
**Subtasks:**
- [ ] T1.6.1: Implement `GetByIdAsync`
- [ ] T1.6.2: Implement `GetByHashAsync`
- [ ] T1.6.3: Implement `CreateAsync`
- [ ] T1.6.4: Implement `RevokeAsync`
- [ ] T1.6.5: Implement `PruneExpiredAsync`
- [ ] T1.6.6: Implement `GetActiveTokensAsync`
- [ ] T1.6.7: Write integration tests
**Verification:**
- [ ] All methods implemented
- [ ] Token lookup by hash is fast
- [ ] Expired token pruning works
---
### T1.7: Implement Remaining Repositories
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1.5 days
**Description:**
Implement remaining repository interfaces.
**Subtasks:**
- [ ] T1.7.1: Implement `IRoleRepository`
- [ ] T1.7.2: Implement `IScopeRepository`
- [ ] T1.7.3: Implement `IRevocationRepository`
- [ ] T1.7.4: Implement `ILoginAttemptRepository`
- [ ] T1.7.5: Implement `ILicenseRepository`
- [ ] T1.7.6: Write integration tests for all
**Verification:**
- [ ] All repositories implemented
- [ ] All integration tests pass
---
### T1.8: Add Configuration Switch
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Add configuration-based backend selection for Authority.
**Subtasks:**
- [ ] T1.8.1: Update `ServiceCollectionExtensions` in Authority.WebService
- [ ] T1.8.2: Add conditional registration based on `Persistence:Authority`
- [ ] T1.8.3: Test switching between Mongo and Postgres
- [ ] T1.8.4: Document configuration options
**Implementation:**
```csharp
public static IServiceCollection AddAuthorityStorage(
this IServiceCollection services,
IConfiguration configuration)
{
var backend = configuration.GetValue<string>("Persistence:Authority") ?? "Mongo";
return backend.ToLowerInvariant() switch
{
"postgres" => services.AddAuthorityPostgresStorage(configuration),
"mongo" => services.AddAuthorityMongoStorage(configuration),
_ => throw new ArgumentException($"Unknown Authority backend: {backend}")
};
}
```
**Verification:**
- [ ] Can switch between backends via configuration
- [ ] Invalid configuration throws clear error
---
### T1.9: Implement Dual-Write Wrapper (Optional)
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1 day
**Description:**
Implement dual-write repository wrapper for safe migration.
**Subtasks:**
- [ ] T1.9.1: Create `DualWriteUserRepository`
- [ ] T1.9.2: Implement write-to-both logic
- [ ] T1.9.3: Implement read-from-primary-with-fallback logic
- [ ] T1.9.4: Add metrics for dual-write operations
- [ ] T1.9.5: Add logging for inconsistencies
- [ ] T1.9.6: Create similar wrappers for other critical repositories
**Configuration Options:**
```csharp
public sealed class DualWriteOptions
{
public string PrimaryBackend { get; set; } = "Postgres";
public bool WriteToBoth { get; set; } = true;
public bool FallbackToSecondary { get; set; } = true;
public bool ConvertOnRead { get; set; } = true;
}
```
**Verification:**
- [ ] Writes go to both backends
- [ ] Reads work with fallback
- [ ] Inconsistencies are logged
---
### T1.10: Run Verification Tests
**Status:** TODO
**Assignee:** TBD
**Estimate:** 1 day
**Description:**
Verify PostgreSQL implementation matches MongoDB behavior.
**Subtasks:**
- [ ] T1.10.1: Run comparison tests for User repository
- [ ] T1.10.2: Run comparison tests for Token repository
- [ ] T1.10.3: Verify token issuance/verification flow
- [ ] T1.10.4: Verify login flow
- [ ] T1.10.5: Document any differences found
- [ ] T1.10.6: Generate verification report
**Verification Tests:**
```csharp
[Fact]
public async Task Users_Should_Match_Between_Mongo_And_Postgres()
{
var tenantIds = await GetSampleTenantIds(10);
foreach (var tenantId in tenantIds)
{
var mongoUsers = await _mongoRepo.ListAsync(tenantId, new UserQuery());
var postgresUsers = await _postgresRepo.ListAsync(tenantId, new UserQuery());
postgresUsers.Items.Should().BeEquivalentTo(mongoUsers.Items,
options => options.Excluding(u => u.Id));
}
}
```
**Verification:**
- [ ] All comparison tests pass
- [ ] No data discrepancies found
- [ ] Verification report approved
---
### T1.11: Backfill Data (If Required)
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Backfill existing MongoDB data to PostgreSQL.
**Subtasks:**
- [ ] T1.11.1: Create backfill script for tenants
- [ ] T1.11.2: Create backfill script for users
- [ ] T1.11.3: Create backfill script for service accounts
- [ ] T1.11.4: Create backfill script for clients/scopes
- [ ] T1.11.5: Create backfill script for active tokens
- [ ] T1.11.6: Verify record counts match
- [ ] T1.11.7: Verify sample records match
**Verification:**
- [ ] All Tier A data backfilled
- [ ] Record counts match
- [ ] Sample verification passed
---
### T1.12: Switch to PostgreSQL-Only
**Status:** TODO
**Assignee:** TBD
**Estimate:** 0.5 days
**Description:**
Switch Authority to PostgreSQL-only mode.
**Subtasks:**
- [ ] T1.12.1: Update configuration to `"Authority": "Postgres"`
- [ ] T1.12.2: Deploy to staging
- [ ] T1.12.3: Run full integration test suite
- [ ] T1.12.4: Monitor for errors/issues
- [ ] T1.12.5: Deploy to production
- [ ] T1.12.6: Monitor production metrics
**Verification:**
- [ ] All tests pass in staging
- [ ] No errors in production
- [ ] Performance metrics acceptable
---
## Exit Criteria
- [ ] All repository interfaces implemented for PostgreSQL
- [ ] All integration tests pass
- [ ] Verification tests pass (MongoDB vs PostgreSQL comparison)
- [ ] Configuration switch working
- [ ] Authority running on PostgreSQL in production
- [ ] MongoDB Authority collections archived
---
## Risks & Mitigations
| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| Token verification regression | Low | High | Extensive testing, dual-write |
| OAuth flow breakage | Low | High | Test all OAuth flows |
| Performance regression | Medium | Medium | Load testing before switch |
---
## Rollback Plan
1. Change configuration: `"Authority": "Mongo"`
2. Deploy configuration change
3. MongoDB still has all data (dual-write period)
4. Investigate and fix PostgreSQL issues
5. Re-attempt conversion
---
*Phase Version: 1.0.0*
*Last Updated: 2025-11-28*