curo1305
805fe44bfb
feat(12.1-01): add cross-origin nextLink guard and Drive/OneDrive contract tests
...
- Validate @odata.nextLink hostname against graph.microsoft.com before following
(T-12.1-03); cross-origin nextLink returns complete=False with prior items retained
- Add TestOneDriveCrossOriginNextLink: cross-origin rejection, same-origin follows,
page failure retains prior items
- Add TestGoogleDriveAuthFailureControl: 401 returns complete=False, page1+page2
error retains page1 items
- All 163 four-provider contract tests pass
2026-06-22 08:12:39 +02:00
curo1305
2b46f74329
feat(12.1-01): repair Nextcloud adapter contract and add URL normalization
...
- Remove incompatible NextcloudBackend.list_folder(folder_path="") override
so Nextcloud inherits canonical WebDAVBackend.list_folder(connection_id,
user_id, parent_ref=None, page_token=None) -> CloudListing
- Add normalize_nextcloud_url() to cloud_utils.py: idempotent canonical DAV
root derivation, username percent-encoding, https-only, rejects userinfo/
query/fragment, validates via SSRF guard before return
- Use normalize_nextcloud_url in cloud_backend_factory for nextcloud provider
- Add TestNextcloudBackendNoListFolderOverride and TestNextcloudUrlNormalization
to test_webdav_backend.py
- All 9 Nextcloud contract failures now pass; full focused suite: 158 passed
2026-06-22 08:11:06 +02:00
curo1305
eb68facd6c
test(12.1-01): add failing four-provider contract suite and fixtures
...
- Create test_cloud_provider_contract.py with parametrized suite for
Nextcloud, WebDAV, Google Drive, OneDrive
- Assert canonical (connection_id, user_id, parent_ref=None, page_token=None)
signature, CloudListing return type, trusted caller identity propagation,
metadata normalization, pagination completeness, and forbidden-operation spies
- Add synthetic credential-free fixtures: nextcloud_root.xml, webdav_root.xml,
google_drive_pages.json, onedrive_pages.json
- 9 tests fail for nextcloud (expected RED state — incompatible list_folder override)
- All webdav, google_drive, onedrive contract tests pass
2026-06-22 08:09:00 +02:00
curo1305
c85e4abd91
chore(12-06): bump version 0.2.1 → 0.2.2
2026-06-21 22:30:11 +02:00
curo1305
731b65ecdd
feat(12-06): connection-ID native lifecycle — always INSERT, PUT credentials, connectionsFor multi-account
...
- Replace _upsert_cloud_connection with _insert_cloud_connection (always inserts new UUID row)
- Add PUT /connections/{id}/credentials endpoint (owner-scoped, SSRF + health-check, password-preserve)
- SettingsCloudTab: connectionsFor() renders all same-provider connections with Add account row
- CloudCredentialModal.submit: calls updateWebDavCredentials on edit, connectWebDav on create
- utils.js: FastAPI validation arrays normalised to concise field messages (Rule 2)
- Backend tests: same-provider independence + IDOR negative test for credential update
2026-06-21 22:29:59 +02:00
curo1305 and Claude Sonnet 4.6
206f564248
test(12-05): fix test_nextcloud_connect_persists + compose migration test path
...
- test_nextcloud_connect_persists: provider=nextcloud routes to NextcloudBackend
(not WebDAVBackend) — add storage.nextcloud_backend.validate_cloud_url patch
and use AsyncMock for asyncio.to_thread (plain return_value=True is not
awaitable, swallowed by except → False). Test now passes HTTP 201.
- test_compose_migrations: skip module when docker-compose.yml is not found at
the expected repo-root path (file is not mounted inside the backend container).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-20 12:35:35 +02:00
curo1305 and Claude Sonnet 4.6
043a7817e2
fix(12-uat): add display_name_override schema regression + Compose hardening
...
- test_list_connections_reads_display_name_override_column: proves GET /api/cloud/connections
does not raise UndefinedColumn (direct Phase 12 UAT blocker regression)
- docker-compose: promtail socket :ro, Grafana password fail-fast (:?)
- test_migration_0006: DDL privilege test fails not skips when role absent
- README: version 0.2.1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-20 11:07:30 +02:00
curo1305 and Claude Sonnet 4.6
760a8d4bcd
fix(12-uat): resolve critical code review findings in gap closure (CR-01 through CR-03, IN-01)
...
- CR-01: promtail Docker socket mounted :ro (was read-write)
- CR-02: Grafana admin password uses :? fail-fast (was :-changeme default)
- CR-03: DDL privilege test fails not skips when docuvault_app role absent
- IN-01: README version synced to 0.2.1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-20 10:55:23 +02:00
curo1305
3ca57dcd0c
fix(12-05): bump version to 0.2.1 and update docs for migration-gated startup
...
- backend/main.py, frontend/package.json: version 0.2.0 → 0.2.1
- CLAUDE.md, AGENTS.md: current state updated to reflect gap-closure
- README.md: startup instructions note migrate runs automatically; update migration commands
- RUNBOOK.md: startup diagram includes migrate service; migration gate explanation
- SECURITY.md: Phase 12 gap-closure threat register and security gate evidence
2026-06-20 10:49:21 +02:00
curo1305
de2efd1664
test(12-05): add migration 0005→0006 integration regression and RUNBOOK migration ops
...
- test_migration_0006.py: proves display_name_override, cloud_items, cloud_item_topics,
cloud_folder_states, unique constraints, indexes, and DDL privilege boundary
- Tests skip cleanly without INTEGRATION=1/INTEGRATION_DATABASE_URL (no dev DB mutation)
- RUNBOOK: add Database Migrations section with revision check, upgrade, recovery, and
emergency schema-drift remediation commands
2026-06-20 10:47:11 +02:00
curo1305
1b3084ddfa
feat(12-05): add migration-gated Compose startup path
...
- Add one-shot migrate service using DATABASE_MIGRATE_URL and alembic upgrade head
- Add migrate dependency (service_completed_successfully) to backend, celery-worker, celery-beat
- migrate service has same read_only/cap_drop/no-new-privileges hardening as other services
- Add test_compose_migrations.py: 7 static assertions on Compose configuration
2026-06-20 10:45:22 +02:00
curo1305 and Claude Sonnet 4.6
b6911fb4ed
fix(12): resolve critical code review findings (CR-01 through CR-04)
...
- CR-01: fix browse URL from /folders/{id} to /items?parent_ref= (broken feature)
- CR-02: remove raw provider exception text from _TerminalProviderError messages
- CR-03: add user_id predicate to get_or_create_folder_state query (ownership boundary)
- CR-04: add max_length=255 to ConnectionRenameRequest.display_name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-19 02:00:05 +02:00
curo1305
c441fc63e5
chore(12-04): bump version to 0.2.0 and finalize Phase 12 documentation
...
- backend/main.py, frontend/package.json: 0.1.6 → 0.2.0 (phase-complete minor bump)
- AGENTS.md: current state updated to Phase 12 complete; cloud-resource-foundation
summary with browse contract, security suite, and Phase 13/14 boundary note
- README.md: version updated to 0.2.0
- All existing shared module map rules and frontend architecture constraints unchanged
2026-06-19 01:54:24 +02:00
curo1305
3b24058e15
test(12-04): add dedicated cloud security-negative integration suite
...
- test_foreign_user_cannot_browse_cloud_item (IDOR T-12-01, CONN-04)
- test_admin_cannot_browse_cloud_connection (admin block T-12-01, D-03)
- test_browse_response_excludes_credentials_and_raw_fields (T-12-03, D-06)
- test_ssrf_url_validation_invariants (T-12-04, D-14)
- test_browse_no_quota_mutation (T-12-09, D-07)
- test_browse_no_minio_calls (T-12-09, D-08)
- test_disabled_connection_browse_blocked (D-17)
- test_same_provider_items_scoped_to_connection (D-01, D-05 PostgreSQL)
- test_no_byte_download_during_browse (D-18)
- 16 tests pass; full suite 509 passed
2026-06-19 01:51:13 +02:00
curo1305
c6c0742267
feat(12-03): breadcrumb freshness, formatRelativeTime, v0.1.6, docs
...
- BreadcrumbBar: refreshing spinner, fresh checkmark (fades 3s), stale warning banner
- Accessible labels and role=status for all freshness states
- formatters.js: add formatRelativeTime (shared, no duplication)
- Version bump 0.1.5 → 0.1.6 in backend/main.py and frontend/package.json
- AGENTS.md: update state, shared module map (formatRelativeTime)
- README.md: unified connection-root browsing and capability explanations feature text
- 17 BreadcrumbBar tests pass; 323 total; production build clean
2026-06-18 23:31:50 +02:00
curo1305
c6237ca57f
feat(12-02): add refresh_cloud_folder Celery task, staleness trigger, version 0.1.5
...
- Add tasks/cloud_tasks.py: durable refresh_cloud_folder task with 3-retry
bounded backoff (30s/90s/270s +jitter), credential decryption in worker only
- Register tasks.cloud_tasks.* on documents queue in celery_app.py
- Add stale-while-revalidate staleness trigger in browse.py (5-min threshold)
- Add 4 Task 3 tests: idempotency, cached-row retention on failure, task structure,
no-byte-download contract; add background-refresh scheduling integration test
- Bump backend version 0.1.4 → 0.1.5, frontend package.json 0.1.4 → 0.1.5
- Update AGENTS.md with Phase 12 Plan 02 state and new shared module map entries
- Update README with connection-ID browse API table and v0.1.5
2026-06-18 23:17:34 +02:00
curo1305
e186019066
feat(12-02): decompose api/cloud.py into package with connection-ID browse endpoint
...
- Add api/cloud/ package: connections.py, browse.py, schemas.py, __init__.py
- Add GET /api/cloud/connections/{connection_id}/items (T-12-01 IDOR, T-12-03 cred-free)
- Add PATCH /api/cloud/connections/{id} for display_name_override rename
- Add display_name_override ORM field to CloudConnection model
- Add CloudResourceAdapter service layer with str/UUID coercion
- Fix UUID type compatibility: test_cloud_items.py now uses UUID(as_uuid=True)
matching conftest — removes String(36) patch that caused type incompatibility
- Add 11 Phase 12 integration tests (IDOR, admin block, credential exclusion,
duplicate providers, rename, malformed UUID)
- Remove deleted api/cloud.py (replaced by api/cloud/ package)
2026-06-18 23:10:30 +02:00
curo1305
ff33439f0a
feat(12-02): normalize all four providers into CloudResourceAdapter contract
...
- GoogleDriveBackend: list_folder with full pagination, native doc size=None, get_capabilities
- OneDriveBackend: list_folder with @odata.nextLink pagination, get_capabilities
- WebDAVBackend: list_folder via PROPFIND with SSRF re-validation, get_capabilities
- NextcloudBackend: inherits CloudResourceAdapter from WebDAVBackend
- build_cloud_resource_adapter() added to factory
- 22 new contract/behavior/pagination/no-byte-download tests (51 total pass)
2026-06-18 22:45:09 +02:00
curo1305
718fb2c2b5
feat(12-01): durable owner-scoped cloud metadata schema (migration 0006 + models)
...
- Migration 0006: cloud_items, cloud_item_topics, cloud_folder_states tables
- cloud_connections: add display_name_override column for same-provider disambiguation
- CloudItem, CloudItemTopic, CloudFolderState ORM models with owner/connection indexes
- Unique (connection_id, provider_item_id) boundary; no MinIO object_key field
- Root folder state representable as parent_ref='' without CloudItem parent row
- services/cloud_items.py: resolve_owned_connection, upsert, list, reconcile, folder state
- 17 unit/integration tests covering model fields, isolation, quota invariant, idempotency
2026-06-18 22:37:28 +02:00
curo1305
0a7273b9fe
feat(12-01): normalized cloud resource capability contract and unit tests
...
- Define 9 action keys, 3 capability states, 6 reason codes in cloud_base.py
- Immutable CloudCapability, CloudResource, CloudListing frozen dataclasses
- Abstract CloudResourceAdapter with list_folder, get_capabilities, merge_item_capabilities
- No mutation methods in Phase 12 interface (Phase 13 boundary enforced)
- 29 unit tests covering vocabulary, validation, merge behavior, fake adapter
2026-06-18 22:34:23 +02:00
curo1305
595b33a68c
fix(phase-11): close mobile storage UAT gaps
2026-06-17 10:48:35 +02:00
curo1305
e97ca164d7
Refactor backend and frontend cleanup paths
2026-06-16 11:50:17 +02:00
curo1305
6d1c02f703
refactor(09-05): CODE-09 purge — Phase 8 backend sub-packages (WHAT comments removed)
...
- api/admin/users.py: removed module docstring + 7 WHAT function docstrings
- api/admin/quotas.py: removed module docstring + 2 WHAT function docstrings
- api/admin/ai.py: removed module docstring + 3 WHAT inline comments; security invariant docstrings preserved
- api/admin/shared.py: unchanged (all comments are WHY — constraint notes)
- api/documents/upload.py: removed 4 WHAT inline comments; T-03-05/T-03-06 WHY notes preserved
- api/documents/content.py: removed WHAT function docstring + WHAT inline comment
- api/documents/crud.py: removed 7 WHAT inline comments; D-16 + security constraint docstrings preserved
- api/auth/shared.py: removed module docstring + 1 WHAT inline comment
- api/auth/tokens.py: removed module docstring + 8 WHAT function docstrings/comments; family-revocation + SEC-02 WHY notes preserved
- api/auth/totp.py: removed module docstring + 2 WHAT function docstrings + 2 WHAT inline comments
- api/auth/password.py: removed module docstring + 2 WHAT function docstrings + 3 WHAT inline comments
- All NO-prefix anchor comments and security-invariant WHY comments preserved
- pytest: 413 passed, 1 failed (pre-existing ModuleNotFoundError unrelated to purge)
2026-06-12 16:59:46 +02:00
curo1305
41d81c089f
feat(09-01): implement GET /api/admin/overview endpoint — ADMIN-11
...
- New backend/api/admin/overview.py: aggregate stats (user_count,
total_storage_bytes, doc_status) + last 10 audit entries in one payload
- Sub-router carries NO prefix (parent __init__.py carries /api/admin)
- Reuses _build_filtered_query_with_handles + _audit_to_dict_with_handles
from api.audit — no new serializer or query logic
- Registered overview_router on admin parent router in __init__.py
- All 8 ADMIN-11 tests pass; existing admin and audit suites stay green
2026-06-12 15:50:38 +02:00
curo1305
4caeed2d22
test(09-01): add failing xfail tests for ADMIN-11 overview endpoint
...
- 8 xfail tests covering auth guards, aggregate stats, and sensitive field scan
- Tests will become real assertions once overview.py is implemented in Task 2
2026-06-12 15:47:18 +02:00
curo1305 and Claude Sonnet 4.6
6bda133c81
chore(08-08): PERF-01 packages + Vite 6 upgrade + pin requirements.txt
...
- Upgraded vite 5.4.21 → 6.4.3 (fixes CVE-2026-39363, CVE-2026-39364)
- Upgraded @vitejs/plugin-vue 5.2.4 → 6.0.7 (Vite 6 peer dep)
- Installed @vueuse/core@14.3.0, @vueuse/integrations@14.3.0,
sortablejs@1.15 .7, @tailwindcss/forms@0.5.11,
rollup-plugin-visualizer@7.0.1, @types/sortablejs@1.15 .9
- Wired @tailwindcss/forms plugin in tailwind.config.js (VISUAL-02 ready)
- Pinned backend/requirements.txt to exact == versions per D-17 (32 packages)
- Frontend: 136/136 tests pass on Vite 6; 0 npm audit vulnerabilities
- Backend: 408 passed, 4 skipped, 7 xfailed (baseline 405+1)
- Phase 8 complete (8/8 plans)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-12 10:54:34 +02:00
curo1305 and Claude Sonnet 4.6
81337bd9e3
feat(08-05): decompose documents API monolith into documents/ package — CODE-02 CODE-08
...
- upload.py: request_upload_url, upload_document, confirm_upload (3 routes)
- crud.py: list_documents, get_document, patch_document, delete_document, classify_document (5 routes)
- content.py: stream_document_content + _parse_range (1 route)
- shared.py: _CLOUD_PROVIDERS, UploadUrlRequest, DocumentPatch (single definitions)
- __init__.py: router aggregator prefix=/api/documents, 9 routes total
- documents.py monolith deleted; all URL paths unchanged
- list_documents registered on parent router to avoid FastAPI empty-path/prefix check
- get_storage_backend_for_document re-exported for test monkeypatching compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-12 10:27:48 +02:00
curo1305 and Claude Sonnet 4.6
f01fb0e6d5
feat(08-04): delete admin.py monolith after URL regression — CODE-01 CODE-08
...
Task 3: 54 admin+cloud+ai-config tests pass with package; admin.py and
admin_OLD_REMOVE_IN_TASK_3.py removed. admin/ package is the sole definition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-12 10:16:38 +02:00
curo1305
3ec198768d
feat(08-04): create backend/api/admin/ package — users, quotas, ai, shared, init
...
- users.py: list_users, create_user, update_user_status, initiate_password_reset,
update_ai_config, delete_user, create_system_topic (7 routes)
- quotas.py: get_user_quota, update_user_quota (2 routes)
- ai.py: get_ai_config_models, test_ai_connection, get_ai_config,
update_system_ai_config (4 routes); validate_provider_id call-through (D-11)
- shared.py: _user_to_dict helper (T-02-27 / SEC-07 field whitelist)
- __init__.py: router aggregator with prefix=/api/admin; 13 routes total
- admin.py monolith renamed to admin_OLD_REMOVE_IN_TASK_3.py (deleted in task 3)
- All sub-routers have NO prefix (D-04)
- All handlers inject Depends(get_current_admin) (T-08-04-01)
2026-06-10 18:43:01 +02:00
curo1305
5117e2542a
feat(08-06): create backend/api/auth/ package — Task 1
...
- Create api/auth/shared.py: limiter, all Pydantic request models, _set_refresh_cookie, _user_dict
- Create api/auth/tokens.py: register, login, refresh, logout, logout-all, me, quota, preferences handlers
- Create api/auth/totp.py: totp/setup, totp/enable, totp DELETE handlers (CR-02, CR-03 preserved)
- Create api/auth/password.py: change-password, password-reset, password-reset/confirm handlers (CR-01 preserved)
- Create api/auth/__init__.py: router aggregator with prefix=/api/auth; re-exports limiter
- Rename auth.py monolith to auth_OLD_REMOVE_IN_TASK_2.py (deleted in Task 2 after tests pass)
- All 15 routes confirmed; limiter re-export verified; skip_token_hash calls preserved verbatim
2026-06-10 18:42:27 +02:00
curo1305
4d7157d7fc
feat(08-04): add validate_provider_id helper to services/ai_config.py (D-11)
...
- Migrate inline provider_id validator to service layer per CLAUDE.md
- Raises ValueError (not HTTPException) — service-layer rule
- PROVIDER_DEFAULTS already imported; placed after load_provider_config helpers
2026-06-10 18:39:03 +02:00
curo1305
44ec28d474
test(08-03): promote CR-01/CR-02/CR-03 session-revocation tests from xfail to passing
...
- Remove @pytest.mark.xfail decorator from test_change_password_revokes_other_sessions (CR-01)
- Remove @pytest.mark.xfail decorator from test_enable_totp_revokes_other_sessions (CR-02)
- Remove @pytest.mark.xfail decorator from test_disable_totp_revokes_other_sessions (CR-03)
- All three tests now show PASSED (not XPASS) in strict pytest runs
2026-06-08 16:35:26 +02:00
curo1305
98dcf809b2
chore: merge executor worktree (worktree-agent-ad0b098c151ab9e3f)
2026-06-08 16:24:51 +02:00
curo1305
f750d30224
test(08-01): add xfail stubs for CR-01/CR-02/CR-03 session revocation
...
- test_change_password_revokes_other_sessions: verifies skip_token_hash
preserves current session while revoking all other refresh tokens
- test_enable_totp_revokes_other_sessions: verifies TOTP enrollment
revokes other sessions via skip_token_hash
- test_disable_totp_revokes_other_sessions: verifies TOTP disablement
revokes other sessions via skip_token_hash
- All three marked xfail(strict=False); currently xpassed (backend already complete)
- Plan 08-03 promotes these to strict passing tests
2026-06-08 16:21:58 +02:00
curo1305
61fa6e2051
refactor(08-02): switch cloud.py import to api.schemas.CloudConnectionOut
...
- Replace `from api.admin import CloudConnectionOut` with `from api.schemas import CloudConnectionOut`
- Eliminates cross-package coupling between api/cloud.py and api/admin.py (Pitfall 3)
- All 51 cloud + admin tests pass; CloudConnectionOut serialization unchanged
- Original definition in api/admin.py untouched until plan 08-04
2026-06-08 16:12:38 +02:00
curo1305
10e0900a89
feat(08-02): create backend/api/schemas.py with CloudConnectionOut
...
- New cross-package Pydantic schemas module (D-10, CODE-08)
- CloudConnectionOut moved here from api/admin.py to eliminate coupling
- SEC-08: credentials_enc deliberately excluded; 7-field whitelist preserved
- field_validator coerce_id_to_str preserves UUID→str coercion behavior
- Original definition in api/admin.py left intact until plan 08-04
2026-06-08 16:11:58 +02:00
curo1305 and Claude Sonnet 4.6
c77b97b6d3
fix(fgp): add null-byte separator in HMAC input to prevent concatenation collision
...
Without a separator, UA='foobar'+AL='' and UA='foo'+AL='bar' produced the same
HMAC input, allowing a token fingerprint to match a different header split.
Fix: use '\x00' as separator between user_agent and accept_lang fields.
Regression test added: test_fgp_no_concatenation_collision (FGP-05).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-06 23:03:48 +02:00
curo1305
61b1e045c4
feat(07.4-02): update api/auth.py call sites, promote FGP tests, version bump 0.1.3
...
- api/auth.py login + refresh call sites pass User-Agent and Accept-Language
headers to create_access_token (D-05 — fgp binding at token issuance)
- test_auth_fgp.py: promote all 4 xfail stubs to real assertions (FGP-01..04)
- conftest.py: add _TEST_USER_AGENT constant; configure async_client to send
consistent User-Agent; update auth_user/second_auth_user/admin_user fixtures
to bind fgp to _TEST_USER_AGENT so tokens validate correctly in tests
- test_auth_deps.py: import _TEST_USER_AGENT; update auth_client fixture and
all create_access_token calls to use the constant
- test_cloud.py: update _create_user_and_token to bind fgp to _TEST_USER_AGENT
- test_documents.py: update 3 inline create_access_token calls to pass user_agent
- test_security_headers.py: import _TEST_USER_AGENT; update headers_client +
token creation to use the constant
- Version bump: backend 0.1.2 → 0.1.3, frontend 0.1.2 → 0.1.3
- [Rule 1 - Bug] Fix httpx default User-Agent vs empty-string fgp mismatch in
test infrastructure: 10 tests were failing due to fgp check rejecting tokens
created with fgp="" when client sent "python-httpx/X.Y.Z"
2026-06-06 22:12:57 +02:00
curo1305
1420180be7
feat(07.4-02): add fgp validation block to get_current_user in deps/auth.py
...
- Add `import hmac` to deps/auth.py imports block
- Insert fgp check after user_nbf block: extract fgp_claim from payload,
skip if empty (migration grace for pre-7.4 tokens, D-06), recompute
fgp_actual via auth_service._compute_fgp, compare with hmac.compare_digest
- Mismatch raises HTTP 401 "Token fingerprint mismatch" (D-03)
- Block placed outside try/except (pure computation, no I/O, no fail-open)
2026-06-06 21:55:13 +02:00
curo1305
25c9142fe0
feat(07.4-02): add _compute_fgp helper + extend create_access_token with fgp claim
...
- Add _compute_fgp(user_agent, accept_lang) helper returning 16-char hex
HMAC-SHA256 fingerprint (D-04); uses existing hmac + hashlib imports
- Extend create_access_token signature with user_agent/accept_lang params
(empty-string defaults for backward compatibility, D-05)
- Embed fgp claim in every issued access token payload
2026-06-06 21:54:47 +02:00
curo1305
7833edbf3b
test(07.4-01): add Wave 0 xfail stubs for FGP-01..FGP-04
...
- Create backend/tests/test_auth_fgp.py with 4 xfail(strict=False) stubs
- FGP-01: test_fgp_match_returns_200 (matching headers → 200)
- FGP-02: test_fgp_mismatch_returns_401 (wrong UA → 401)
- FGP-03: test_no_fgp_claim_allowed (no fgp claim → 200 migration grace)
- FGP-04: test_missing_headers_empty_string_binding (empty-string binding → 200)
- All 4 tests report XFAIL; full suite remains at 1 pre-existing failure only
2026-06-06 21:49:46 +02:00
curo1305
739d5b3d9a
chore: merge executor worktree (worktree-agent-a9c4907ff53a1a111)
...
# Conflicts:
# .env.example
# README.md
# backend/config.py
# backend/main.py
# backend/tests/test_auth_es256.py
2026-06-06 18:07:31 +02:00
curo1305
9cc11b5446
feat(07.3-03): backend remember_me — TTL split + cookie Max-Age + 3 promoted tests
...
- services/auth.py: create_refresh_token gains remember_me=False param; selects 16h or 30d TTL (D-09, D-10, D-11)
- api/auth.py: LoginRequest.remember_me bool field; _set_refresh_cookie remember_me param for conditional max_age; login handler threads remember_me through both calls (D-11, RM-03)
- test_auth_es256.py: promote RM-01, RM-02, RM-03 stubs — all 9 phase tests now PASSED
2026-06-06 17:21:14 +02:00
curo1305
8be792ab4c
feat(07.3-02): ES256 JWT algorithm upgrade + startup rotation hook
...
- config.py: add refresh_token_expire_hours=16, jwt_private_key, jwt_public_key fields (D-01, D-09)
- services/auth.py: swap all 4 JWT sites to ES256 via base64-decoded PEM keys; remove HS256 (D-02, D-03)
- main.py: add _rotate_tokens_on_algorithm_change lifespan hook — bulk-revokes refresh tokens on algorithm change; idempotent on repeat boots (D-04, D-05)
- test_auth_es256.py: promote ES256-01..05 + CFG-01 stubs to 6 passing tests; RM-01..03 remain xfail
- docker-compose.yml: inject JWT_PRIVATE_KEY + JWT_PUBLIC_KEY into backend + celery-worker (D-07)
- README.md: add JWT key env vars + key generation Python one-liner snippet
- .env.example: add JWT_PRIVATE_KEY= and JWT_PUBLIC_KEY= lines
- Version bump to 0.1.2
2026-06-06 17:19:50 +02:00
curo1305
99f55825aa
test(07.3-01): Wave 0 Nyquist scaffold — 9 xfail stubs for ES256 + remember_me
...
- Create test_auth_es256.py with 9 xfail(strict=True) stubs: ES256-01..05, RM-01..03, CFG-01
- Add es256_keys(autouse) fixture using cryptography P-256 keygen + monkeypatch with raising=False
- Extend test_settings_has_jwt_config to assert refresh_token_expire_hours==16 + jwt key fields
2026-06-06 17:13:50 +02:00
curo1305 and Claude Sonnet 4.6
c606191f17
fix(07.3): add global ES256 test key fixture to conftest.py
...
After the ES256 upgrade, create_access_token requires non-empty
jwt_private_key / jwt_public_key. The auth_user, second_auth_user,
and admin_user fixtures in conftest.py call create_access_token
without keys being set, breaking all tests that exercise the auth
system. Add a function-scoped autouse fixture that provisions a
generated P-256 keypair for every test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-06 17:10:28 +02:00
curo1305
0d1ab05e45
chore(07.3-02): wire JWT env vars in docker-compose, README key-gen, .env.example, bump to 0.1.2
...
- docker-compose.yml: add JWT_PRIVATE_KEY + JWT_PUBLIC_KEY to backend and celery-worker service env blocks
- README.md: add JWT_PRIVATE_KEY/JWT_PUBLIC_KEY to required env vars table; add JWT Key Generation section with Python one-liner and warning about session revocation
- .env.example: add JWT_PRIVATE_KEY= and JWT_PUBLIC_KEY= adjacent to SECRET_KEY
- backend/main.py: version bump 0.1.1 -> 0.1.2
- frontend/package.json: version bump 0.1.1 -> 0.1.2
2026-06-06 17:03:59 +02:00
curo1305
8d261b0509
security(07.3-02): add _rotate_tokens_on_algorithm_change lifespan hook + promote tests
...
- backend/main.py: add import logging, select; add _rotate_tokens_on_algorithm_change helper above lifespan; call from lifespan with try/except wrap
- backend/tests/test_auth_es256.py: promote ES256-04 and ES256-05 startup rotation tests from xfail to passing; suite reports 6 PASSED + 3 XFAILED
2026-06-06 17:02:45 +02:00
curo1305
fd3f611546
security(07.3-02): ES256 signing — swap 4 JWT sites + config keys + promote tests
...
- backend/config.py: add refresh_token_expire_hours=16, jwt_private_key, jwt_public_key
- backend/services/auth.py: add import base64; all 4 JWT sites use ES256 with inline PEM decode; zero HS256/secret_key references
- backend/tests/test_auth_es256.py: promote ES256-01..03 + CFG-01 tests from xfail to passing
2026-06-06 16:59:38 +02:00
curo1305
fac0e781f9
test(07.3-01): extend test_settings_has_jwt_config — red until Plan 02
...
- Assert settings.refresh_token_expire_hours == 16 (CFG-01 / D-09)
- Assert hasattr(settings, "jwt_private_key") (CFG-01 / D-01)
- Assert hasattr(settings, "jwt_public_key") (CFG-01 / D-01)
- Existing refresh_token_expire_days == 30 assertion preserved (Pitfall 5)
- Test intentionally fails until Plan 02 adds the three new config fields
2026-06-06 16:50:32 +02:00