|
| 1 | +# Frequently Asked Questions |
| 2 | + |
| 3 | +## I forgot my admin password. How do I reset it? |
| 4 | + |
| 5 | +If you're locked out of your account, you can reset the password directly in the database by running a one-liner inside the running container: |
| 6 | + |
| 7 | +```bash |
| 8 | +docker exec <container_name> python3 -c " |
| 9 | +from passlib.context import CryptContext |
| 10 | +import sqlite3 |
| 11 | +pwd = CryptContext(schemes=['bcrypt_sha256']).hash('<your_new_password>') |
| 12 | +db = sqlite3.connect('/app/data/grimoire.db') |
| 13 | +db.execute(\"UPDATE users SET hashed_password = ? WHERE username = ?\", (pwd, '<your_username>')) |
| 14 | +db.commit() |
| 15 | +db.close() |
| 16 | +print('Done') |
| 17 | +" |
| 18 | +``` |
| 19 | + |
| 20 | +Replace `<container_name>`, `<your_new_password>`, and `<your_username>` with the correct values for your setup. The container name is typically `grimoire` unless you changed it in your compose file. |
| 21 | + |
| 22 | +Alternatively, you can create a new admin account by [pre-seeding a users file](../README.md#pre-seeding-users) and restarting the stack. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## The scanner finds no books after I reorganized my library. |
| 27 | + |
| 28 | +Grimoire expects a specific folder structure inside your library volume mount. The scanner looks for a **`books/`** subfolder at the root of the mount: |
| 29 | + |
| 30 | +``` |
| 31 | +/library/ ← volume mount target |
| 32 | + books/ |
| 33 | + D&D 5e/ |
| 34 | + Core Rules/ |
| 35 | + Players Handbook.pdf |
| 36 | + Pathfinder/ |
| 37 | + ... |
| 38 | + maps/ ← optional |
| 39 | + tokens/ ← optional |
| 40 | +``` |
| 41 | + |
| 42 | +If your PDFs live directly under the mounted folder (e.g. `RPGs/<GameSystem>/...` without a `books/` subfolder), the scanner will find nothing. |
| 43 | + |
| 44 | +**Fix** — mount your library folder as `/library/books` instead of `/library`: |
| 45 | + |
| 46 | +```yaml |
| 47 | +volumes: |
| 48 | + - /path/to/your/rpgs:/library/books:ro |
| 49 | + - ./grimoire/data:/data |
| 50 | +``` |
| 51 | +
|
| 52 | +This lets you keep your existing file structure on the host without adding an extra `books/` folder. After updating the compose file, restart the stack and trigger a rescan from the admin panel. |
| 53 | + |
| 54 | +> **Note:** "Remove missing files" deletes database records for files that can't be found at their expected paths. If you moved files around on the host before the volume mount was correct, those records were removed. Re-mounting correctly and rescanning will re-add everything. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## How do I configure OIDC with Authentik? |
| 59 | + |
| 60 | +Below is a complete setup for Authentik that maps groups to Grimoire roles and controls NSFW access via a separate group. |
| 61 | + |
| 62 | +### 1. Create the groups |
| 63 | + |
| 64 | +In Authentik, create these groups: |
| 65 | + |
| 66 | +| Group | Purpose | |
| 67 | +|---|---| |
| 68 | +| `grimoire-admin` | Full admin access | |
| 69 | +| `grimoire-gm` | GM role | |
| 70 | +| `grimoire-player` | Player role | |
| 71 | +| `nsfw` | Grants explicit content access to non-admin users | |
| 72 | + |
| 73 | +Assign your users to the appropriate groups. |
| 74 | + |
| 75 | +### 2. Create a custom scope |
| 76 | + |
| 77 | +Go to **Customization → Property Mappings** and create two **Scope Mappings**. |
| 78 | + |
| 79 | +**Name: `Grimoire Groups`** |
| 80 | +**Scope: `groups`** — maps Authentik groups to Grimoire roles: |
| 81 | + |
| 82 | +```python |
| 83 | +groups = [group.name for group in user.ak_groups.all()] |
| 84 | +
|
| 85 | +grimoire_groups = [] |
| 86 | +if "grimoire-admin" in groups: |
| 87 | + grimoire_groups.append("admin") |
| 88 | +if "grimoire-gm" in groups: |
| 89 | + grimoire_groups.append("gm") |
| 90 | +if "grimoire-player" in groups: |
| 91 | + grimoire_groups.append("player") |
| 92 | +
|
| 93 | +return {"groups": grimoire_groups} |
| 94 | +``` |
| 95 | + |
| 96 | +**Name: `Grimoire Permissions`** |
| 97 | +**Scope: `permissions`** — controls explicit content access: |
| 98 | + |
| 99 | +```python |
| 100 | +groups = [group.name for group in user.ak_groups.all()] |
| 101 | +
|
| 102 | +if "grimoire-admin" in groups: |
| 103 | + return { |
| 104 | + "permissions": { |
| 105 | + "viewNSFW": True |
| 106 | + } |
| 107 | + } |
| 108 | +
|
| 109 | +explicit = "nsfw" in groups |
| 110 | +
|
| 111 | +return { |
| 112 | + "permissions": { |
| 113 | + "viewNSFW": explicit |
| 114 | + } |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +### 3. Configure the provider |
| 119 | + |
| 120 | +In your Authentik OAuth2/OIDC provider: |
| 121 | + |
| 122 | +1. Open (or create) your **OAuth2/OpenID Provider**. |
| 123 | +2. Scroll to **Advanced Protocol Settings**. |
| 124 | +3. Under **Scopes**, click **Add** and select both `Grimoire Groups` and `Grimoire Permissions` from the list. |
| 125 | +4. Save the provider. |
| 126 | +5. Note your **Client ID** and **Client Secret** from the provider's overview page. |
| 127 | + |
| 128 | +### 4. Configure Grimoire |
| 129 | + |
| 130 | +In Grimoire **Settings → Authentication**: |
| 131 | + |
| 132 | +1. Set **Issuer URL** to your Authentik application's issuer (e.g. `https://authentik.example.com/application/o/<app-slug>/`) and click **Autopopulate**. |
| 133 | + > Authentik's token issuer often differs from the provider URL. If login fails with an issuer mismatch, copy the `iss` value from a decoded token and paste it into the **Token Issuer** field. |
| 134 | +2. Paste your **Client ID** and **Client Secret**. |
| 135 | +3. Register the displayed **Redirect URI** in your Authentik provider. |
| 136 | +4. Set **Groups Claim** to `groups`. |
| 137 | +5. Set **Advanced Permissions Claim** to `permissions`. |
| 138 | +6. Enable **Auto-register** if you want accounts created automatically on first login. |
| 139 | +7. Enable **OpenID Connect**. |
0 commit comments