diff --git a/tutorials/03-server-modules-security.md b/tutorials/03-server-modules-security.md new file mode 100644 index 0000000..8ea4132 --- /dev/null +++ b/tutorials/03-server-modules-security.md @@ -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. +```` \ No newline at end of file