test-data-service 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. test_data_service-0.1.0/LICENSE +21 -0
  2. test_data_service-0.1.0/MANIFEST.in +7 -0
  3. test_data_service-0.1.0/PKG-INFO +229 -0
  4. test_data_service-0.1.0/pyproject.toml +69 -0
  5. test_data_service-0.1.0/readme.md +192 -0
  6. test_data_service-0.1.0/requirements-dev.txt +6 -0
  7. test_data_service-0.1.0/requirements.txt +5 -0
  8. test_data_service-0.1.0/setup.cfg +4 -0
  9. test_data_service-0.1.0/setup.py +9 -0
  10. test_data_service-0.1.0/src/test_data_service.egg-info/PKG-INFO +229 -0
  11. test_data_service-0.1.0/src/test_data_service.egg-info/SOURCES.txt +30 -0
  12. test_data_service-0.1.0/src/test_data_service.egg-info/dependency_links.txt +1 -0
  13. test_data_service-0.1.0/src/test_data_service.egg-info/entry_points.txt +2 -0
  14. test_data_service-0.1.0/src/test_data_service.egg-info/requires.txt +12 -0
  15. test_data_service-0.1.0/src/test_data_service.egg-info/top_level.txt +1 -0
  16. test_data_service-0.1.0/src/testdataservice/__init__.py +26 -0
  17. test_data_service-0.1.0/src/testdataservice/__main__.py +10 -0
  18. test_data_service-0.1.0/src/testdataservice/accounts.py +168 -0
  19. test_data_service-0.1.0/src/testdataservice/app.py +558 -0
  20. test_data_service-0.1.0/src/testdataservice/auth.py +141 -0
  21. test_data_service-0.1.0/src/testdataservice/cli.py +62 -0
  22. test_data_service-0.1.0/src/testdataservice/config.py +162 -0
  23. test_data_service-0.1.0/src/testdataservice/keystore.py +154 -0
  24. test_data_service-0.1.0/src/testdataservice/passwords.py +41 -0
  25. test_data_service-0.1.0/src/testdataservice/sessions.py +44 -0
  26. test_data_service-0.1.0/src/testdataservice/storage.py +219 -0
  27. test_data_service-0.1.0/src/testdataservice/types.py +57 -0
  28. test_data_service-0.1.0/src/testdataservice/web/static/app.js +509 -0
  29. test_data_service-0.1.0/src/testdataservice/web/static/style.css +136 -0
  30. test_data_service-0.1.0/src/testdataservice/web/templates/index.html +162 -0
  31. test_data_service-0.1.0/tds.example.yml +6 -0
  32. test_data_service-0.1.0/tests/test_service.py +449 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roy de Kleijn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include readme.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ include requirements-dev.txt
5
+ include tds.example.yml
6
+ recursive-include src/testdataservice *.py
7
+ recursive-include src/testdataservice/web *.html *.css *.js
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: test-data-service
3
+ Version: 0.1.0
4
+ Summary: A disk-backed test data service with a REST API and web interface
5
+ Author-email: Roy de Kleijn <dekleijn.roy@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/testsmith-io/test-data-service
8
+ Project-URL: Documentation, https://github.com/testsmith-io/test-data-service#readme
9
+ Project-URL: Repository, https://github.com/testsmith-io/test-data-service
10
+ Project-URL: Issues, https://github.com/testsmith-io/test-data-service/issues
11
+ Keywords: test-data,rest,api,fastapi,key-value,testing,mock-data
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Classifier: Framework :: FastAPI
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: fastapi>=0.110
26
+ Requires-Dist: uvicorn[standard]>=0.27
27
+ Requires-Dist: jinja2>=3.1
28
+ Requires-Dist: pydantic>=2.0
29
+ Requires-Dist: pyyaml>=6.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.4; extra == "dev"
32
+ Requires-Dist: httpx>=0.27; extra == "dev"
33
+ Requires-Dist: black>=23.0; extra == "dev"
34
+ Requires-Dist: flake8>=6.0; extra == "dev"
35
+ Requires-Dist: mypy>=1.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+
39
+
40
+ ## Test Data Service
41
+
42
+ A working prototype of the above. Data is stored as JSON files on disk
43
+ (one file per namespace), exposed through a REST API and a web interface,
44
+ with optional role-based access control.
45
+
46
+ ### Install & run
47
+
48
+ ```bash
49
+ # from PyPI (once published)
50
+ pip install test-data-service
51
+
52
+ # start the server (API + web UI) on http://127.0.0.1:8000
53
+ tds serve # or: python -m testdataservice serve
54
+ # open http://127.0.0.1:8000 in a browser
55
+ ```
56
+
57
+ From source, for development:
58
+
59
+ ```bash
60
+ python3 -m venv .venv
61
+ .venv/bin/pip install -e ".[dev]"
62
+ .venv/bin/tds serve
63
+ ```
64
+
65
+ Packaging/release details (build, `twine`, GitHub Actions publish-on-release)
66
+ are in [CONTRIBUTING.md](CONTRIBUTING.md).
67
+
68
+ #### Configure with a YAML file
69
+
70
+ Drop a `tds.yml` (or `tds.yaml`) in your project root and just run `tds serve` —
71
+ no flags needed. Environment variables override the file.
72
+
73
+ ```yaml
74
+ # tds.yml
75
+ data_dir: ./data # where namespace files + .auth live
76
+ auth: true # turn on accounts / access control
77
+ host: 127.0.0.1
78
+ port: 8000
79
+ ```
80
+
81
+ ### Data model
82
+
83
+ Each entry is a `key` / `value` pair inside a **namespace**. A value is one
84
+ of four types: `string`, `boolean`, `number`, or `array`. The type is stored
85
+ alongside the value and validated on write.
86
+
87
+ ### REST API
88
+
89
+ `POST` targets the namespace (collection) with the key **in the body**; `PUT`
90
+ targets a specific entry URI. They are not interchangeable — `POST` only creates,
91
+ `PUT` only updates.
92
+
93
+ | Method | Path | Permission | Description |
94
+ |----------|-------------------------|------------|--------------------------------------|
95
+ | `GET` | `/api/health` | — | Health + auth status |
96
+ | `GET` | `/api/whoami` | — | Caller's access summary |
97
+ | `GET` | `/api/namespaces` | any key | List accessible namespaces |
98
+ | `GET` | `/api/{ns}` | read `ns` | List entries in a namespace |
99
+ | `POST` | `/api/{ns}` | write `ns` | Create an entry (`409` if key exists) |
100
+ | `GET` | `/api/{ns}/{key}` | read `ns` | Get one entry |
101
+ | `PUT` | `/api/{ns}/{key}` | write `ns` | Update an entry (`404` if missing) |
102
+ | `DELETE` | `/api/{ns}/{key}` | write `ns` | Delete an entry |
103
+ | `DELETE` | `/api/{ns}` | write `ns` | Delete a namespace |
104
+
105
+ ```bash
106
+ # create — POST to the namespace, key in the body
107
+ curl -X POST localhost:8000/api/demo \
108
+ -H 'Content-Type: application/json' \
109
+ -d '{"key":"greeting","value":"hello","type":"string"}'
110
+
111
+ # update — PUT to the entry's URI
112
+ curl -X PUT localhost:8000/api/demo/greeting \
113
+ -H 'Content-Type: application/json' \
114
+ -d '{"value":"hi there","type":"string"}'
115
+
116
+ curl localhost:8000/api/demo # list entries
117
+ ```
118
+
119
+ (The names `namespaces`, `health`, and `whoami` are reserved and can't be used
120
+ as namespaces, since they'd collide with the routes above.)
121
+
122
+ The API is self-documenting via OpenAPI. Interactive docs are available at:
123
+
124
+ - **`/docs`** — Swagger UI (also linked as **API docs** in the web interface header)
125
+ - **`/redoc`** — ReDoc
126
+ - **`/openapi.json`** — raw OpenAPI schema
127
+
128
+ When auth is enabled, each operation in Swagger exposes an `X-API-Key` header
129
+ field you can fill in to try requests.
130
+
131
+ ### CSV import / export (web UI only)
132
+
133
+ The web interface has **Download CSV** / **Import CSV** buttons on each
134
+ namespace. These are a convenience of the web UI only — they are built on the
135
+ regular entry API (export reads the entries; import creates/updates each row),
136
+ so there are no dedicated CSV endpoints. The CSV has three columns —
137
+ `key`, `type`, `value`:
138
+
139
+ ```csv
140
+ key,type,value
141
+ name,string,alice
142
+ count,number,7
143
+ tags,array,"[""x"", ""y""]"
144
+ ```
145
+
146
+ On import the `type` column is optional — if omitted, each value's type is
147
+ inferred from its text.
148
+
149
+ ### Access control (accounts + API keys)
150
+
151
+ Off by default. **Turn it on** with `auth: true` in `tds.yml`, `--auth`, or
152
+ `TDS_AUTH_ENABLED=1`. There are two credential types, and you can use both:
153
+
154
+ * **User accounts** — username + password. Humans log into the **web UI**
155
+ (session cookie). Passwords are hashed with scrypt (a slow, salted KDF); the
156
+ plaintext is never stored.
157
+ * **API keys** — long random tokens for scripts/CI. Only a SHA-256 hash is
158
+ stored; the token is shown **once**, at creation.
159
+
160
+ Access is **scoped to each namespace**. A role of `admin` means full CRUD on
161
+ that namespace; `reader` means read-only. A **global admin** manages everything.
162
+
163
+ #### First run
164
+
165
+ On first start with auth on, a default admin account is created:
166
+ **username `admin`, password `admin`** — and the web UI **requires you to set a
167
+ new password** before anything else.
168
+
169
+ #### What the admin does (from the web UI)
170
+
171
+ 1. **Create namespaces** (the admin-only form in the sidebar).
172
+ 2. **Create user accounts** scoped to namespaces — e.g. `alice` with
173
+ `projects=admin` (full CRUD on `projects`). Users then log in and
174
+ create/read/update/delete data in their namespaces, from the web UI **and**
175
+ the API.
176
+
177
+ #### Using the API as a user
178
+
179
+ Each signed-in user can mint a personal **API key** (web UI → *My API keys*, or
180
+ `POST /api/me/keys`) that inherits their namespace permissions:
181
+
182
+ ```bash
183
+ # the token is returned once; then use it as a header
184
+ curl -X POST localhost:8000/api/projects \
185
+ -H "X-API-Key: $TOKEN" -H "Content-Type: application/json" \
186
+ -d '{"key":"env","value":"staging","type":"string"}'
187
+ ```
188
+
189
+ #### Auth & admin endpoints (summary)
190
+
191
+ | Method | Path | Who | Purpose |
192
+ |----------|-------------------------|--------|--------------------------------------|
193
+ | `POST` | `/api/login` | anyone | Log in (sets session cookie) |
194
+ | `POST` | `/api/logout` | anyone | Clear session |
195
+ | `POST` | `/api/password` | user | Change your own password |
196
+ | `POST` | `/api/namespaces` | admin | Create a namespace |
197
+ | `GET`/`POST`/`DELETE` | `/api/accounts[/{user}]` | admin | Manage user accounts |
198
+ | `GET`/`POST`/`DELETE` | `/api/me/keys[/{id}]` | user | Manage your own API keys |
199
+ | `GET`/`POST`/`PUT`/`DELETE` | `/api/keys[/{id}]` | admin | Manage standalone (machine) keys |
200
+
201
+ Standalone machine-to-machine keys (not tied to an account) are still managed by
202
+ an admin at `/api/keys`. For bootstrapping you may seed keys via `TDS_API_KEYS`
203
+ or an admin token via `TDS_ADMIN_KEY` (both hashed in memory, never written to
204
+ disk).
205
+
206
+ ### Configuration
207
+
208
+ Settings come from `tds.yml`/`tds.yaml` in the project root, overridden by
209
+ environment variables:
210
+
211
+ | YAML key | Env var | Default | Purpose |
212
+ |------------|--------------------|--------------|------------------------------------------|
213
+ | `data_dir` | `TDS_DATA_DIR` | `./tds-data` | Namespace files + `.auth/` live here |
214
+ | `auth` | `TDS_AUTH_ENABLED` | `false` | Turn access control on/off |
215
+ | `host` | — | `127.0.0.1` | Bind host |
216
+ | `port` | — | `8000` | Bind port |
217
+ | `secret` | `TDS_SECRET` | *(generated)*| Signs web-UI session cookies |
218
+ | `admin_key`| `TDS_ADMIN_KEY` | *(empty)* | Bootstrap machine admin token |
219
+ | `api_keys` | `TDS_API_KEYS` | *(empty)* | Seed machine keys + grants |
220
+ | — | `TDS_KEY_PEPPER` | *(empty)* | Optional pepper for API-key hashing |
221
+
222
+ Credentials live under `<data_dir>/.auth/` (`accounts.json`, `keys.json`,
223
+ `secret`) — all hashed/secret material, never plaintext passwords or tokens.
224
+
225
+ ### Tests
226
+
227
+ ```bash
228
+ .venv/bin/python -m pytest
229
+ ```
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "test-data-service"
7
+ version = "0.1.0"
8
+ description = "A disk-backed test data service with a REST API and web interface"
9
+ readme = "readme.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Roy de Kleijn", email = "dekleijn.roy@gmail.com" },
14
+ ]
15
+ keywords = ["test-data", "rest", "api", "fastapi", "key-value", "testing", "mock-data"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Testing",
26
+ "Framework :: FastAPI",
27
+ ]
28
+
29
+ dependencies = [
30
+ "fastapi>=0.110",
31
+ "uvicorn[standard]>=0.27",
32
+ "jinja2>=3.1",
33
+ "pydantic>=2.0",
34
+ "pyyaml>=6.0",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ dev = [
39
+ "pytest>=7.4",
40
+ "httpx>=0.27",
41
+ "black>=23.0",
42
+ "flake8>=6.0",
43
+ "mypy>=1.0",
44
+ ]
45
+
46
+ [project.scripts]
47
+ tds = "testdataservice.cli:main"
48
+
49
+ [project.urls]
50
+ Homepage = "https://github.com/testsmith-io/test-data-service"
51
+ Documentation = "https://github.com/testsmith-io/test-data-service#readme"
52
+ Repository = "https://github.com/testsmith-io/test-data-service"
53
+ Issues = "https://github.com/testsmith-io/test-data-service/issues"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
57
+
58
+ [tool.setuptools.package-data]
59
+ testdataservice = ["web/templates/*.html", "web/static/*"]
60
+
61
+ [tool.black]
62
+ line-length = 100
63
+ target-version = ['py310', 'py311', 'py312']
64
+
65
+ [tool.mypy]
66
+ python_version = "3.10"
67
+ warn_return_any = true
68
+ warn_unused_configs = true
69
+ disallow_untyped_defs = false
@@ -0,0 +1,192 @@
1
+
2
+
3
+ ## Test Data Service
4
+
5
+ A working prototype of the above. Data is stored as JSON files on disk
6
+ (one file per namespace), exposed through a REST API and a web interface,
7
+ with optional role-based access control.
8
+
9
+ ### Install & run
10
+
11
+ ```bash
12
+ # from PyPI (once published)
13
+ pip install test-data-service
14
+
15
+ # start the server (API + web UI) on http://127.0.0.1:8000
16
+ tds serve # or: python -m testdataservice serve
17
+ # open http://127.0.0.1:8000 in a browser
18
+ ```
19
+
20
+ From source, for development:
21
+
22
+ ```bash
23
+ python3 -m venv .venv
24
+ .venv/bin/pip install -e ".[dev]"
25
+ .venv/bin/tds serve
26
+ ```
27
+
28
+ Packaging/release details (build, `twine`, GitHub Actions publish-on-release)
29
+ are in [CONTRIBUTING.md](CONTRIBUTING.md).
30
+
31
+ #### Configure with a YAML file
32
+
33
+ Drop a `tds.yml` (or `tds.yaml`) in your project root and just run `tds serve` —
34
+ no flags needed. Environment variables override the file.
35
+
36
+ ```yaml
37
+ # tds.yml
38
+ data_dir: ./data # where namespace files + .auth live
39
+ auth: true # turn on accounts / access control
40
+ host: 127.0.0.1
41
+ port: 8000
42
+ ```
43
+
44
+ ### Data model
45
+
46
+ Each entry is a `key` / `value` pair inside a **namespace**. A value is one
47
+ of four types: `string`, `boolean`, `number`, or `array`. The type is stored
48
+ alongside the value and validated on write.
49
+
50
+ ### REST API
51
+
52
+ `POST` targets the namespace (collection) with the key **in the body**; `PUT`
53
+ targets a specific entry URI. They are not interchangeable — `POST` only creates,
54
+ `PUT` only updates.
55
+
56
+ | Method | Path | Permission | Description |
57
+ |----------|-------------------------|------------|--------------------------------------|
58
+ | `GET` | `/api/health` | — | Health + auth status |
59
+ | `GET` | `/api/whoami` | — | Caller's access summary |
60
+ | `GET` | `/api/namespaces` | any key | List accessible namespaces |
61
+ | `GET` | `/api/{ns}` | read `ns` | List entries in a namespace |
62
+ | `POST` | `/api/{ns}` | write `ns` | Create an entry (`409` if key exists) |
63
+ | `GET` | `/api/{ns}/{key}` | read `ns` | Get one entry |
64
+ | `PUT` | `/api/{ns}/{key}` | write `ns` | Update an entry (`404` if missing) |
65
+ | `DELETE` | `/api/{ns}/{key}` | write `ns` | Delete an entry |
66
+ | `DELETE` | `/api/{ns}` | write `ns` | Delete a namespace |
67
+
68
+ ```bash
69
+ # create — POST to the namespace, key in the body
70
+ curl -X POST localhost:8000/api/demo \
71
+ -H 'Content-Type: application/json' \
72
+ -d '{"key":"greeting","value":"hello","type":"string"}'
73
+
74
+ # update — PUT to the entry's URI
75
+ curl -X PUT localhost:8000/api/demo/greeting \
76
+ -H 'Content-Type: application/json' \
77
+ -d '{"value":"hi there","type":"string"}'
78
+
79
+ curl localhost:8000/api/demo # list entries
80
+ ```
81
+
82
+ (The names `namespaces`, `health`, and `whoami` are reserved and can't be used
83
+ as namespaces, since they'd collide with the routes above.)
84
+
85
+ The API is self-documenting via OpenAPI. Interactive docs are available at:
86
+
87
+ - **`/docs`** — Swagger UI (also linked as **API docs** in the web interface header)
88
+ - **`/redoc`** — ReDoc
89
+ - **`/openapi.json`** — raw OpenAPI schema
90
+
91
+ When auth is enabled, each operation in Swagger exposes an `X-API-Key` header
92
+ field you can fill in to try requests.
93
+
94
+ ### CSV import / export (web UI only)
95
+
96
+ The web interface has **Download CSV** / **Import CSV** buttons on each
97
+ namespace. These are a convenience of the web UI only — they are built on the
98
+ regular entry API (export reads the entries; import creates/updates each row),
99
+ so there are no dedicated CSV endpoints. The CSV has three columns —
100
+ `key`, `type`, `value`:
101
+
102
+ ```csv
103
+ key,type,value
104
+ name,string,alice
105
+ count,number,7
106
+ tags,array,"[""x"", ""y""]"
107
+ ```
108
+
109
+ On import the `type` column is optional — if omitted, each value's type is
110
+ inferred from its text.
111
+
112
+ ### Access control (accounts + API keys)
113
+
114
+ Off by default. **Turn it on** with `auth: true` in `tds.yml`, `--auth`, or
115
+ `TDS_AUTH_ENABLED=1`. There are two credential types, and you can use both:
116
+
117
+ * **User accounts** — username + password. Humans log into the **web UI**
118
+ (session cookie). Passwords are hashed with scrypt (a slow, salted KDF); the
119
+ plaintext is never stored.
120
+ * **API keys** — long random tokens for scripts/CI. Only a SHA-256 hash is
121
+ stored; the token is shown **once**, at creation.
122
+
123
+ Access is **scoped to each namespace**. A role of `admin` means full CRUD on
124
+ that namespace; `reader` means read-only. A **global admin** manages everything.
125
+
126
+ #### First run
127
+
128
+ On first start with auth on, a default admin account is created:
129
+ **username `admin`, password `admin`** — and the web UI **requires you to set a
130
+ new password** before anything else.
131
+
132
+ #### What the admin does (from the web UI)
133
+
134
+ 1. **Create namespaces** (the admin-only form in the sidebar).
135
+ 2. **Create user accounts** scoped to namespaces — e.g. `alice` with
136
+ `projects=admin` (full CRUD on `projects`). Users then log in and
137
+ create/read/update/delete data in their namespaces, from the web UI **and**
138
+ the API.
139
+
140
+ #### Using the API as a user
141
+
142
+ Each signed-in user can mint a personal **API key** (web UI → *My API keys*, or
143
+ `POST /api/me/keys`) that inherits their namespace permissions:
144
+
145
+ ```bash
146
+ # the token is returned once; then use it as a header
147
+ curl -X POST localhost:8000/api/projects \
148
+ -H "X-API-Key: $TOKEN" -H "Content-Type: application/json" \
149
+ -d '{"key":"env","value":"staging","type":"string"}'
150
+ ```
151
+
152
+ #### Auth & admin endpoints (summary)
153
+
154
+ | Method | Path | Who | Purpose |
155
+ |----------|-------------------------|--------|--------------------------------------|
156
+ | `POST` | `/api/login` | anyone | Log in (sets session cookie) |
157
+ | `POST` | `/api/logout` | anyone | Clear session |
158
+ | `POST` | `/api/password` | user | Change your own password |
159
+ | `POST` | `/api/namespaces` | admin | Create a namespace |
160
+ | `GET`/`POST`/`DELETE` | `/api/accounts[/{user}]` | admin | Manage user accounts |
161
+ | `GET`/`POST`/`DELETE` | `/api/me/keys[/{id}]` | user | Manage your own API keys |
162
+ | `GET`/`POST`/`PUT`/`DELETE` | `/api/keys[/{id}]` | admin | Manage standalone (machine) keys |
163
+
164
+ Standalone machine-to-machine keys (not tied to an account) are still managed by
165
+ an admin at `/api/keys`. For bootstrapping you may seed keys via `TDS_API_KEYS`
166
+ or an admin token via `TDS_ADMIN_KEY` (both hashed in memory, never written to
167
+ disk).
168
+
169
+ ### Configuration
170
+
171
+ Settings come from `tds.yml`/`tds.yaml` in the project root, overridden by
172
+ environment variables:
173
+
174
+ | YAML key | Env var | Default | Purpose |
175
+ |------------|--------------------|--------------|------------------------------------------|
176
+ | `data_dir` | `TDS_DATA_DIR` | `./tds-data` | Namespace files + `.auth/` live here |
177
+ | `auth` | `TDS_AUTH_ENABLED` | `false` | Turn access control on/off |
178
+ | `host` | — | `127.0.0.1` | Bind host |
179
+ | `port` | — | `8000` | Bind port |
180
+ | `secret` | `TDS_SECRET` | *(generated)*| Signs web-UI session cookies |
181
+ | `admin_key`| `TDS_ADMIN_KEY` | *(empty)* | Bootstrap machine admin token |
182
+ | `api_keys` | `TDS_API_KEYS` | *(empty)* | Seed machine keys + grants |
183
+ | — | `TDS_KEY_PEPPER` | *(empty)* | Optional pepper for API-key hashing |
184
+
185
+ Credentials live under `<data_dir>/.auth/` (`accounts.json`, `keys.json`,
186
+ `secret`) — all hashed/secret material, never plaintext passwords or tokens.
187
+
188
+ ### Tests
189
+
190
+ ```bash
191
+ .venv/bin/python -m pytest
192
+ ```
@@ -0,0 +1,6 @@
1
+ -r requirements.txt
2
+ pytest>=7.4
3
+ httpx>=0.27
4
+ black>=23.0
5
+ flake8>=6.0
6
+ mypy>=1.0
@@ -0,0 +1,5 @@
1
+ fastapi>=0.110
2
+ uvicorn[standard]>=0.27
3
+ jinja2>=3.1
4
+ pydantic>=2.0
5
+ pyyaml>=6.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ """Setup script for test-data-service.
2
+
3
+ Configuration lives in pyproject.toml; this shim exists for tooling that still
4
+ invokes setup.py.
5
+ """
6
+
7
+ from setuptools import setup
8
+
9
+ setup()