feat(auth): add User model and per-user repository ownership (E1.1)#70
Open
SHAURYAKSHARMA24 wants to merge 2 commits into
Open
feat(auth): add User model and per-user repository ownership (E1.1)#70SHAURYAKSHARMA24 wants to merge 2 commits into
SHAURYAKSHARMA24 wants to merge 2 commits into
Conversation
Introduce identity persistence ahead of authentication. Adds a User table and scopes repositories to an owner so a request can only ever see its own data. This is the foundation E1.2+ build on, and it is independent of the native-JWT-vs-Auth0 decision still open on the E1 epic. - User model: UUID id, unique email, is_active, timestamps. No credential column yet; that belongs to E1.2. - owner_id FK on repositories, indexed. Alembic 0002 creates the users table, seeds a system user, and backfills existing repositories to it so the upgrade is non-destructive. upgrade/downgrade/upgrade all verified. - Owner-scoped data access: RepositoryService filters every read by the current user and returns 404 (not 403) for another user's repository, so existence never leaks. Import de-duplication is now per-owner. - Temporary current-user seam (get_current_user) attributes requests to the seed user, with an X-Dev-User override honoured only in development/test so multi-tenant behaviour is testable before sign-in exists. E1.2 replaces the body of this dependency with token verification; its callers do not change. The unscoped repository queries remain for internal callers (analysis, ai, documentation) and move onto the current user in E1.3. Tests: cross-user read/list/delete denial and a migration round-trip. Full backend suite green (92 passed, 1 skipped).
| from app.core.config import get_settings | ||
| from app.models.base import Base | ||
| from app.models.repository import RepositoryRecord | ||
| from app.models.user import User |
| from app.core.logging import configure_logging | ||
| from app.core.observability import new_request_id, reset_request_id, runtime_metrics, set_request_id | ||
| from app.models import RepositoryRecord # noqa: F401 - imported so metadata includes model | ||
| from app.models import RepositoryRecord, User # noqa: F401 - imported so metadata includes models |
…ion(32) The 0002 revision id was 35 characters. Alembic's default alembic_version.version_num column is VARCHAR(32); SQLite ignores the length (so local tests passed) but PostgreSQL enforces it and the Docker Compose CI job failed writing the version row. Rename the revision (and its file) to 0002_users_and_repo_owner (25 chars). No schema or data change.
15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements E1.1 — the identity foundation for M1's auth epic. Adds a
Usertable and scopes every repository to an owner, so a request can only ever read or mutate its own data. Deliberately independent of the native-JWT-vs-Auth0 decision still open on #60: aUserrow andowner_idscoping are needed either way, so this unblocks E1.2+ without pre-committing that choice.Backend only. No frontend, no application behaviour change for the existing single-tenant flow (everything is transparently attributed to a seed user until sign-in lands).
What's here
Usermodel — UUID id, unique email,is_active, timestamps. No credential column yet; password/token handling is E1.2's job.owner_idFK onrepositories, indexed. Alembic0002creates theuserstable, seeds a system user (00000000-…-0000), and backfills existing repositories to it, so the migration is non-destructive on a populated database.RepositoryServicefilters every read by the current user and returns 404, not 403, for another user's repository so existence never leaks. Import de-duplication is now per-owner (two users may import the same repo).get_current_userattributes requests to the seed user, with anX-Dev-Userheader override honoured only in development/test so multi-tenant behaviour is testable before real sign-in. E1.2 swaps the body of this one dependency for token verification; its callers don't change.Scoping note: the unscoped repository queries remain for internal callers (analysis / ai / documentation). Moving those onto the current user is E1.3 by design, so this PR stays task-sized.
Type
Verification
Commands/results:
Postgres: I could not run Postgres locally (no Docker on this machine), so the migration is verified on SQLite only here. The Docker Compose CI job exercises the Postgres path — the backend container runs
alembic upgrade headagainst Postgres on start (AUTO_CREATE_TABLES=false), then the readiness probe boots the app. The migration is written to be dialect-safe (batch operations; the seed user is inserted before the FK is validated so the constraint holds on a populated table). If the Compose job surfaces anything, I'll fix it here.Risk and Rollback
downgradeis implemented and verified. Existing single-tenant behaviour is preserved because unauthenticated requests resolve to the seed user that owns all pre-existing data.X-Dev-Userseam is honoured only whenAPP_ENVisdevelopment/test, and ignored in production. It does not lower the current security posture — the app is already open/single-tenant pre-auth — and it is removed by E1.2. Flagging it as a conscious, temporary choice rather than hidden.alembic downgrade 0001_initial(or redeploy the prior image, which pins the earlier head).Checklist
Closes #61