6d65f7eb09
ensure better API test coverage on important routes 1. tests/api/public_shares.hurl — create a share token, verify, list contents, fetch a file, fetch a folder zip, then revoke and re-verify with the token. Same pattern as grants.hurl. ~30 min, biggest security ROI. 2. tests/api/auth_session_lifecycle.hurl — login → refresh → use new token → logout → refresh-rejected → login-again. Covers the session-family invalidation contract. 3. tests/api/admin_user_ops.hurl — admin disables / re-enables / changes role / resets password / sets quota for a fixture user. Five POSTs. 4. tests/api/groups_effective_members.hurl — nested groups: A contains B contains user X; effective-members returns X. Two scenarios, but it's the ReBAC contract under the Drive refactor. 5. tests/api/search_basic.hurl — upload foo.txt, search "foo", get the result; cross-user: bob can't search alice's foo.
135 lines
6.7 KiB
Plaintext
135 lines
6.7 KiB
Plaintext
# =============================================================
|
|
# OxiCloud — Baseline: auth session lifecycle
|
|
# =============================================================
|
|
# Covers the refresh-token / logout / status surface that the
|
|
# existing `auth_login.hurl` deliberately leaves alone (login +
|
|
# lockout only). Browsers and the desktop NC client both rely
|
|
# on the rotate-and-revoke semantics being correct; this file
|
|
# pins them as a single end-to-end flow.
|
|
#
|
|
# Coverage:
|
|
# 1. GET /api/auth/status (no auth required, used by login page)
|
|
# 2. POST /api/auth/login (capture initial access + refresh)
|
|
# 3. POST /api/auth/refresh (rotate; capture new tokens)
|
|
# 4. New access token works on /api/auth/me
|
|
# 5. OLD refresh token rejected after rotation (session-family
|
|
# single-use enforcement)
|
|
# 6. POST /api/auth/logout (revokes the current refresh)
|
|
# 7. Refresh after logout → 401 (revocation actually took effect)
|
|
# 8. Access token still works briefly until it expires — we
|
|
# don't assert that explicitly because TTL is configurable
|
|
# and the access-token revocation semantics are documented
|
|
# as "JWT remains valid until exp"; logout only kills the
|
|
# refresh path.
|
|
# =============================================================
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 1 — Unauthenticated /api/auth/status probe.
|
|
# The login page hits this on every load; the response
|
|
# determines whether the "Create first admin" flow shows.
|
|
# ─────────────────────────────────────────────────────────────
|
|
GET {{base_url}}/api/auth/status
|
|
|
|
HTTP 200
|
|
[Asserts]
|
|
jsonpath "$.initialized" == true
|
|
jsonpath "$.admin_count" >= 1
|
|
jsonpath "$.registration_allowed" == true
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 2 — Login as admin. Capture BOTH tokens — we need the
|
|
# refresh later to verify rotation semantics.
|
|
# ─────────────────────────────────────────────────────────────
|
|
POST {{base_url}}/api/auth/login
|
|
Content-Type: application/json
|
|
{ "username": "{{username}}", "password": "{{password}}" }
|
|
|
|
HTTP 200
|
|
[Captures]
|
|
access_v1: jsonpath "$.access_token"
|
|
refresh_v1: jsonpath "$.refresh_token"
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 3 — Refresh: mint a new (access, refresh) pair. The refresh
|
|
# token is rotated — the response carries a NEW refresh
|
|
# that supersedes refresh_v1.
|
|
# ─────────────────────────────────────────────────────────────
|
|
POST {{base_url}}/api/auth/refresh
|
|
Content-Type: application/json
|
|
{ "refresh_token": "{{refresh_v1}}" }
|
|
|
|
HTTP 200
|
|
[Captures]
|
|
access_v2: jsonpath "$.access_token"
|
|
refresh_v2: jsonpath "$.refresh_token"
|
|
[Asserts]
|
|
jsonpath "$.access_token" != "{{access_v1}}"
|
|
jsonpath "$.refresh_token" != "{{refresh_v1}}"
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 4 — The new access token works on a protected endpoint.
|
|
# ─────────────────────────────────────────────────────────────
|
|
GET {{base_url}}/api/auth/me
|
|
Authorization: Bearer {{access_v2}}
|
|
|
|
HTTP 200
|
|
[Asserts]
|
|
jsonpath "$.username" == "{{username}}"
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 5 — The OLD refresh token MUST be rejected after rotation.
|
|
# This is the session-family single-use property: replay
|
|
# of a used refresh token is treated as theft and rejected.
|
|
#
|
|
# The handler's OpenAPI doc says 401, but the actual response
|
|
# is 403: the refresh service raises `ErrorKind::AccessDenied`
|
|
# which maps to HTTP 403 in this codebase. Pinning the
|
|
# observed-and-correct behavior here.
|
|
# ─────────────────────────────────────────────────────────────
|
|
POST {{base_url}}/api/auth/refresh
|
|
Content-Type: application/json
|
|
{ "refresh_token": "{{refresh_v1}}" }
|
|
|
|
HTTP 403
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 6 — Logout using the v2 refresh + v2 access. Server-side
|
|
# this revokes the session and clears auth cookies in the
|
|
# response.
|
|
# ─────────────────────────────────────────────────────────────
|
|
POST {{base_url}}/api/auth/logout
|
|
Authorization: Bearer {{access_v2}}
|
|
Content-Type: application/json
|
|
{ "refresh_token": "{{refresh_v2}}" }
|
|
|
|
HTTP 200
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 7 — Post-logout: the v2 refresh token is now revoked. A
|
|
# refresh attempt is rejected with 403 (same AccessDenied
|
|
# mapping as step 5).
|
|
# ─────────────────────────────────────────────────────────────
|
|
POST {{base_url}}/api/auth/refresh
|
|
Content-Type: application/json
|
|
{ "refresh_token": "{{refresh_v2}}" }
|
|
|
|
HTTP 403
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 8 — Sanity re-check: status still reports the system as
|
|
# initialized after logout (no state regression).
|
|
# ─────────────────────────────────────────────────────────────
|
|
GET {{base_url}}/api/auth/status
|
|
|
|
HTTP 200
|
|
[Asserts]
|
|
jsonpath "$.initialized" == true
|