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

7.2 KiB

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.

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.