Skip to content

Latest commit

 

History

History
140 lines (102 loc) · 8.85 KB

File metadata and controls

140 lines (102 loc) · 8.85 KB

Multi-User

Bindery v1.0 introduces per-user library scoping: authors, books, downloads, quality profiles, and metadata profiles can be owned by a specific user, and users log in with their own credentials.

Root folders are not per-user. They are a single shared, admin-managed pool (Settings → Root Folders), and import destinations resolve per-author plus a global default — there is no per-user library directory today. Regular users don't get their own root folders. Per-user root folders are a tracked future enhancement, not current behaviour.

Important — data isolation is opt-in. Per-user data isolation is gated behind the BINDERY_ENFORCE_TENANCY environment variable, which defaults OFF. With it unset (the default), any authenticated user can see and manage all data — Bindery behaves like a single-user instance regardless of how many accounts exist. To make users see only their own library, set BINDERY_ENFORCE_TENANCY=true.

When enforcement is on, Bindery scopes:

  • Tier-2 join-scoped resources — download queue, history, pending grabs, and the OPDS catalogue — to the requesting user.
  • Per-user resources — each user's own authors, books, profiles, API key, password, and notification preferences. (Root folders are not per-user; see the note above.)

Admins see everything in list views. With enforcement on, an admin is never filtered by ownership: the authors and books list endpoints (and the OPDS feed) return all users' libraries plus unowned/global rows, the same way an admin can already open any single item by ID. This is a shared library across admins, by design — it does not widen access, it makes lists consistent with per-item access. Non-admin (user) accounts stay isolated to their own rows plus unowned/global rows. Requests authenticated by API key (or in disabled / local-only auth modes) carry no user identity and are likewise unscoped.

Role-based gating of admin-only configuration (indexers, download clients, user management, system settings) applies in both modes — that does not depend on BINDERY_ENFORCE_TENANCY. The flag only controls whether library data is partitioned per user.

For upgrade instructions and migration steps, see docs/upgrade-v1.md.

Role model

Two roles exist: admin and user.

  • The first account created through the /setup wizard is always admin.
  • Users created by an admin via the Users page (the people icon in the header) default to the user role.
  • OIDC auto-provisioned users get the user role by default. To make the IdP authoritative for the admin role, set BINDERY_OIDC_ADMIN_GROUP so users in that IdP group are promoted automatically; see OIDC role mapping in docs/auth-oidc.md.
  • An admin can change any user's role at any time: Make admin / Make user on the Users page, or PUT /api/v1/auth/users/{id}/role with {"role": "admin"}.

Capability matrix

Action admin user
View and manage own authors/books/downloads Yes Yes
View and manage own quality/metadata profiles Yes Yes
Manage root folders (single shared/global pool) Yes No
Change own password and API key Yes Yes
Configure own notification preferences Yes Yes
View other users' library data Yes No
Manage other users' library data Yes No
Create, edit, delete users Yes No
Change user roles Yes No
Configure indexers Yes No
Configure download clients Yes No
Configure system-wide settings Yes No
View admin settings tabs in UI Yes No
Trigger system-level operations (backup, scan, migrate) Yes No

User management

Creating a user

Users → Add User (admin only, via the people icon in the header), or via API:

curl -X POST http://bindery:8787/api/v1/auth/users \
  -H "X-Api-Key: <admin-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "password": "correct-horse-battery", "role": "user"}'

OIDC users are created automatically on first login — no pre-creation needed unless BINDERY_OIDC_AUTO_PROVISION=false.

Listing users

curl http://bindery:8787/api/v1/auth/users \
  -H "X-Api-Key: <admin-api-key>"

Returns: [{"id": 1, "username": "admin", "role": "admin", "last_seen": "..."}]. Passwords and OIDC credentials are never returned.

Updating a user

# Promote to admin
curl -X PUT http://bindery:8787/api/v1/auth/users/2/role \
  -H "X-Api-Key: <admin-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'

Deleting a user

curl -X DELETE http://bindery:8787/api/v1/auth/users/2 \
  -H "X-Api-Key: <admin-api-key>"

Deleting a user does not delete their library data. Authors, books, and downloads owned by that user remain in the database but become inaccessible through the normal UI. Reassign or remove the user's data before deleting:

  1. While signed in as an admin, review the account's content from the regular Authors and Books pages — admin list views are never ownership-filtered, so every user's rows are visible there.
  2. Delete anything that should not be kept.
  3. Then delete the user.

Settings UI layout

Everyone sees the General tab (appearance, downloads, file naming, storage, backup, security — including their own API key and password change) and About. The remaining tabs are admin-only, in four groups:

  • Sources — Indexers, Download Clients, Notifications
  • Library — Quality Profiles, Metadata Profiles, Root Folders
  • Integrations — Calibre, Audiobookshelf, Grimmory, API Keys
  • System — Import, Blocklist, Logs

Non-admins who open an admin tab are redirected back to General; admin API routes return 403. Users are managed on the dedicated Users page (the people icon in the header), not inside Settings.

CSRF tokens

v1.0 replaces the X-Requested-With header check with a proper double-submit CSRF token on all session-cookie-authenticated mutations.

Browser users: the UI handles this transparently.

API scripts using session cookies: fetch a token first:

TOKEN=$(curl -s -b "bindery_session=<value>" \
  http://bindery:8787/api/v1/auth/csrf | jq -r .token)

curl -X POST http://bindery:8787/api/v1/author \
  -b "bindery_session=<value>" \
  -H "X-CSRF-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ursula K. Le Guin"}'

API scripts using X-Api-Key: CSRF is not required — API-key requests bypass the check entirely. Existing automation continues to work without changes.

See also

Troubleshooting

Symptom Cause Fix
User B can see User A's authors or books via the API BINDERY_ENFORCE_TENANCY is unset — this is the documented default behaviour, not a bug This is expected when tenancy enforcement is off: all authenticated users share one library view. To partition library data per user, set BINDERY_ENFORCE_TENANCY=true and restart. (Admins can access all users' data regardless of this flag, by design.)
Data remains after a user is deleted DELETE /auth/users/{id} does not cascade to library data Reassign or delete the user's authors and books before deleting the user account (see "Deleting a user" above).
403 Forbidden on an API call that worked before v1.0 Session-cookie mutations now require X-CSRF-Token Switch callers to X-Api-Key auth (CSRF-exempt), or add a GET /auth/csrf preflight to your script (see "CSRF tokens" above).
Admin locked out — no admin account exists or all admins deleted User row has role='user' or all admin rows were removed Recover via direct DB update (no Bindery restart needed if you can write to the DB file): sqlite3 /config/bindery.db "UPDATE users SET role='admin' WHERE username='<your-username>';" — or in Kubernetes: kubectl exec deploy/bindery -- sqlite3 /config/bindery.db "UPDATE users SET role='admin' WHERE id=1;"
OIDC user auto-provisioned as user but should be admin BINDERY_OIDC_ADMIN_GROUP not set, so the IdP group is never consulted Set BINDERY_OIDC_ADMIN_GROUP to the IdP group name (and BINDERY_OIDC_GROUP_CLAIM if your IdP emits groups under a non-default claim). To promote a single existing account without group mapping: PUT /api/v1/auth/users/{id}/role with {"role": "admin"}.
Non-admin user can reach admin settings page in UI Browser cached a pre-v1.0 session or route bundle Hard-refresh the page (Ctrl+Shift+R). If it persists, log out and back in. The backend enforces role checks regardless of what the UI renders.