- A password reset or change, "log out everywhere" and a revoked untrusted password revoke all of the account's API tokens too (audited): one minted from a stolen session no longer outlives the answer. - A bearer token or link grant with a non-ASCII character is refused (401/403), not a 500. - Making a token's grants or a folder link holds the folder, so a racing delete cannot fail the foreign key; a link's view count is one atomic UPDATE. - A folder link's card counts exactly the files its page lists (one `links.folder_files`). - A file with no extension is served without a trailing dot. - 0106's downgrade names a Hub folder so it never collides with one of the user's folders. - The speed baseline reads share tokens through the app (the database keeps them hashed/encrypted). - Tests: the token scope on the real session stack, the folder poster, the Hub refused for a link, 0106's downgrade data step, 0107 without a key; docs/comments follow (three folder kinds, folder links, key change, the cleanup job's token warnings).
51 lines
2.0 KiB
Python
51 lines
2.0 KiB
Python
"""Migration 0106's downgrade data step, DB-lane: a Hub folder holding a file becomes an own folder
|
|
that keeps the file — under a name its user's own folders do not already have (their names are
|
|
unique per user, case-blind), and with its token gone with the tokens table."""
|
|
|
|
from alembic import command
|
|
from alembic.config import Config
|
|
from sqlalchemy import text
|
|
|
|
from app.db import engine
|
|
|
|
|
|
def test_a_hub_folder_with_a_file_comes_back_as_an_own_folder_beside_a_namesake(db, make_user):
|
|
uid = make_user().id
|
|
with engine.begin() as conn:
|
|
hub = conn.execute(
|
|
text("INSERT INTO file_folders (user_id, kind) VALUES (:u, 'hub') RETURNING id"),
|
|
{"u": uid},
|
|
).scalar_one()
|
|
conn.execute(
|
|
text("INSERT INTO file_folders (user_id, kind, name) VALUES (:u, 'own', :n)"),
|
|
{"u": uid, "n": f"hub {hub}"}, # the name the downgrade would pick, in another case
|
|
)
|
|
held = conn.execute(
|
|
text(
|
|
"INSERT INTO download_jobs (user_id, source_kind, source_ref, profile_snapshot,"
|
|
" status, folder_id) VALUES (:u, 'upload', 'x', '{}', 'queued', :f) RETURNING id"
|
|
),
|
|
{"u": uid, "f": hub},
|
|
).scalar_one()
|
|
conn.execute(
|
|
text(
|
|
"INSERT INTO api_tokens (user_id, name, prefix, token_hash) VALUES"
|
|
" (:u, 'hub', 'sfl_abcd1234', 'h') "
|
|
),
|
|
{"u": uid},
|
|
)
|
|
cfg = Config("alembic.ini")
|
|
command.downgrade(cfg, "0105_job_source_key")
|
|
try:
|
|
with engine.connect() as conn:
|
|
kind, name = conn.execute(
|
|
text("SELECT kind, name FROM file_folders WHERE id = :i"), {"i": hub}
|
|
).one()
|
|
folder_of_job = conn.execute(
|
|
text("SELECT folder_id FROM download_jobs WHERE id = :j"), {"j": held}
|
|
).scalar_one()
|
|
finally:
|
|
command.upgrade(cfg, "head")
|
|
assert (kind, name) == ("own", f"Hub {hub} (2)")
|
|
assert folder_of_job == hub
|