PR1: cloud auth + identity storage foundation#562
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds the initial building blocks for principal-based cloud authentication and DB-backed identity storage, without wiring into request paths yet. This establishes the auth domain model (principals + managed tokens) and the additive cloudstore schema/CRUD needed for follow-up server integration slices.
Changes:
- Added
internal/cloud/authfoundation types for principals/roles/sources plus managed-token generation and HMAC-based hashing/verification with a resolver that supports managed + legacy env tokens. - Extended
internal/cloud/cloudstorewith additive Postgres migrations and new identity CRUD for principals, human users, token metadata, project grants, and auth audit events (with sensitive-key rejection). - Added Postgres-gated integration tests for identity storage and migrations, plus updates to the OpenSpec task/progress artifacts.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/changes/cloud-user-token-management/tasks.md | Marks PR1 tasks as complete in the OpenSpec task list. |
| openspec/changes/cloud-user-token-management/apply-progress.md | Adds progress log describing PR1A/PR1B scope, tests, and remaining work. |
| internal/cloud/cloudstore/identity.go | Introduces identity storage DTOs + CRUD helpers, last-admin guard, and auth audit metadata filtering. |
| internal/cloud/cloudstore/identity_storage_test.go | Adds Postgres-gated integration tests + a few pure helper tests for normalization/metadata filtering. |
| internal/cloud/cloudstore/cloudstore.go | Adds additive migrations for principals/users/tokens/grants/audit tables and indexes. |
| internal/cloud/auth/foundation.go | Adds principal domain model, managed token generator, token hasher/verifier, and resolver (managed + legacy). |
| internal/cloud/auth/foundation_test.go | Adds unit tests covering token format/entropy, hasher/verifier behavior, resolver behavior, and legacy compatibility. |
| internal/cloud/auth/auth.go | Switches legacy token equality check to a constant-time hash-based comparison helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| res, err := cs.db.ExecContext(ctx, ` | ||
| UPDATE cloud_principal_tokens | ||
| SET revoked_at = COALESCE(revoked_at, NOW()), revoked_by_principal_id = $2, revocation_reason = $3 |
| func (cs *CloudStore) RevokeProjectGrant(ctx context.Context, principalID, project string) error { | ||
| if cs == nil || cs.db == nil { | ||
| return fmt.Errorf("cloudstore: not initialized") | ||
| } | ||
| normalized := normalizeCloudProjectGrant(project) | ||
| res, err := cs.db.ExecContext(ctx, `DELETE FROM cloud_project_grants WHERE principal_id = $1 AND project = $2`, strings.TrimSpace(principalID), normalized) | ||
| if err != nil { | ||
| return fmt.Errorf("cloudstore: revoke project grant: %w", err) | ||
| } | ||
| _, _ = res.RowsAffected() | ||
| return nil | ||
| } |
| func requireAffected(res sql.Result, label string) error { | ||
| rows, err := res.RowsAffected() | ||
| if err != nil { | ||
| return nil | ||
| } | ||
| if rows == 0 { | ||
| return fmt.Errorf("cloudstore: %s not found", label) | ||
| } | ||
| return nil | ||
| } |
| switch reflected.Kind() { | ||
| case reflect.Map: | ||
| if reflected.Type().Key().Kind() != reflect.String { | ||
| return nil | ||
| } | ||
| for _, key := range reflected.MapKeys() { | ||
| keyText := key.String() | ||
| if sensitiveAuthAuditKey(keyText) { | ||
| return fmt.Errorf("%w: %s", ErrSensitiveAuditMetadata, keyText) | ||
| } | ||
| if err := rejectSensitiveAuthAuditValue(reflected.MapIndex(key).Interface()); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| case reflect.Slice, reflect.Array: | ||
| for i := 0; i < reflected.Len(); i++ { | ||
| if err := rejectSensitiveAuthAuditValue(reflected.Index(i).Interface()); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
| return nil |
| dsn := os.Getenv("CLOUDSTORE_TEST_DSN") | ||
| if dsn == "" { | ||
| t.Skip("CLOUDSTORE_TEST_DSN not set — skipping integration test (requires Postgres)") | ||
| } | ||
| if !strings.HasPrefix(dsn, "postgres://") && !strings.HasPrefix(dsn, "postgresql://") { | ||
| t.Skip("test requires URL-style CLOUDSTORE_TEST_DSN so a per-test search_path can be attached") | ||
| } |
Merge chained follow-up into PR6 branch before continuing the feature-branch chain.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
Merge according to the feature-branch-chain order for cloud user token management.
PR1 — Auth + storage foundation
Additive auth domain + DB-backed identity storage for principal-based cloud auth. No server/dashboard/CLI wiring yet.
internal/cloud/auth): principal domain types, roles, managed-token generation, dedicated-pepper HMAC hasher/verifier, storage-agnostic managed-token lookup,PrincipalResolver(managed + legacy).internal/cloud/cloudstore): additivecloud_principals,cloud_human_users,cloud_principal_tokens,cloud_project_grants,cloud_auth_audit_log(allIF NOT EXISTS); CRUD for principals/users/tokens/grants; hash-only token persistence; last-active-admin guard (advisory lock); auth audit with sensitive-key redaction.Chain Context
Tracker: #561