Files
ADconsle/docs/configuration.md
kzbkowskiandClaude Sonnet 5 b313c126ce
CI / PHP 8.2 (push) Failing after 1m40s
CI / PHP 8.3 (push) Failing after 55s
CI / PHP 8.4 (push) Failing after 1m11s
Release image / image (push) Successful in 2m33s
ADconsole v1.1.0
Config backup, a Docker dev/prod split, and a Gitea-built release image.

Added
- /settings -> General -> "Configuration backup": JSON export/import of the
  panel's general settings + attribute definitions
  (App\Services\ConfigBackupService); audited as config.export / config.import.
  Export can leave out the attribute-encryption key. No AD data included.
- docker/compose.dev.yaml: live-reload dev stack that bind-mounts the working
  tree. docker/php/Dockerfile is now multi-stage (base -> dev / prod).
- .github/workflows/release.yml: on every vX.Y.Z tag, builds the prod image and
  pushes gitea.kzbikowski.pl/kzbkowski/adconsole:{X.Y.Z,X.Y,latest}.

Changed
- docker/compose.yaml: self-contained prod image (app code + composer install
  --no-dev baked in via COPY); persistent state in docker/data/{app_storage,db};
  defaults to the released image (ADCONSOLE_VERSION pins a tag, ADCONSOLE_IMAGE
  replaces the ref). Apache now honours public/.htaccess (AllowOverride All).
  Run from the repo root with --env-file .env.
- Repo root now holds only README.md; CHANGELOG / CONTRIBUTING / SECURITY /
  CODE_OF_CONDUCT moved under docs/.

Upgrade notes: docs/upgrading.md#v110--docker-layout. Full log: docs/CHANGELOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JKT9p3bfP8RZesExdYCS52
2026-09-10 09:13:04 +02:00

141 lines
7.2 KiB
Markdown

# Configuration
Two layers:
1. **`.env`** — deployment settings, read at boot, never written back. After any
change run `php artisan config:cache` (or `php artisan optimize`), or the old
cached values keep being used.
2. **Database (`/settings`)** — in-app settings via `PanelSettingsService`:
general settings in `panel_settings` (one JSON row), the AD attribute /
column definitions in `panel_attributes` (one row each).
## `.env` reference
### Application
| Key | Default | Notes |
|---|---|---|
| `APP_NAME` | `ADconsole` | Shown in the header / page titles. |
| `APP_ENV` | `production` | `local` on a dev box only. |
| `APP_KEY` | — | `php artisan key:generate`. Also the fallback key for encrypted attributes (see below). **Rotating it invalidates existing sessions and any attributes encrypted with it.** |
| `APP_DEBUG` | `false` | Never `true` in production — stack traces leak DNs and query filters. |
| `APP_URL` | — | Exact public URL, scheme + host, no trailing slash. |
| `APP_DOMAIN` | — | Hostname the Docker/Traefik router matches. Ignored on bare metal. |
| `APP_LOCALE` | `pl` | `pl` or `en`. |
| `APP_FALLBACK_LOCALE` | `en` | Used for any missing translation key. |
| `LOG_CHANNEL` / `LOG_LEVEL` | `stack` / `warning` | Standard Laravel logging. |
### Database
ADconsole stores **no directory data** in SQL — only sessions, cache, queue, the
panel's own config (`panel_settings`, `panel_attributes`) and the change log
(`panel_audit`).
| Key | Notes |
|---|---|
| `DB_CONNECTION` | `mysql` (MariaDB/MySQL), `pgsql`, or `sqlite`. |
| `DB_HOST` / `DB_PORT` / `DB_DATABASE` / `DB_USERNAME` / `DB_PASSWORD` | Connection. |
| `DB_ROOT_PASSWORD` | Only consumed by `docker/compose.yaml` when it first initialises `docker/data/db`. |
### Sessions / cache / queue
| Key | Default | Notes |
|---|---|---|
| `SESSION_DRIVER` | `file` | `database` or `redis` if you run more than one app node. |
| `SESSION_LIFETIME` | `120` | Minutes. |
| `CACHE_STORE` | `database` | The app's own heavy caches (OU tree, dashboard, org chart) always use the **`file`** store regardless of this. |
| `QUEUE_CONNECTION` | `database` | No queued jobs ship today. |
### Active Directory — connection
| Key | Default | Notes |
|---|---|---|
| `LDAP_HOSTS` | `127.0.0.1` | Comma-separated DC list, tried in order. `LDAP_HOST` (singular) also accepted. |
| `LDAP_USERNAME` | — | Service-account bind DN or `user@domain`. |
| `LDAP_PASSWORD` | — | Service-account password. |
| `LDAP_PORT` | `636` | `636` LDAPS, `389` plain/STARTTLS. |
| `LDAP_SSL` | `true` | LDAPS. |
| `LDAP_TLS` | `false` | STARTTLS on 389. Use **one** of SSL/TLS. |
| `LDAP_TLS_VERIFY` | `false` | `true` = validate the DC certificate chain. Leave `false` for an internal-CA / self-signed cert the app host doesn't trust. |
| `LDAP_TIMEOUT` | `5` | Seconds. |
| `LDAP_BASE_DN` | — | **Must cover users, computers and groups.** If those OUs are siblings, use the domain root (`DC=…,DC=…`). |
| `LDAP_LOGGING` | `true` | Logs LDAP operations to the Laravel log. |
> Writes to AD (password reset, enable/disable, create, move, delete) **require**
> `LDAP_SSL=true` or `LDAP_TLS=true`. A clear connection can read but every write
> of `unicodePwd` etc. is refused by the DC.
### Active Directory — defaults & access
| Key | Notes |
|---|---|
| `LDAP_DEFAULT_USER_OU` | Where new users are created; also caps the Users OU tree. |
| `LDAP_DEFAULT_COMPUTER_OU` | Same for Computers. |
| `LDAP_DEFAULT_GROUPS_OU` | Same for Groups. |
| `AD_DOMAIN_LABEL` | Friendly domain name in the header (defaults to `LDAP_BASE_DN`). |
| `AD_LOGIN_GROUP` | AD group whose members may sign in. Full DN (recommended) or bare CN. Nested membership + primary group honoured. **Empty = nobody can log in.** |
`AD_LOGIN_GROUP` controls *who can open the panel*. It does **not** grant
directory permissions — what the service account may change is decided by AD
delegation. See [active-directory.md](active-directory.md).
### Mail
Not used by the app as shipped. Configure `MAIL_*` only if you add notifications.
## In-app settings (`/settings`)
All read and written through `App\Services\PanelSettingsService`. Nothing here
touches `.env`.
### General tab
Stored as one JSON row in **`panel_settings`** (key `panel`). Absent → the
built-in `PanelSettingsService::defaults()` apply.
| Setting | Meaning |
|---|---|
| Reset password → *must change at next logon* | Default state of that checkbox in the reset dialog. |
| Reset password → *unlock account* | Default state of the "also unlock" checkbox. |
| Password policy → *max age (days)* | Drives the "password expires / expired" columns and dashboard tiles. `0` = no expiry. This is ADconsole's own figure — it does not read the domain's `maxPwdAge`. |
| Password generator → *length* / *symbols* | For the in-panel generator only; does not touch AD policy. |
| Attribute encryption key | Symmetric key used to encrypt/decrypt attributes whose type is *encrypted* (e.g. cloud extension attributes). Empty = feature off. `base64:…` (16/32 bytes) is used verbatim — paste `APP_KEY` here if values were encrypted with it; any other string is hashed to a 32-byte key. See `App\Services\AttributeCipher`. |
| Audit log → *require a reason for destructive actions* | When on, disable / delete / move-OU / offboard refuse to run without a reason. An audit row (`panel_audit`) is written either way — this only forces the operator to type why. Default: off. |
### Attributes tab
Defines the attribute set the Users module works with. One row per attribute in
**`panel_attributes`**; row order (`position`) sets column / field order. Empty
table → `PanelSettingsService::defaultAttributes()` apply.
| Column | Meaning |
|---|---|
| `key` | internal id (`[A-Za-z0-9_-]`) |
| `label` | column / field caption |
| `ldap` | the AD attribute name |
| `category` | profile-dialog tab: `general` / `org` / `address` / `hr` / `account` |
| `type` | `text` · `number` · `date` · `encrypted` (editable) or `system` · `account_status` · `password_expiry` (computed / read-only) |
| `is_column` | show it as a column on the user list |
| `position` | display order |
Locked rows that can't be removed or retyped: `login` (sAMAccountName),
`distinguishedName`, the computed `status` and `password_expiry`, and
`account_expiry` (fixed `date`, `accountExpires`). If deleted directly in SQL
they are re-created from the defaults on the next read.
### Backup / restore
The *General* tab has a **Configuration backup** card:
- **Export** downloads a JSON file with the General-tab settings and the full
attribute list (`App\Services\ConfigBackupService::export()`). Tick *leave the
encryption key out* to blank `encryption.key` in the file.
- **Import** reads such a file back and overwrites `panel_settings` (key `panel`)
and `panel_attributes`. Values are re-validated on the way in (ranges clamped,
locked attribute rows re-asserted), so a hand-edited file can't push the panel
into a bad state. A file that isn't a `format: adconsole-config` JSON, or that
declares a newer schema `version`, is rejected with a form error.
Both actions write a `panel_audit` row (`config.export` / `config.import`). The
backup never contains Active Directory data or the audit log itself.