STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_userdata.py
Go to the documentation of this file.
1"""Per-user data helper (backend/userdata.py).
2
3Identity comes from X-Auth-Email (Caddy in prod; a `local` fallback in dev), and
4one user's saved configs must never be visible to another. Tests the module
5directly so it needs neither the full app nor its heavy deps.
6"""
7
8import os
9import sys
10
11import pytest
12
13sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14
15pytest.importorskip("fastapi", reason="userdata imports fastapi.Request")
16
17from backend import userdata # noqa: E402
18
19
20class _Req:
21 def __init__(self, headers):
22 self.headers = headers
23
24
25@pytest.fixture(autouse=True)
26def _isolate(tmp_path, monkeypatch):
27 monkeypatch.setenv("USERDATA_DIR", str(tmp_path))
28
29
31 assert userdata.current_user(_Req({})) == "local"
32
33
35 assert userdata.current_user(_Req({"X-Auth-Email": "Alice@Berkeley.EDU"})) == "alice@berkeley.edu"
36
37
39 who = userdata.current_user(_Req({"X-Auth-Email": "../../etc/passwd"}))
40 assert "/" not in who and ".." not in who
41
42
44 userdata.write_config("u@x.edu", "My Baseline", {"input": {"a": 1}})
45 userdata.write_config("u@x.edu", "Higher Pc!!", {"input": {"a": 2}})
46 assert [c["slug"] for c in userdata.list_configs("u@x.edu")] == ["higher-pc", "my-baseline"]
47 blob = userdata.read_config("u@x.edu", "my-baseline")
48 assert blob["config"] == {"input": {"a": 1}} and blob["name"] == "My Baseline"
49 assert userdata.delete_config("u@x.edu", "my-baseline") is True
50 assert userdata.delete_config("u@x.edu", "my-baseline") is False
51
52
54 userdata.write_config("alice@x.edu", "one", {"input": {}})
55 assert userdata.list_configs("bob@x.edu") == []
56
57
59 with pytest.raises(ValueError):
60 userdata.write_config("u@x.edu", "***", {})
61
62
64 assert userdata.read_config("u@x.edu", "../../../etc/passwd") is None
65
66
67# ── cross-user helpers (added for design sharing) ────────────────────────────
68
69
71 """An ?owner= param and the injected header must normalize identically, or
72 an ownership check hinges on two slightly different transforms."""
73 for raw in ["Alice@Berkeley.EDU", "alice@berkeley.edu", " Alice@Berkeley.EDU "]:
74 assert userdata.slug_user(raw) == userdata.current_user(_Req({"X-Auth-Email": raw}))
75
76
78 for raw in ["../..", "a/b", ".", "..", ""]:
79 slug = userdata.slug_user(raw)
80 assert "/" not in slug and slug not in ("", ".", "..")
81
82
84 """The cross-user browse inspects every sibling folder; if inspection
85 created them, one listing would invent a directory per user it looked at."""
86 userdata.user_dir("ghost@x.edu", create=False)
87 assert list(tmp_path.iterdir()) == []
88
89 userdata.user_dir("real@x.edu")
90 assert (tmp_path / "real@x.edu" / userdata.APP).is_dir()
91
92
94 assert userdata.all_users() == []
95
96 userdata.user_dir("alice@x.edu")
97 userdata.user_dir("bob@x.edu")
98 userdata.user_dir("carol@x.edu", app="someotherapp")
99 (tmp_path / ".hidden").mkdir()
100 (tmp_path / "stray-file").write_text("")
101
102 assert userdata.all_users() == ["alice@x.edu", "bob@x.edu"]
103 assert userdata.all_users(app="someotherapp") == ["carol@x.edu"]
104
105
106def test_all_users_survives_a_missing_root(tmp_path, monkeypatch):
107 monkeypatch.setenv("USERDATA_DIR", str(tmp_path / "not-created-yet"))
108 assert userdata.all_users() == []
__init__(self, headers)
test_all_users_lists_only_people_with_data_for_this_app(tmp_path)
test_slug_user_matches_current_user_exactly()
test_current_user_falls_back_to_local_without_header()
_isolate(tmp_path, monkeypatch)
test_current_user_lowercases_the_email()
test_user_dir_create_false_does_not_conjure_a_tree(tmp_path)
test_all_users_survives_a_missing_root(tmp_path, monkeypatch)
test_slug_user_cannot_escape_the_root()
test_traversal_slug_reads_nothing()
test_write_read_list_delete_roundtrip()
test_identity_cannot_escape_via_traversal()