- 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)
- 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
- 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
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>
- 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
- 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
- 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
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>
- 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"
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>
- 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
- Added "jti": str(uuid.uuid4()) to the access token payload in services/auth.py
- Promoted Wave 0 xfail stub test_create_access_token_includes_jti_claim to PASS
- Added test_create_access_token_jti_is_unique_per_call uniqueness guard
- uuid module was already imported at module top — no new imports needed
- Add test_change_password_writes_user_nbf_to_redis (xfail strict=False)
- Add test_enable_totp_writes_user_nbf_to_redis (xfail strict=False)
- Add test_disable_totp_writes_user_nbf_to_redis (xfail strict=False)
- Each test exercises the full auth flow then asserts user_nbf Redis key
- Wave 2 (Plan 03) promotes stubs by replacing pytest.xfail() with real assertion
- All 27 existing auth API tests unaffected
- Add test_create_access_token_includes_jti_claim with xfail(strict=False)
- Asserts 'jti' key present in decoded payload, parseable as UUID
- Wave 1 (Plan 02) will add jti to create_access_token to promote this stub
- All 17 existing tests unaffected
Results from Phase 6 production hardening load run:
stats, stats_history, failures, and exceptions captured for baseline
performance reference.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes from manual SLA validation (06-LOCUST-01):
1. backend/api/auth.py: concurrent registrations with same email/handle caused
UniqueViolation to bubble as 500. Now wraps session.commit() in try/except
IntegrityError → 409 Conflict. Regression covered by existing auth API tests.
2. backend/load_tests/locustfile.py: rewrote from single-shared-account to
pre-authenticated unique accounts. New @events.test_start listener creates
50 accounts in 9-user batches (65 s pause between batches to stay under
10 req/min IP rate limit). Added min-request guard to SLA check so a
run-time-consumed-by-setup result never silently "passes".
Run command updated to --run-time 12m.
Known open issue: _account_key in services/rate_limiting.py falls back to IP
when request.state.current_user is not yet set (rate limiter key_func runs
before handler body). Fix: extract sub from JWT directly. See next session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DocumentCard classification_failed badge requires doc.status from the
API list endpoint. _doc_to_dict was omitting the field; this fix adds
it and adds a regression test that would have caught the omission.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/admin/ai-config/models — fetch model list from provider's /models endpoint
- POST /api/admin/ai-config/test-connection — replaced GET; accepts unsaved form
values (api_key, base_url, model_name) so admins can test before saving
- TestConnectionRequest Pydantic model with provider_id validation
- SearchableModelSelect.vue — new reusable combobox; Teleport to body avoids
overflow:hidden clipping; shows all models on open, filters only on typing;
static "Enter manually" item always pinned at bottom; caches per provider
- AdminAiConfigTab: model name input replaced with SearchableModelSelect;
testingProvider ref + spinner added to Test Connection button
- DocumentCard: processing spinner + "Classifying…", pending "Queued" badge,
classification_failed badge; topics section only shown when status=ready
- FileManagerView: onUnmounted imported (polling wiring pending next session)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>