Badge search results list (ae_comp__badge_obj_li): - 4 action buttons per row: Print, Review (nav link), Copy Link (clipboard), Email Link - Visibility rules: unprinted-only for non-edit mode; all non-hidden for trusted+edit - Plain name display (User/EyeOff icon) — name is no longer a print link - Obscured email for non-trusted users - Debug row (ID, CR, UP, PC, FP, LP) in edit mode - All icons converted to Lucide (Font Awesome removed) Badge print page (/print): - 3 header action buttons: Print Now, Review (nav), Email Link - Removed old [badge_id]/+page.svelte placeholder (moved to trash) - Added is_trusted, is_edit_mode, print state derived vars - "Already printed Nx — last [timestamp]" warning inline with name - Removed unused imports (browser, onMount, events_slct) Badge review page (/review): - 3 header action buttons: Print (nav), Copy Link (clipboard), Email Link - Added events_loc for email placeholder + title event name - Added is_edit_mode, print_count, is_printed, copy_status - FA icons replaced with Lucide (ShieldCheck, UserCheck, User) - Title now includes event name (was missing) Infrastructure: - print/+page.ts and review/+page.ts added (non-blocking badge loaders) - ae_comp__badge_review_form.svelte stub created (fields pending) - Fixed: components no longer write to $ae_loc.edit_mode (critical bug) Docs: - NEW: AE__Permissions_and_Security.md — full permissions hierarchy reference - NEW: PROJECT__AE_Events_Badges_Review_Print.md — agent task brief for review form + print font controls - UPDATED: MODULE__AE_Events_Badges.md rev 5 — field permissions spec, header buttons, still-needed list by priority Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
175 lines
6.9 KiB
Markdown
175 lines
6.9 KiB
Markdown
# Aether — Permissions and Security
|
||
|
||
**Last updated:** 2026-02-27
|
||
**Source of truth:** `src/lib/ae_utils/ae_utils__perm_checks.ts`, `src/lib/stores/ae_stores.ts`
|
||
|
||
---
|
||
|
||
## Access Level Hierarchy
|
||
|
||
Highest to lowest. Each level **inherits all access from every level below it**.
|
||
|
||
| Level | `access_type` string | Typical Use |
|
||
| --- | --- | --- |
|
||
| Super | `super` | OSIT internal — full system access |
|
||
| Manager | `manager` | Account managers |
|
||
| Administrator | `administrator` | Event/account admins |
|
||
| Trusted | `trusted` | **Onsite staff** — site passcode or AE login |
|
||
| Public | `public` | Site-wide passcode granted |
|
||
| Authenticated | `authenticated` | Identity verified (e.g. IDAA Novi UUID) |
|
||
| Anonymous | `anonymous` | Default — not signed in |
|
||
|
||
> **Note on Public vs Authenticated:** `public` is a *site-wide* unlock (anyone with the passcode). `authenticated` verifies a *specific identity*. In the hierarchy, public outranks authenticated because it implies broader site access.
|
||
|
||
---
|
||
|
||
## `$ae_loc` Store — Permission Flags
|
||
|
||
`$ae_loc` is a `persisted()` store (backed by localStorage). Key fields:
|
||
|
||
```typescript
|
||
$ae_loc.access_type // string: current access type ('anonymous', 'trusted', etc.)
|
||
|
||
// Cumulative boolean flags (true = "you have AT LEAST this level")
|
||
$ae_loc.anonymous_access // always true
|
||
$ae_loc.authenticated_access // true from authenticated and above
|
||
$ae_loc.public_access // true from public and above
|
||
$ae_loc.trusted_access // true from trusted and above ← most-used gate
|
||
$ae_loc.administrator_access // true from administrator and above
|
||
$ae_loc.manager_access // true from manager and above
|
||
$ae_loc.super_access // true only at super
|
||
|
||
// Exclusive check flags (true = "you are EXACTLY this level")
|
||
$ae_loc.trusted_check // true only if access_type === 'trusted'
|
||
$ae_loc.administrator_check // etc.
|
||
// (rarely needed — prefer the _access flags)
|
||
|
||
// Behavior flags
|
||
$ae_loc.edit_mode // boolean — user preference, see below
|
||
$ae_loc.adv_mode // boolean — advanced mode toggle
|
||
```
|
||
|
||
### Additional intermediate levels (in permission checks, not in hierarchy order)
|
||
`support`, `assistant`, `verified`, `provisional` — appear in `_access` flags but are not part of the canonical `access_level_order`. Treat as internal/intermediate.
|
||
|
||
---
|
||
|
||
## Edit Mode — Critical Rules
|
||
|
||
`$ae_loc.edit_mode` is a **user preference**, not a permission level.
|
||
|
||
**Rules that must never be broken:**
|
||
1. **Components must never write to `$ae_loc.edit_mode`** — only the system menu toggle and sign-out/permission-drop handlers may change it.
|
||
2. Edit mode is only available to `trusted` and above in 95% of modules (the toggle is hidden from lower-access users).
|
||
3. Edit mode persists across navigation — it is NOT reset by page loads or component mounts.
|
||
4. Sign-out and permission drops to below `authenticated` should reset `edit_mode` to `false`.
|
||
|
||
> **Background:** A bug was fixed (2026-02-27) where `ae_comp__badge_obj_view.svelte` was writing `$ae_loc.edit_mode = false` in a data-loading `$effect`, silently overriding the user's preference on every navigation to the badge print page.
|
||
|
||
---
|
||
|
||
## Authentication Methods
|
||
|
||
| Method | Grants | Used For |
|
||
| --- | --- | --- |
|
||
| Site passcode (`site_access_code_kv`) | `trusted`, `public`, or `authenticated` | Onsite staff and event attendees |
|
||
| AE Username + Password | `trusted` and above | Staff with AE accounts |
|
||
| Novi UUID | `authenticated` | IDAA members (Novi membership system) |
|
||
|
||
Passcodes are stored per-level in `$ae_loc.site_access_code_kv`:
|
||
```typescript
|
||
site_access_code_kv: {
|
||
administrator: null, // highest passcode tier
|
||
trusted: null, // onsite staff passcode
|
||
public: 'public1980', // example
|
||
authenticated: 'auth1980'
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Utility Functions
|
||
|
||
### `process_permission_checks(access_type: string)`
|
||
Returns a full permission object (`_check` and `_access` flags) for a given access type string. Used when access type changes to update `$ae_loc`.
|
||
|
||
```typescript
|
||
import { process_permission_checks } from '$lib/ae_utils/ae_utils__perm_checks';
|
||
const checks = process_permission_checks('trusted');
|
||
// checks.trusted_access === true
|
||
// checks.administrator_access === false
|
||
```
|
||
|
||
### `compare_access_levels(level_a, level_b)`
|
||
Returns `1` if `level_a` is higher, `-1` if lower, `0` if equal. Useful for threshold comparisons.
|
||
|
||
---
|
||
|
||
## Privacy and Security Rules
|
||
|
||
### IDAA — International Doctors in Alcoholics Anonymous
|
||
- **ALL IDAA content is private. Always. No exceptions.**
|
||
- BB (Bulletin Board / Posts), Archives, Recovery Meetings — all require authentication.
|
||
- IDAA users authenticate via Novi UUID at `authenticated` level or higher.
|
||
- A prior agent accidentally exposed IDAA BB data publicly — treat any IDAA exposure as Sev-1.
|
||
|
||
### Journals
|
||
- Private personal data. Always authenticated. Passcode/encryption features exist.
|
||
- Never expose journal content publicly.
|
||
|
||
### `PUBLIC_AE_API_SECRET_KEY`
|
||
- Ongoing Sev-1 audit. Do not introduce new usages.
|
||
- Prefer per-request API key headers (`x-aether-api-key` + `x-account-id`).
|
||
|
||
### Email Display
|
||
Non-trusted users must never see a full email address. Obscure using:
|
||
```typescript
|
||
// joh***@example.com
|
||
function obscure_email(email: string): string {
|
||
const at = email.indexOf('@');
|
||
if (at < 0) return email;
|
||
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
|
||
}
|
||
```
|
||
This pattern lives in `ae_comp__badge_obj_li.svelte` — move to `ae_utils` if needed elsewhere.
|
||
|
||
---
|
||
|
||
## Module-Specific Permission Patterns
|
||
|
||
### Events — Badges
|
||
|
||
| Scenario | Visibility | Print Action | Review Actions |
|
||
| --- | --- | --- | --- |
|
||
| Anonymous / below trusted | Unprinted only | None (name display only) | Email Review Link button (→ email API) |
|
||
| Trusted, not Edit Mode | Unprinted only | Clickable (first print) | Email Review Link button |
|
||
| Trusted, Edit Mode | All non-hidden | Clickable incl. reprint; shows `Nx` count | Email Review Link + direct Review Link (clipboard) |
|
||
|
||
- Print count badge: shown as `Nx` (e.g. `2×`) next to the printer icon when `print_count >= 1`
|
||
- Edit mode for badges: limited to `trusted_access` users (toggle hidden from lower levels)
|
||
- `person_passcode` field (for attendee-gated review URL): **not yet in DB** as of 2026-02-27
|
||
|
||
### IDAA
|
||
- Auth gate test must be the **first test** in any test file — privacy enforcement is a hard requirement.
|
||
- Default required permission: `trusted_access` or higher for module access.
|
||
|
||
---
|
||
|
||
## Common Template Patterns
|
||
|
||
```svelte
|
||
<!-- Gate on trusted access -->
|
||
{#if $ae_loc.trusted_access}
|
||
|
||
<!-- Gate on edit mode (always check trusted too — edit mode alone is insufficient) -->
|
||
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
|
||
|
||
<!-- Gate on administrator -->
|
||
{#if $ae_loc.administrator_access}
|
||
|
||
<!-- Show full vs obscured email -->
|
||
{$ae_loc.trusted_access ? email : obscure_email(email)}
|
||
```
|
||
|
||
> Never gate purely on `$ae_loc.edit_mode` without also checking a permission level. Edit mode is a UI preference, not a permission grant.
|