📋 Product Owner view

Feature inventory, spec alignment, behavioural gaps  ·  Product Owner, Product Manager

Generated 4 days ago
⚡ Agent trace  ·  13 steps  ·  $0.0386  ·  click to expand
search_claims(query='feature capability user', category='any')search_claims(query='README spec documentation', category='informational')search_claims(query='api endpoint route handler', category='any')search_claims(query='app application server', category='informational')search_claims(query='user', category='any')search_claims(query='model', category='any')search_claims(query='item', category='any')search_claims(query='email password reset token', category='any')read_source_file(file_path='backend/app/api/main.py', start_line=1)read_source_file(file_path='backend/app/api/routes/users.py', start_line=1)read_source_file(file_path='backend/app/api/routes/login.py', start_line=1)read_source_file(file_path='backend/app/api/routes/login.py', start_line=61)read_source_file(file_path='backend/app/api/routes/users.py', start_line=60)read_source_file(file_path='backend/app/api/routes/items.py', start_line=1)read_source_file(file_path='backend/app/api/routes/items.py', start_line=61)read_source_file(file_path='backend/app/api/routes/private.py', start_line=1)read_source_file(file_path='backend/app/api/routes/utils.py', start_line=1)read_source_file(file_path='backend/app/core/config.py', start_line=1)read_source_file(file_path='backend/app/core/security.py', start_line=1)read_source_file(file_path='backend/app/utils.py', start_line=1)read_source_file(file_path='backend/app/utils.py', start_line=61)read_source_file(file_path='backend/app/core/config.py', start_line=61)read_source_file(file_path='backend/app/main.py', start_line=1)read_source_file(file_path='backend/app/core/db.py', start_line=1)read_source_file(file_path='backend/app/initial_data.py', start_line=1)read_source_file(file_path='backend/app/api/deps.py', start_line=1)read_source_file(file_path='backend/app/models.py', start_line=1)search_claims(query='README', category='informational')read_source_file(file_path='backend/app/models.py', start_line=61)read_source_file(file_path='backend/app/crud.py', start_line=1)

Product View

Feature Inventory

Feature Status Confidence Notes
User Registration (self-service) Partial High UserRegister model exists at backend/app/models.py:26-29 but there is no public endpoint for self-registration. The POST /users/ endpoint at backend/app/api/routes/users.py:54-78 is superuser-only.
User Registration (admin/superuser) Implemented High Superusers can create users via POST /users/ — includes optional welcome email. backend/app/api/routes/users.py:54-78
User Registration (private/internal) Implemented High POST /private/users/ at backend/app/api/routes/private.py:23-38 — only available when ENVIRONMENT=local. No auth required.
Login / Access Token Implemented High POST /login/access-token at backend/app/api/routes/login.py:23-42. Returns JWT bearer token.
Test Token (validate current token) Implemented High POST /login/test-token at backend/app/api/routes/login.py:45-50. Returns current user info.
Password Recovery (request) Implemented High POST /password-recovery/{email} at backend/app/api/routes/login.py:53-74. Sends email with reset link. Anti-enumeration: always returns same message regardless of whether email exists.
Password Recovery (HTML preview) Implemented High POST /password-recovery-html-content/{email} at backend/app/api/routes/login.py:100-120. Superuser-only. Returns rendered HTML email.
Password Reset (consume token) Implemented High POST /reset-password/ at backend/app/api/routes/login.py:77-97. Validates token, updates password.
User Profile (read own) Implemented High GET /users/me — standard FastAPI pattern, returns current user.
User Profile (update own) Implemented High PATCH /users/me at backend/app/api/routes/users.py:81-100. Email uniqueness check with 409 conflict.
Change Own Password Implemented High PATCH /users/me/password at backend/app/api/routes/users.py:103-120. Validates current password, prevents same-password reuse.
Admin: List All Users Implemented High GET /users/ at backend/app/api/routes/users.py:32-51. Superuser-only. Paginated with skip/limit.
Admin: Create User Implemented High POST /users/ at backend/app/api/routes/users.py:54-78. Superuser-only.
Admin: Get User by ID Implemented High GET /users/{user_id} — superuser-only.
Admin: Update User Implemented High PATCH /users/{user_id} — superuser-only.
Admin: Delete User Implemented High DELETE /users/{user_id} at backend/app/api/routes/users.py:215-232. Superuser-only. Cascades to delete all user's items.
Items: CRUD Implemented High Full CRUD at backend/app/api/routes/items.py:13-113. Create, Read, List, Update, Delete.
Items: Ownership Scoping Implemented High Non-superusers only see/edit/delete their own items. Superusers see all. backend/app/api/routes/items.py:21-42, 56, 89, 109
Items: Paginated List Implemented High GET /items/ with skip/limit params, ordered by created_at desc. backend/app/api/routes/items.py:13-45
Email: Test Email Implemented High POST /utils/test-email/ at backend/app/api/routes/utils.py:11-26. Superuser-only.
Health Check Implemented High GET /utils/health-check/ at backend/app/api/routes/utils.py:29-31. Public, returns true.
Sentry Error Tracking Implemented High Initialized at backend/app/main.py:14-15. Only enabled when SENTRY_DSN is set and environment is not "local".
CORS Configuration Implemented High Configurable via BACKEND_CORS_ORIGINS env var + FRONTEND_HOST. backend/app/core/config.py:40-49
Database Migrations Missing High backend/app/core/db.py:16-22 explicitly states "Tables should be created with Alembic migrations" but the Alembic migration code is commented out. Table creation via SQLModel.metadata.create_all is also commented out.
Self-Service Registration Missing High No public endpoint exists for users to register themselves. The UserRegister model exists but is unused by any endpoint.
Email Templates Implemented High Three templates exist: test_email.html, reset_password.html, new_account.html. Rendered via Jinja2. backend/app/utils.py:25-30

What the Code Does That the Spec Doesn't Mention

  1. Timing attack prevention on login — When a user provides an email that doesn't exist, the system still runs Argon2 password verification against a dummy hash (DUMMY_HASH at backend/app/crud.py:42) to ensure response times are consistent. This is a security hardening measure not typically documented in specs.

  2. Automatic password hash rehashing — When a user logs in and the password hash algorithm has been upgraded (e.g., Argon2 parameters change), the system transparently re-hashes and stores the updated hash during authentication (backend/app/crud.py:55-59). This is a silent migration mechanism.

  3. Dual hashing algorithm support — The system supports both Argon2 and bcrypt password hashers simultaneously (backend/app/core/security.py:11-16). This allows gradual migration between algorithms.

  4. Private/internal user creation endpointPOST /private/users/ at backend/app/api/routes/private.py:23-38 is only available in local environment. It has no authentication requirement and accepts an is_verified field that is ignored (the field is defined in the Pydantic model but never used in the handler).

  5. Password recovery HTML preview endpointPOST /password-recovery-html-content/{email} at backend/app/api/routes/login.py:100-120 is a superuser-only endpoint that returns the rendered HTML of the password recovery email. This is a debugging/QA tool not typically exposed.

  6. Anti-enumeration on password recovery — The password recovery endpoint always returns the same message ("If that email is registered, we sent a password recovery link") regardless of whether the email exists (backend/app/api/routes/login.py:72-74). This prevents email enumeration attacks.

  7. Anti-enumeration on password reset — If a valid reset token references a deleted user, the system returns "Invalid token" rather than revealing the user doesn't exist (backend/app/api/routes/login.py:86-88).

  8. New account email with plaintext password — When a superuser creates a user account, the system sends the user's plaintext password in the welcome email (backend/app/api/routes/users.py:69-77). This is a security consideration — the password is transmitted in clear text via email.


Spec Gaps

  1. No self-service registration — The UserRegister model exists at backend/app/models.py:26-29 but there is no endpoint that uses it. Users cannot sign up on their own. This appears to be scaffolding for a feature that was planned but not implemented.

  2. Database migrations not wired up — The code at backend/app/core/db.py:16-22 explicitly says tables should be created with Alembic migrations, but the migration setup is commented out. The init_db function only creates the first superuser — it does not create database tables. This means the application will fail on first run unless tables are created manually or the commented-out create_all is enabled.

  3. is_verified field is dead code — The PrivateUserCreate model at backend/app/api/routes/private.py:16-20 has an is_verified field, but it is never read or stored in the handler. The field exists in the API contract but has no effect.

  4. No rate limiting on login — The login endpoint has no rate limiting or account lockout mechanism. This is a security gap for production deployments.

  5. No email verification flow — While there's an is_verified field concept in the private API, there is no email verification endpoint or flow for users to verify their email addresses.


Open Questions for Engineering

  1. Self-service registration — The UserRegister model exists but is unused. Was self-registration planned for the roadmap? If so, is there a frontend component that was supposed to call it? Should we add a POST /users/register endpoint?

  2. Database initialization — How are database tables created in production? The code comments say "use Alembic migrations" but there's no Alembic configuration. Is this handled by an external deployment script, or is this a known gap?

  3. Private endpoint security — The POST /private/users/ endpoint has no authentication and is only gated by environment. Is this intentional for local development only, or should it have some form of authentication even in local mode?

  4. Password in welcome email — Sending plaintext passwords in welcome emails is a security anti-pattern. Should we switch to a "set your password" link instead?

  5. Rate limiting — There's no rate limiting on login or password recovery endpoints. Is this acceptable for the current deployment, or should we add it before the next release?

  6. is_verified field — The PrivateUserCreate model accepts is_verified but it's never stored. Is this a bug, or is the field intended for future use? Should we remove it or implement the verification flow?

  7. Token expiration — The access token expires in 8 days (60 * 24 * 8 minutes at backend/app/core/config.py:36). Is this appropriate for the product's security requirements? No refresh token mechanism exists.