Files
ADconsle/README.md
T
kzbkowskiandClaude Sonnet 5 2b970226fc
CI / PHP 8.4 (push) Failing after 1m5s
ci: require PHP 8.4, test 8.4 only
composer.lock already pins packages that need PHP >= 8.4.1 (symfony/* v8,
nesbot/carbon 3, laravel/pint), so `composer install` on the 8.2 / 8.3 CI
legs has been failing since v1.0.0. Bump composer.json to "php": "^8.4",
regenerate the lock platform block, drop 8.2 / 8.3 from the ci.yml matrix,
and update the "PHP 8.2+" references in the docs. The Docker image was
already php:8.4-apache.

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

11 KiB

ADconsole

A self-hosted web panel for day-to-day Active Directory administration — users, computers, groups and read-only Group Policy — built on Laravel 11 and LdapRecord.

License: MIT PHP 8.4+ Laravel 11

ADconsole gives helpdesk and sysadmin staff a clean UI for the tasks that otherwise mean RSAT, dsa.msc or PowerShell: reset a password, unlock an account, move an object between OUs, add someone to a group, check who has a stale password. It talks directly to a domain controller over LDAP — there is no database copy of your directory to drift out of sync.

  • No AD data at rest. Every read and write goes straight to a DC through LdapRecord. The SQL database only holds the app's own sessions, cache and panel settings.
  • Domain login, group-gated. Admins sign in with their own AD account; access is limited to members of one AD group you nominate.
  • Bilingual UI — Polish and English, switchable per session.
  • Deploys in minutes — a docker compose up stack, or drop it on any PHP 8.4+ host.

⚠️ ADconsole performs privileged directory operations (password resets, account enable/disable, deletions). Read SECURITY.md and the hardening checklist before exposing it.


Features

Module What it does
Dashboard (/dashboard) Domain health tiles (disabled / locked / stale-password / inactive / expired accounts, object counts), password-age histogram, "expiring soon / inactive / newly created" mini-reports. Tiles link straight to the matching filtered list. Cached, recalculated on demand.
Users (/users) OU tree + full-text search, create, view/edit (configurable attribute set), password reset (single and bulk), move OU (single and bulk), password-expiry policy, enable/disable, group membership, photo, CSV export of the current (filtered) list, and a one-click offboarding flow (disable + randomise password + strip group memberships + move OU + set expiry, in a safe order).
Computers (/computers) OU tree, OS filter, LAPS (legacy ms-Mcs-AdmPwd + Windows LAPS) and BitLocker recovery keys, reset trust account, enable/disable the object, delete.
Groups (/groups) Type/scope filter, create, edit description & mail, manage members, delete.
Org chart (/organisation) manager → directReports hierarchy rendered with d3-org-chart.
GPO (/gpo) Read-only list of Group Policy objects and their OU links, plus a simplified RSoP lookup. Editing policy content (SYSVOL) is out of scope.
Audit log (/audit) Read-only record of every change the panel makes to AD — actor, action, target, optional reason and a before/after diff — filterable by admin, action, object and date. Stored in the panel_audit table (application data, never AD).
Configuration (/settings) Password policy & generator, attribute-encryption key, an optional "require a reason for destructive actions" switch, and the definition of which AD attributes show up as list columns / profile fields. Stored in the panel_settings / panel_attributes tables.

Full walkthrough: docs/architecture.md.

Requirements

  • PHP 8.4+ with ldap, pdo_mysql (or your DB driver), mbstring, curl, zip, openssl
  • Composer 2
  • A SQL database (MariaDB/MySQL recommended; SQLite/PostgreSQL work too) — for sessions, cache and panel settings only
  • Network reachability to a domain controller, ideally over LDAPS (636) or STARTTLS — password writes fail on an unencrypted connection
  • An AD service account that can read your directory and modify the objects you want to manage (see docs/active-directory.md)
  • For the container image: Docker + Docker Compose v2

Quick start — Docker

git clone https://gitea.kzbikowski.pl/kzbkowski/ADconsle.git adconsole && cd adconsole
cp .env.example .env
# edit .env: APP_URL/APP_DOMAIN, DB_* passwords, and the whole LDAP_* / AD_* block

# Production — pulls the released image, external Traefik proxy. Run from the
# repo root and point Compose at the repo-root .env:
docker compose --env-file .env -f docker/compose.yaml up -d

# Or without a proxy — publish a local port instead:
cp docker/compose.override.yaml.template docker/compose.override.yaml
docker compose --env-file .env -f docker/compose.yaml -f docker/compose.override.yaml up -d
# -> http://localhost:8080   (set APP_URL to match)

# Live-reload dev stack (bind-mounts the working tree):
docker compose --env-file .env -f docker/compose.dev.yaml up --build

docker/compose.yaml runs the self-contained image the Gitea release workflow publishes (…/adconsole:1.1.0); up -d pulls it, --build rebuilds from your tree. Only .env and docker/data/ (DB + storage) are mounted. The entrypoint generates APP_KEY, runs migrations and warms the caches on first boot. Details: docs/deployment.md.

Quick start — bare metal

git clone https://gitea.kzbikowski.pl/kzbkowski/ADconsle.git adconsole && cd adconsole
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
# edit .env (LDAP_* / AD_* / DB_*)

php artisan migrate
php artisan config:cache && php artisan route:cache && php artisan view:cache

# point your web server's document root at ./public
# (dev only:)  php artisan serve

Step-by-step, including Apache/nginx vhosts: docs/installation.md.

Configuration at a glance

Where What How
.env Connection & deployment settings: DC hosts, service account, base DN, default OUs, login group, DB, app URL. Edit the file, then php artisan config:cache. Every key is documented in .env.example and docs/configuration.md.
Database (/settings) In-app settings: password policy/generator + encryption key (panel_settings), and the AD attribute / column definitions (panel_attributes). Edited in the UI; persisted via App\Services\PanelSettingsService.

Who can log in is AD_LOGIN_GROUP. What the service account may change is set by AD delegation, not by ADconsole — see docs/active-directory.md.

How it works (short version)

  • config/ldap.php defines one connection from LDAP_*. App\Ldap\User extends the LdapRecord AD user model.
  • Login = LdapRecord plain-bind with the typed credentials; then App\Ldap\Rules\MemberOfLoginGroup checks membership of AD_LOGIN_GROUP (nested + primary group aware). Success stores ad_admin in the session; all /… panel routes sit behind auth:ldap.
  • Controllers query/modify AD live. Heavy reads (OU trees, dashboard, org chart) are cached on the file store for a few minutes with a manual "recalculate" button.
  • LdapAttributeHelper decodes the awkward bits (userAccountControl bit flags, Windows FileTime timestamps, accountExpires "never" sentinels).
  • Sensitive attributes (e.g. cloud extension attributes) can be stored encrypted with a key set in the panel — App\Services\AttributeCipher.

More: docs/architecture.md.

Tests

php artisan test          # ~40 tests, SQLite :memory:
vendor/bin/pint --test    # code style

A PHP 8.5 PDO::MYSQL_ATTR_SSL_CA deprecation makes PHPUnit tag every test DEPR — that is noise, not a failure.

Project layout

app/
  Http/Controllers/Admin/   one controller per module
  Ldap/                     AD user model + login rule
  Services/                 PanelSettings, AttributeCipher, LdapAttributeHelper, OuTree
  Models/                   PanelSetting (JSON config row) + PanelAttribute (one row per attribute)
config/ldap.php             connection, login group, default OUs, attribute map
resources/views/admin/      Blade UI (+ components/ + partials/)
public/css/style.css        hand-written styles (no build step)
public/js/main.js           hand-written JS
routes/web.php              English URL segments and route names
lang/{pl,en}/               structured translation keys
docs/                       installation / AD / deployment / architecture / …
docker/compose.yaml         production stack (self-contained image + MariaDB)
docker/compose.dev.yaml     live-reload dev stack (bind-mounts the working tree)
docker/php/                 PHP 8.4 + Apache Dockerfile (base/dev/prod) + entrypoint

Documentation

Everything lives in docs/: installation · configuration · Active Directory setup · architecture · deployment · development · troubleshooting · upgrading · publishing.

Project meta: changelog · contributing · security policy.

Roadmap / non-goals

Planned: server-side password-policy validation before write; optional TOTP for off-LAN login; export of the audit log.

Out of scope: editing GPO content (SYSVOL/SMB); being a general LDAP browser; managing non-AD directories.

Released under the MIT License — provided "as is", without warranty. Copyright (c) 2026 Kacper Żbikowski.

ADconsole is built on the Laravel framework (MIT) and the laravel/laravel application skeleton (MIT); the skeleton's MIT terms are satisfied by this repository's own LICENSE. Third-party PHP and JavaScript dependencies are pulled in at build time by Composer / a CDN and are not vendored in this repository; each keeps its own licence (all permissive — MIT / BSD / Apache-2.0). Run composer licenses after composer install for the authoritative list.

Active Directory, Windows, Windows Server and Group Policy are trademarks of Microsoft Corporation. ADconsole is an independent project and is not affiliated with, endorsed or sponsored by Microsoft.

When you deploy ADconsole your organisation is the data controller for the personal data it reads from AD (names, logins, e-mail, phone, photo, …). You are responsible for the lawful basis, retention and data-subject rights; restrict panel access to authorised admins and always use LDAPS/STARTTLS. See the in-app About → Legal page and SECURITY.md.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md and the CODE_OF_CONDUCT.md. Security reports: SECURITY.md.