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
141 lines
7.2 KiB
Markdown
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.
|