Commit 861469

2026-08-30 20:16:05 Ralph Thesen: Add CLI page documenting the flask user management commands
/dev/null .. cli.md
@@ 0,0 1,115 @@
+# Command Line Interface
+
+An Otter Wiki ships a Flask command line interface for user management (from
+version **2.18.0**). It is the recovery path when nobody can log in any more or
+the mail server is broken, so it works without a browser and without a working
+login. Every command carries its own `--help`, for example `flask user --help`
+or `flask user create --help`.
+
+## Running the CLI
+
+The environment variable `FLASK_APP=otterwiki.server` must be set. Both docker
+images set it already, so inside a container the commands are just
+`flask user ...`.
+
+### In Docker
+
+Run the CLI in the running container with `docker compose exec`. Replace
+`otterwiki` below with the name of your service.
+
+The full image (`redimp/otterwiki:2`) runs the wiki as the `www-data` user even
+though the container itself starts as root. Run the CLI as `www-data` too, so
+that database writes and git commits are not left owned by root in `/app-data`:
+
+```
+docker compose exec -u www-data otterwiki flask user list
+```
+
+> [!NOTE]
+> If you set `PUID`/`PGID` on the container, run the CLI as that user instead of
+> `www-data`. The slim image (`redimp/otterwiki:2-slim`) already runs as
+> `www-data`, so there `-u` is not needed:
+> `docker compose exec otterwiki flask user list`.
+
+### From a source install
+
+Export `OTTERWIKI_SETTINGS` so the app finds its configuration, then use the
+`flask` from the virtual environment the app runs in:
+
+```
+export OTTERWIKI_SETTINGS=/path/to/settings.cfg
+venv/bin/flask user list
+```
+
+## User management
+
+Users have three independent attributes the CLI can set:
+
+- **Flags** `email_confirmed` and `approved`.
+- **Permissions** `read`, `write`, `upload` and `admin`. Granting `admin`
+ implies `approved`, `read`, `write` and `upload`.
+- A **password**. A user without a password cannot log in.
+
+`--flags` and `--permissions` take comma-separated lists, for example
+`--permissions=read,write`.
+
+### List users
+
+```
+flask user list # human-readable table
+flask user list --json # machine-readable
+```
+
+### Create a user
+
+`flask user create EMAIL NAME` creates an account. The new user has **no
+password** and cannot log in until one is set with `flask user password`.
+
+```
+flask user create user@example.com "Jane Doe" --flags=email_confirmed,approved --permissions=read,write
+```
+
+Short options `-f` (flags) and `-p` (permissions) also work.
+
+### Set or reset a password
+
+`flask user password EMAIL` takes exactly one of:
+
+- `-i`, `--interactive` prompt for the new password.
+- `-g`, `--generate` generate a 12-character password and print it.
+- `-r`, `--send-password-reset` email a reset link (requires a configured mail
+ server).
+- `-d`, `--delete` remove the password, blocking login until a reset.
+
+### Edit a user
+
+`flask user edit EMAIL` changes an account. Provide at least one of
+`--new-email`, `--new-name`, `--flags` or `--permissions`.
+
+> [!WARNING]
+> `--flags` and `--permissions` **overwrite** the current values rather than
+> adding to them. To make someone an admin without dropping their other
+> attributes, pass the full set you want.
+
+```
+flask user edit user@example.com --new-name="Jane Roe" --permissions=read,write,upload
+```
+
+### Delete a user
+
+`flask user delete EMAIL` asks for confirmation first; `-y` / `--confirm`
+skips it.
+
+## Recovering admin access
+
+If you are locked out, create a fresh admin account (or re-grant admin to your
+own) and generate a password for it:
+
+```
+docker compose exec -u www-data otterwiki flask user create you@example.com "You" -p admin
+docker compose exec -u www-data otterwiki flask user password you@example.com --generate
+```
+
+The second command prints the new password. Log in with it and change it from
+your profile. See the [[FAQ|FAQ#i-locked-myself-out]] for the same recipe from
+the other direction.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9