# 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.