Add tutorials/03-server-modules-security.md
Feat(tutorials): add stubs for 03 Security (RBAC)
This commit is contained in:
parent
fff0dec262
commit
ec407aae5b
|
@ -0,0 +1,87 @@
|
||||||
|
**`tutorials/03-server-modules-security.md`**
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
# 03 — Server Modules & Security
|
||||||
|
|
||||||
|
## Roles & Helper
|
||||||
|
```python
|
||||||
|
import anvil.server
|
||||||
|
from anvil import users, secrets
|
||||||
|
|
||||||
|
ROLES = {"viewer","editor","admin"}
|
||||||
|
|
||||||
|
def require_role(*roles):
|
||||||
|
u = users.get_user()
|
||||||
|
if not u: raise PermissionError("Login required")
|
||||||
|
if not set(roles).intersection(set(u.get('roles', []))):
|
||||||
|
raise PermissionError("Insufficient role")
|
||||||
|
````
|
||||||
|
|
||||||
|
## Validated Callable
|
||||||
|
|
||||||
|
```python
|
||||||
|
@anvil.server.callable
|
||||||
|
def secure_greet(name):
|
||||||
|
name = (name or "").strip()
|
||||||
|
if not (1 <= len(name) <= 64): raise ValueError("Bad name")
|
||||||
|
return f"Top-secret hello, {name}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Secrets + Fake External
|
||||||
|
|
||||||
|
```python
|
||||||
|
API_KEY = secrets.get_secret('DEMO_API_KEY') # set in Anvil
|
||||||
|
```
|
||||||
|
|
||||||
|
## Simple Rate Limit (per user)
|
||||||
|
|
||||||
|
Use a module-level dict `{user_id: [timestamps...]}`; trim to last 60s, allow ≤5.
|
||||||
|
|
||||||
|
## Client UX
|
||||||
|
|
||||||
|
* Try/catch PermissionError → Notification("You need editor role").
|
||||||
|
* Disable button while awaiting server.
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
**`tutorials/04-uplink-file-hash.md`**
|
||||||
|
```markdown
|
||||||
|
# 04 — Uplink: Local File Hash
|
||||||
|
|
||||||
|
## Uplink Script (local)
|
||||||
|
```python
|
||||||
|
import anvil.server, hashlib
|
||||||
|
@anvil.server.callable("hash_file")
|
||||||
|
def hash_file(buffer):
|
||||||
|
h = hashlib.sha256(buffer.get_bytes())
|
||||||
|
return h.hexdigest()
|
||||||
|
anvil.server.connect("YOUR-UPLINK-KEY")
|
||||||
|
anvil.server.wait_forever()
|
||||||
|
````
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
* FileLoader → `file_loader_1_change`: call `hash_file` with the Media object.
|
||||||
|
* Show result; if timeout/conn error → “Uplink offline”.
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
**`tutorials/05-auth-roles-ui.md`**
|
||||||
|
```markdown
|
||||||
|
# 05 — Auth: Users + Roles + UI Gating
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
Enable Users service. Add roles to users (e.g., `editor`, `admin`).
|
||||||
|
|
||||||
|
## Server
|
||||||
|
Use `require_role` from Tutorial 03 to gate `dangerous_action`.
|
||||||
|
|
||||||
|
## Client
|
||||||
|
- Add Login/Logout buttons.
|
||||||
|
- Show current user + roles.
|
||||||
|
- Hide/show editor-only actions via `component.visible`.
|
||||||
|
|
||||||
|
## Demo Flow
|
||||||
|
- Viewer logs in → can’t see editor actions.
|
||||||
|
- Editor logs in → sees & runs gated action.
|
||||||
|
````
|
Loading…
Reference in New Issue