fluxfiles-token 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.
- fluxfiles_token-0.1.0/.gitignore +110 -0
- fluxfiles_token-0.1.0/LICENSE +21 -0
- fluxfiles_token-0.1.0/PKG-INFO +226 -0
- fluxfiles_token-0.1.0/README.md +206 -0
- fluxfiles_token-0.1.0/pyproject.toml +41 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/__init__.py +38 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/crypto.py +78 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/exceptions.py +9 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/py.typed +0 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/token.py +841 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/types.py +71 -0
- fluxfiles_token-0.1.0/src/fluxfiles_token/verify.py +32 -0
- fluxfiles_token-0.1.0/tests/test_crypto.py +85 -0
- fluxfiles_token-0.1.0/tests/test_php_compat.py +107 -0
- fluxfiles_token-0.1.0/tests/test_token.py +317 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
vendor/
|
|
3
|
+
node_modules/
|
|
4
|
+
# …but the vendored front-end lib we ship on purpose (xterm.js for the SFTP
|
|
5
|
+
# terminal) must be committed — it's served as a static asset, not installed.
|
|
6
|
+
!packages/core/assets/vendor/
|
|
7
|
+
!packages/core/assets/vendor/**
|
|
8
|
+
|
|
9
|
+
# Environment
|
|
10
|
+
.env
|
|
11
|
+
# Adapter-e2e .env backup (global-setup restores the real one in teardown)
|
|
12
|
+
packages/core/.env.adapter-e2e.bak
|
|
13
|
+
|
|
14
|
+
# Storage
|
|
15
|
+
storage/uploads/
|
|
16
|
+
storage/rate_limit.json
|
|
17
|
+
|
|
18
|
+
# Core package storage (runtime/test artifacts)
|
|
19
|
+
packages/core/storage/uploads/
|
|
20
|
+
packages/core/storage/rate_limit.json
|
|
21
|
+
packages/core/storage/demo_ip.json
|
|
22
|
+
|
|
23
|
+
# Build output
|
|
24
|
+
packages/react/dist/
|
|
25
|
+
packages/vue/dist/
|
|
26
|
+
packages/node/dist/
|
|
27
|
+
build/
|
|
28
|
+
|
|
29
|
+
# Python (packages/python)
|
|
30
|
+
packages/python/.venv/
|
|
31
|
+
packages/python/dist/
|
|
32
|
+
packages/python/*.egg-info/
|
|
33
|
+
**/__pycache__/
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
.ruff_cache/
|
|
37
|
+
|
|
38
|
+
# Test playground scaffolds (source committed, deps not)
|
|
39
|
+
packages/core/tests/apps/**/node_modules/
|
|
40
|
+
packages/core/tests/apps/node_modules/
|
|
41
|
+
# Generated Laravel proxy e2e app (regenerate: laravel-e2e/setup.sh; source in laravel-e2e/)
|
|
42
|
+
packages/core/tests/apps/laravel-app/
|
|
43
|
+
|
|
44
|
+
# Playwright runtime artifacts
|
|
45
|
+
.playwright-mcp/
|
|
46
|
+
test-results/
|
|
47
|
+
playwright-report/
|
|
48
|
+
# Ad-hoc QA screenshots taken at the repo root during manual browser testing
|
|
49
|
+
# (never part of the source — they slipped in once; keep them out for good).
|
|
50
|
+
/*.png
|
|
51
|
+
|
|
52
|
+
# OS files
|
|
53
|
+
.DS_Store
|
|
54
|
+
Thumbs.db
|
|
55
|
+
|
|
56
|
+
# IDE
|
|
57
|
+
.idea/
|
|
58
|
+
.vscode/
|
|
59
|
+
*.swp
|
|
60
|
+
*.swo
|
|
61
|
+
*~
|
|
62
|
+
|
|
63
|
+
# Testing
|
|
64
|
+
coverage/
|
|
65
|
+
.phpunit.result.cache
|
|
66
|
+
.phpunit.cache/
|
|
67
|
+
|
|
68
|
+
# Logs
|
|
69
|
+
*.log
|
|
70
|
+
|
|
71
|
+
# Project docs (private)
|
|
72
|
+
docs/FLUXFILES-ROADMAP.md
|
|
73
|
+
docs/ROADMAP.md
|
|
74
|
+
# Wrapper lockfiles aren't needed for published libs and break cross-platform
|
|
75
|
+
# npm optional-dep resolution (npm/cli#4828). CI does a fresh install.
|
|
76
|
+
packages/sdk/package-lock.json
|
|
77
|
+
packages/ckeditor4/package-lock.json
|
|
78
|
+
packages/tinymce/package-lock.json
|
|
79
|
+
docs/PHASE2-OPTIMIZATION-PLAN.md
|
|
80
|
+
docs/COMMERCIAL-STRATEGY.md
|
|
81
|
+
docs/LICENSING-PLAN.md
|
|
82
|
+
docs/*.SECRET.txt
|
|
83
|
+
docs/license-signing-key*
|
|
84
|
+
docs/release-signing-key*
|
|
85
|
+
docs/*.key
|
|
86
|
+
|
|
87
|
+
# Proprietary paid modules (private — not part of the MIT core; each lives in its
|
|
88
|
+
# own private repo for distribution). NEVER commit these to the public monorepo.
|
|
89
|
+
packages/optimize/
|
|
90
|
+
packages/share/
|
|
91
|
+
packages/intake/
|
|
92
|
+
packages/versioning/
|
|
93
|
+
packages/webhooks/
|
|
94
|
+
packages/ai/
|
|
95
|
+
packages/ocr/
|
|
96
|
+
packages/virus/
|
|
97
|
+
packages/backup/
|
|
98
|
+
packages/c2pa/
|
|
99
|
+
packages/audit-export/
|
|
100
|
+
packages/sso/
|
|
101
|
+
packages/dlp/
|
|
102
|
+
packages/legal-hold/
|
|
103
|
+
|
|
104
|
+
# License server runtime DB (customer records — never commit)
|
|
105
|
+
services/license-server/data/
|
|
106
|
+
*.sqlite
|
|
107
|
+
*.sqlite-*
|
|
108
|
+
|
|
109
|
+
# Claude subagent working artifacts (plans, review findings)
|
|
110
|
+
.claude/work/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 thai-pc
|
|
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,226 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fluxfiles-token
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Server-side Python SDK for minting FluxFiles JWTs (plain + BYOB), byte-compatible with the PHP core and @fluxfiles/node
|
|
5
|
+
Project-URL: Homepage, https://github.com/thai-pc/fluxfiles#python-server-side-token-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/thai-pc/fluxfiles
|
|
7
|
+
Project-URL: Issues, https://github.com/thai-pc/fluxfiles/issues
|
|
8
|
+
Author: thai-pc
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: byob,file-manager,fluxfiles,jwt,r2,s3,server,token
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: cryptography>=42
|
|
14
|
+
Requires-Dist: pyjwt<3,>=2.8
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# FluxFiles for Python (server-side token SDK)
|
|
22
|
+
|
|
23
|
+
Mint [FluxFiles](https://github.com/thai-pc/fluxfiles) JWTs from any Python
|
|
24
|
+
backend (Django, FastAPI, Flask, …). Tokens are **byte-compatible with the PHP
|
|
25
|
+
core**, so non-PHP apps can issue access tokens — including encrypted BYOB
|
|
26
|
+
(Bring Your Own Bucket) credentials — without running PHP.
|
|
27
|
+
|
|
28
|
+
> **This package only issues tokens — it is not a backend.** You still run a
|
|
29
|
+
> FluxFiles **core service** (the file-manager backend that talks to storage;
|
|
30
|
+
> a PHP app, e.g. the Docker image) for the token to authenticate against.
|
|
31
|
+
> `fluxfiles-token` simply removes the need for *your app* to be PHP in order
|
|
32
|
+
> to mint those tokens.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
- Python 3.10+
|
|
37
|
+
- A running FluxFiles **core service** the issued tokens authenticate against
|
|
38
|
+
(the SDK/iframe `endpoint` points at it).
|
|
39
|
+
- The same **`FLUXFILES_SECRET`** your FluxFiles core server uses to verify
|
|
40
|
+
tokens (HS256, **must be ≥ 32 bytes**). Keep it server-side only.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install fluxfiles-token
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Mint a token
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from fluxfiles_token import create_token
|
|
54
|
+
|
|
55
|
+
token = create_token(
|
|
56
|
+
user_id="user-42",
|
|
57
|
+
perms=["read", "write"],
|
|
58
|
+
disks=["local", "s3"],
|
|
59
|
+
prefix="users/42", # scope the user to their own directory
|
|
60
|
+
max_upload_mb=25,
|
|
61
|
+
allowed_ext=["png", "jpg", "pdf"],
|
|
62
|
+
ttl=3600, # seconds
|
|
63
|
+
# secret=..., # or omit to read FLUXFILES_SECRET from the environment
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Role & edition presets
|
|
68
|
+
|
|
69
|
+
Mint-time-only DX sugar — they expand into ordinary claims and are never
|
|
70
|
+
themselves a JWT claim. Explicit kwargs always win over a preset default.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from fluxfiles_token import create_token
|
|
74
|
+
|
|
75
|
+
# perms=["read", "write"], owner_only=True, allow_extract=True,
|
|
76
|
+
# allow_chmod=False (the "editor" bundle).
|
|
77
|
+
token = create_token(user_id="user-42", role="editor")
|
|
78
|
+
|
|
79
|
+
# Every module claim the "enterprise" edition grants — license/module
|
|
80
|
+
# install are still the real gate; a preset just defaults the per-tenant claim.
|
|
81
|
+
token = create_token(user_id="tenant-9", edition="enterprise")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Enable Import from URL
|
|
85
|
+
|
|
86
|
+
Import-from-URL is **off by default**. Turn it on for a token by setting the
|
|
87
|
+
import options — no server-side per-tenant config is needed:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
token = create_token(
|
|
91
|
+
user_id="user-42",
|
|
92
|
+
perms=["read", "write"],
|
|
93
|
+
allow_url_import=True, # required — enables the feature
|
|
94
|
+
max_import_mb=20, # optional — cap per import (MB)
|
|
95
|
+
import_url_allowlist=["*.unsplash.com"], # optional — restrict source hosts
|
|
96
|
+
# import_path, import_rate_limit, import_concurrency also supported
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The core then accepts `POST /api/fm/import-url` for that token (SSRF-guarded,
|
|
101
|
+
sharing the quota/dedup/variants pipeline). Server-wide defaults come from
|
|
102
|
+
`FLUXFILES_IMPORT_*` env vars on the core service.
|
|
103
|
+
|
|
104
|
+
### SFTP disk: chmod & SSH terminal
|
|
105
|
+
|
|
106
|
+
When the token targets a FluxFiles server that has an **SFTP disk** configured
|
|
107
|
+
(`SFTP_*` env on that server — see the
|
|
108
|
+
[core README](https://github.com/thai-pc/fluxfiles#sftp-disk-vps--shared-hosting)),
|
|
109
|
+
you can hand a token the SFTP file-manager tools. `chmod` is on by default for
|
|
110
|
+
SFTP; the **SSH terminal** is opt-in (it grants shell access as the SSH user):
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
token = create_token(
|
|
114
|
+
user_id="admin-7",
|
|
115
|
+
perms=["read", "write"],
|
|
116
|
+
disks=["sftp"], # an SFTP disk configured on the FluxFiles server
|
|
117
|
+
allow_chmod=True, # cPanel-style permissions (default on for SFTP)
|
|
118
|
+
allow_terminal=True, # SSH terminal — opt-in, off by default
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
These are **standalone-core** features: `fluxfiles-token` mints for a real
|
|
123
|
+
FluxFiles server (Docker / standalone), which serves them — they aren't
|
|
124
|
+
available behind the WordPress / Laravel-proxy adapters.
|
|
125
|
+
|
|
126
|
+
### BYOB — encrypt a user's own bucket credentials
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import os
|
|
130
|
+
from fluxfiles_token import create_byob_token
|
|
131
|
+
|
|
132
|
+
token = create_byob_token(
|
|
133
|
+
user_id="user-42",
|
|
134
|
+
byob_disks={
|
|
135
|
+
"my-s3": {
|
|
136
|
+
"driver": "s3",
|
|
137
|
+
"key": os.environ["USER_AWS_KEY"],
|
|
138
|
+
"secret": os.environ["USER_AWS_SECRET"],
|
|
139
|
+
"bucket": "user-personal-bucket",
|
|
140
|
+
"region": "us-east-1",
|
|
141
|
+
# "endpoint": "https://<acct>.r2.cloudflarestorage.com", # R2/MinIO/Spaces
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Credentials are AES-256-GCM encrypted into the token and decrypted only at
|
|
148
|
+
runtime by the FluxFiles server (which also re-validates the endpoint for
|
|
149
|
+
SSRF). `role`/`edition` presets are supported on BYOB tokens too.
|
|
150
|
+
|
|
151
|
+
### Verify / decode (optional)
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from fluxfiles_token import verify_token, decode_token
|
|
155
|
+
|
|
156
|
+
claims = verify_token(token) # checks HS256 signature + expiry, raises on failure
|
|
157
|
+
peek = decode_token(token) # decode only, NO verification — never trust for auth
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Django view
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from django.http import JsonResponse
|
|
164
|
+
from fluxfiles_token import create_token
|
|
165
|
+
|
|
166
|
+
def fluxfiles_token_view(request):
|
|
167
|
+
token = create_token(
|
|
168
|
+
user_id=str(request.user.id),
|
|
169
|
+
perms=["read", "write"],
|
|
170
|
+
prefix=f"users/{request.user.id}",
|
|
171
|
+
)
|
|
172
|
+
return JsonResponse({"token": token})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### FastAPI route
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from fastapi import APIRouter, Depends
|
|
179
|
+
from fluxfiles_token import create_token
|
|
180
|
+
|
|
181
|
+
router = APIRouter()
|
|
182
|
+
|
|
183
|
+
@router.get("/fluxfiles/token")
|
|
184
|
+
def fluxfiles_token(user=Depends(get_current_user)):
|
|
185
|
+
return {"token": create_token(user_id=user.id, perms=["read", "write"])}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## API
|
|
189
|
+
|
|
190
|
+
| Function | Description |
|
|
191
|
+
|----------|-------------|
|
|
192
|
+
| `create_token(**opts)` | Standard token. Mirrors PHP `fluxfiles_token()`. |
|
|
193
|
+
| `create_byob_token(**opts)` | Token with encrypted BYOB disk credentials. Mirrors `fluxfiles_byob_token()`. |
|
|
194
|
+
| `verify_token(token, secret=None)` | Verify HS256 signature + expiry; returns decoded claims or raises. |
|
|
195
|
+
| `decode_token(token)` | Decode without verifying (inspection/logging only). |
|
|
196
|
+
|
|
197
|
+
`create_token` keyword arguments: `secret=None`, `user_id`, `perms=None`,
|
|
198
|
+
`disks=None`, `prefix=None`, `max_upload_mb=None`, `allowed_ext=None`,
|
|
199
|
+
`ttl=None`, `owner_only=None`, `max_storage_mb=None`, `max_files=None`,
|
|
200
|
+
`edition=None`, `role=None`. Per-tenant overrides (omit to inherit the server
|
|
201
|
+
default): `ai_auto_tag=None` (bool), `rate_read=None` / `rate_write=None`
|
|
202
|
+
(req/min), `variants=None` (`{"thumb": .., "medium": .., "large": ..}` px),
|
|
203
|
+
and every other `allow_*`/module claim documented in
|
|
204
|
+
[`docs/CONFIG.md`](https://github.com/thai-pc/fluxfiles/blob/master/docs/CONFIG.md) —
|
|
205
|
+
plus a `claims: dict` escape hatch for any claim by its raw snake_case name.
|
|
206
|
+
`create_byob_token` replaces `disks` with `byob_disks` (a map of name →
|
|
207
|
+
S3-compatible/SFTP config) and does not take `max_storage_mb`/`max_files`
|
|
208
|
+
(matching the core), but accepts the same `role`/`edition`/per-tenant kwargs.
|
|
209
|
+
|
|
210
|
+
## Compatibility
|
|
211
|
+
|
|
212
|
+
Tokens and BYOB blobs are validated against the PHP core in CI: a
|
|
213
|
+
Python-minted token decodes in `JwtCompat::decode`, and BYOB credentials
|
|
214
|
+
round-trip both ways through `CredentialEncryptor` (HS256 + HKDF-SHA256 +
|
|
215
|
+
AES-256-GCM). Always mint tokens **on the server** — never ship
|
|
216
|
+
`FLUXFILES_SECRET` to the browser.
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
221
|
+
|
|
222
|
+
## Links
|
|
223
|
+
|
|
224
|
+
- Main repository: `https://github.com/thai-pc/fluxfiles`
|
|
225
|
+
- Documentation: `https://github.com/thai-pc/fluxfiles#python-server-side-token-sdk`
|
|
226
|
+
- Issues: `https://github.com/thai-pc/fluxfiles/issues`
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# FluxFiles for Python (server-side token SDK)
|
|
2
|
+
|
|
3
|
+
Mint [FluxFiles](https://github.com/thai-pc/fluxfiles) JWTs from any Python
|
|
4
|
+
backend (Django, FastAPI, Flask, …). Tokens are **byte-compatible with the PHP
|
|
5
|
+
core**, so non-PHP apps can issue access tokens — including encrypted BYOB
|
|
6
|
+
(Bring Your Own Bucket) credentials — without running PHP.
|
|
7
|
+
|
|
8
|
+
> **This package only issues tokens — it is not a backend.** You still run a
|
|
9
|
+
> FluxFiles **core service** (the file-manager backend that talks to storage;
|
|
10
|
+
> a PHP app, e.g. the Docker image) for the token to authenticate against.
|
|
11
|
+
> `fluxfiles-token` simply removes the need for *your app* to be PHP in order
|
|
12
|
+
> to mint those tokens.
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Python 3.10+
|
|
17
|
+
- A running FluxFiles **core service** the issued tokens authenticate against
|
|
18
|
+
(the SDK/iframe `endpoint` points at it).
|
|
19
|
+
- The same **`FLUXFILES_SECRET`** your FluxFiles core server uses to verify
|
|
20
|
+
tokens (HS256, **must be ≥ 32 bytes**). Keep it server-side only.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install fluxfiles-token
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Mint a token
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from fluxfiles_token import create_token
|
|
34
|
+
|
|
35
|
+
token = create_token(
|
|
36
|
+
user_id="user-42",
|
|
37
|
+
perms=["read", "write"],
|
|
38
|
+
disks=["local", "s3"],
|
|
39
|
+
prefix="users/42", # scope the user to their own directory
|
|
40
|
+
max_upload_mb=25,
|
|
41
|
+
allowed_ext=["png", "jpg", "pdf"],
|
|
42
|
+
ttl=3600, # seconds
|
|
43
|
+
# secret=..., # or omit to read FLUXFILES_SECRET from the environment
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Role & edition presets
|
|
48
|
+
|
|
49
|
+
Mint-time-only DX sugar — they expand into ordinary claims and are never
|
|
50
|
+
themselves a JWT claim. Explicit kwargs always win over a preset default.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from fluxfiles_token import create_token
|
|
54
|
+
|
|
55
|
+
# perms=["read", "write"], owner_only=True, allow_extract=True,
|
|
56
|
+
# allow_chmod=False (the "editor" bundle).
|
|
57
|
+
token = create_token(user_id="user-42", role="editor")
|
|
58
|
+
|
|
59
|
+
# Every module claim the "enterprise" edition grants — license/module
|
|
60
|
+
# install are still the real gate; a preset just defaults the per-tenant claim.
|
|
61
|
+
token = create_token(user_id="tenant-9", edition="enterprise")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Enable Import from URL
|
|
65
|
+
|
|
66
|
+
Import-from-URL is **off by default**. Turn it on for a token by setting the
|
|
67
|
+
import options — no server-side per-tenant config is needed:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
token = create_token(
|
|
71
|
+
user_id="user-42",
|
|
72
|
+
perms=["read", "write"],
|
|
73
|
+
allow_url_import=True, # required — enables the feature
|
|
74
|
+
max_import_mb=20, # optional — cap per import (MB)
|
|
75
|
+
import_url_allowlist=["*.unsplash.com"], # optional — restrict source hosts
|
|
76
|
+
# import_path, import_rate_limit, import_concurrency also supported
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The core then accepts `POST /api/fm/import-url` for that token (SSRF-guarded,
|
|
81
|
+
sharing the quota/dedup/variants pipeline). Server-wide defaults come from
|
|
82
|
+
`FLUXFILES_IMPORT_*` env vars on the core service.
|
|
83
|
+
|
|
84
|
+
### SFTP disk: chmod & SSH terminal
|
|
85
|
+
|
|
86
|
+
When the token targets a FluxFiles server that has an **SFTP disk** configured
|
|
87
|
+
(`SFTP_*` env on that server — see the
|
|
88
|
+
[core README](https://github.com/thai-pc/fluxfiles#sftp-disk-vps--shared-hosting)),
|
|
89
|
+
you can hand a token the SFTP file-manager tools. `chmod` is on by default for
|
|
90
|
+
SFTP; the **SSH terminal** is opt-in (it grants shell access as the SSH user):
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
token = create_token(
|
|
94
|
+
user_id="admin-7",
|
|
95
|
+
perms=["read", "write"],
|
|
96
|
+
disks=["sftp"], # an SFTP disk configured on the FluxFiles server
|
|
97
|
+
allow_chmod=True, # cPanel-style permissions (default on for SFTP)
|
|
98
|
+
allow_terminal=True, # SSH terminal — opt-in, off by default
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
These are **standalone-core** features: `fluxfiles-token` mints for a real
|
|
103
|
+
FluxFiles server (Docker / standalone), which serves them — they aren't
|
|
104
|
+
available behind the WordPress / Laravel-proxy adapters.
|
|
105
|
+
|
|
106
|
+
### BYOB — encrypt a user's own bucket credentials
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import os
|
|
110
|
+
from fluxfiles_token import create_byob_token
|
|
111
|
+
|
|
112
|
+
token = create_byob_token(
|
|
113
|
+
user_id="user-42",
|
|
114
|
+
byob_disks={
|
|
115
|
+
"my-s3": {
|
|
116
|
+
"driver": "s3",
|
|
117
|
+
"key": os.environ["USER_AWS_KEY"],
|
|
118
|
+
"secret": os.environ["USER_AWS_SECRET"],
|
|
119
|
+
"bucket": "user-personal-bucket",
|
|
120
|
+
"region": "us-east-1",
|
|
121
|
+
# "endpoint": "https://<acct>.r2.cloudflarestorage.com", # R2/MinIO/Spaces
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Credentials are AES-256-GCM encrypted into the token and decrypted only at
|
|
128
|
+
runtime by the FluxFiles server (which also re-validates the endpoint for
|
|
129
|
+
SSRF). `role`/`edition` presets are supported on BYOB tokens too.
|
|
130
|
+
|
|
131
|
+
### Verify / decode (optional)
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from fluxfiles_token import verify_token, decode_token
|
|
135
|
+
|
|
136
|
+
claims = verify_token(token) # checks HS256 signature + expiry, raises on failure
|
|
137
|
+
peek = decode_token(token) # decode only, NO verification — never trust for auth
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Django view
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from django.http import JsonResponse
|
|
144
|
+
from fluxfiles_token import create_token
|
|
145
|
+
|
|
146
|
+
def fluxfiles_token_view(request):
|
|
147
|
+
token = create_token(
|
|
148
|
+
user_id=str(request.user.id),
|
|
149
|
+
perms=["read", "write"],
|
|
150
|
+
prefix=f"users/{request.user.id}",
|
|
151
|
+
)
|
|
152
|
+
return JsonResponse({"token": token})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### FastAPI route
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from fastapi import APIRouter, Depends
|
|
159
|
+
from fluxfiles_token import create_token
|
|
160
|
+
|
|
161
|
+
router = APIRouter()
|
|
162
|
+
|
|
163
|
+
@router.get("/fluxfiles/token")
|
|
164
|
+
def fluxfiles_token(user=Depends(get_current_user)):
|
|
165
|
+
return {"token": create_token(user_id=user.id, perms=["read", "write"])}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## API
|
|
169
|
+
|
|
170
|
+
| Function | Description |
|
|
171
|
+
|----------|-------------|
|
|
172
|
+
| `create_token(**opts)` | Standard token. Mirrors PHP `fluxfiles_token()`. |
|
|
173
|
+
| `create_byob_token(**opts)` | Token with encrypted BYOB disk credentials. Mirrors `fluxfiles_byob_token()`. |
|
|
174
|
+
| `verify_token(token, secret=None)` | Verify HS256 signature + expiry; returns decoded claims or raises. |
|
|
175
|
+
| `decode_token(token)` | Decode without verifying (inspection/logging only). |
|
|
176
|
+
|
|
177
|
+
`create_token` keyword arguments: `secret=None`, `user_id`, `perms=None`,
|
|
178
|
+
`disks=None`, `prefix=None`, `max_upload_mb=None`, `allowed_ext=None`,
|
|
179
|
+
`ttl=None`, `owner_only=None`, `max_storage_mb=None`, `max_files=None`,
|
|
180
|
+
`edition=None`, `role=None`. Per-tenant overrides (omit to inherit the server
|
|
181
|
+
default): `ai_auto_tag=None` (bool), `rate_read=None` / `rate_write=None`
|
|
182
|
+
(req/min), `variants=None` (`{"thumb": .., "medium": .., "large": ..}` px),
|
|
183
|
+
and every other `allow_*`/module claim documented in
|
|
184
|
+
[`docs/CONFIG.md`](https://github.com/thai-pc/fluxfiles/blob/master/docs/CONFIG.md) —
|
|
185
|
+
plus a `claims: dict` escape hatch for any claim by its raw snake_case name.
|
|
186
|
+
`create_byob_token` replaces `disks` with `byob_disks` (a map of name →
|
|
187
|
+
S3-compatible/SFTP config) and does not take `max_storage_mb`/`max_files`
|
|
188
|
+
(matching the core), but accepts the same `role`/`edition`/per-tenant kwargs.
|
|
189
|
+
|
|
190
|
+
## Compatibility
|
|
191
|
+
|
|
192
|
+
Tokens and BYOB blobs are validated against the PHP core in CI: a
|
|
193
|
+
Python-minted token decodes in `JwtCompat::decode`, and BYOB credentials
|
|
194
|
+
round-trip both ways through `CredentialEncryptor` (HS256 + HKDF-SHA256 +
|
|
195
|
+
AES-256-GCM). Always mint tokens **on the server** — never ship
|
|
196
|
+
`FLUXFILES_SECRET` to the browser.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
- Main repository: `https://github.com/thai-pc/fluxfiles`
|
|
205
|
+
- Documentation: `https://github.com/thai-pc/fluxfiles#python-server-side-token-sdk`
|
|
206
|
+
- Issues: `https://github.com/thai-pc/fluxfiles/issues`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fluxfiles-token"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Server-side Python SDK for minting FluxFiles JWTs (plain + BYOB), byte-compatible with the PHP core and @fluxfiles/node"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [{ name = "thai-pc" }]
|
|
9
|
+
keywords = ["fluxfiles", "jwt", "token", "file-manager", "s3", "r2", "byob", "server"]
|
|
10
|
+
dependencies = ["PyJWT>=2.8,<3", "cryptography>=42"]
|
|
11
|
+
|
|
12
|
+
[project.optional-dependencies]
|
|
13
|
+
dev = ["pytest>=8", "mypy>=1.10", "ruff"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/thai-pc/fluxfiles#python-server-side-token-sdk"
|
|
17
|
+
Repository = "https://github.com/thai-pc/fluxfiles"
|
|
18
|
+
Issues = "https://github.com/thai-pc/fluxfiles/issues"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/fluxfiles_token"]
|
|
26
|
+
|
|
27
|
+
[tool.mypy]
|
|
28
|
+
python_version = "3.10"
|
|
29
|
+
strict = true
|
|
30
|
+
files = ["src"]
|
|
31
|
+
|
|
32
|
+
[tool.ruff]
|
|
33
|
+
target-version = "py310"
|
|
34
|
+
line-length = 110
|
|
35
|
+
src = ["src", "tests"]
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""fluxfiles-token: mint and verify FluxFiles JWTs from Python.
|
|
2
|
+
|
|
3
|
+
Byte-compatible with the PHP core (`packages/core/embed.php`,
|
|
4
|
+
`CredentialEncryptor.php`) and a 1:1 port of `@fluxfiles/node`
|
|
5
|
+
(camelCase -> snake_case). See docs/PYTHON-TOKEN-SDK-DESIGN.md.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .crypto import decrypt_byob, encrypt_byob
|
|
9
|
+
from .exceptions import FluxFilesByobError, FluxFilesTokenError
|
|
10
|
+
from .token import EDITION_PRESETS, ROLE_PRESETS, create_byob_token, create_token
|
|
11
|
+
from .types import (
|
|
12
|
+
ByobDiskConfig,
|
|
13
|
+
ByobS3DiskConfig,
|
|
14
|
+
ByobSftpDiskConfig,
|
|
15
|
+
FluxClaims,
|
|
16
|
+
FluxPermission,
|
|
17
|
+
)
|
|
18
|
+
from .verify import decode_token, verify_token
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"create_token",
|
|
22
|
+
"create_byob_token",
|
|
23
|
+
"verify_token",
|
|
24
|
+
"decode_token",
|
|
25
|
+
"encrypt_byob",
|
|
26
|
+
"decrypt_byob",
|
|
27
|
+
"ROLE_PRESETS",
|
|
28
|
+
"EDITION_PRESETS",
|
|
29
|
+
"FluxFilesTokenError",
|
|
30
|
+
"FluxFilesByobError",
|
|
31
|
+
"ByobDiskConfig",
|
|
32
|
+
"ByobS3DiskConfig",
|
|
33
|
+
"ByobSftpDiskConfig",
|
|
34
|
+
"FluxClaims",
|
|
35
|
+
"FluxPermission",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
__version__ = "0.1.0"
|