Files
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

148 lines
8.7 KiB
PHP

<?php
use App\Http\Controllers\Admin\AuditController;
use App\Http\Controllers\Admin\ComputerController;
use App\Http\Controllers\Admin\ConfigController;
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\GpoController;
use App\Http\Controllers\Admin\GroupController;
use App\Http\Controllers\Admin\OrgChartController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\LocaleController;
use App\Http\Controllers\PageController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Logowanie: konto Active Directory przez guard 'ldap' (LdapRecord "plain
| bind", bez lokalnej tabeli users). Dostęp do panelu mają wyłącznie
| członkowie grupy z AD_LOGIN_GROUP (reguła App\Ldap\Rules\MemberOfLoginGroup).
| Wszystkie trasy panelu chroni middleware 'auth:ldap'. Profil zalogowanego
| administratora trzymany jest w sesji pod kluczem 'ad_admin'.
|
| Segmenty URL są po angielsku; nazwy tras (route names) - też. Zmieniając
| ścieżkę pamiętaj tylko o zgodności z linkami w JS (public/js/main.js).
|
*/
// --- Authentication ---------------------------------------------------------
Route::middleware('guest:ldap')->group(function () {
Route::get('/login', [LoginController::class, 'show'])->name('login');
Route::post('/login', [LoginController::class, 'login'])->name('login.attempt');
});
Route::post('/logout', [LoginController::class, 'logout'])
->middleware('auth:ldap')
->name('logout');
// --- Interface language + public info page (no auth) -----------------------
Route::get('/language/{locale}', [LocaleController::class, 'switch'])->name('locale.switch');
Route::get('/about', [PageController::class, 'about'])->name('pages.about');
Route::redirect('/', '/dashboard');
// --- Dashboard / reports --------------------------------------------------
Route::prefix('dashboard')->name('dashboard.')->middleware('auth:ldap')->group(function () {
Route::get('/', [DashboardController::class, 'index'])->name('index');
Route::post('/refresh', [DashboardController::class, 'refresh'])->name('refresh');
Route::post('/preferences', [DashboardController::class, 'savePreferences'])->name('preferences');
});
// --- Org chart (manager -> directReports) --------------------------------
Route::prefix('organisation')->name('org.')->middleware('auth:ldap')->group(function () {
Route::get('/', [OrgChartController::class, 'index'])->name('index');
Route::post('/refresh', [OrgChartController::class, 'refresh'])->name('refresh');
});
// --- Users --------------------------------------------------------------
Route::prefix('users')->name('users.')->middleware('auth:ldap')->group(function () {
Route::get('/', [UserController::class, 'index'])->name('index');
Route::post('/', [UserController::class, 'store'])->name('store');
// Stałe segmenty PRZED wzorcem /{samaccountname}, żeby nie zostały przez
// niego przechwycone (np. "bulk" jako samaccountname).
Route::get('/search', [UserController::class, 'search'])->name('search');
Route::get('/export', [UserController::class, 'export'])->name('export');
Route::post('/bulk/reset-password', [UserController::class, 'bulkResetPassword'])->name('bulk-reset-password');
Route::post('/bulk/move', [UserController::class, 'bulkMove'])->name('bulk-move');
Route::post('/bulk/password-expiry', [UserController::class, 'bulkPasswordExpiry'])->name('bulk-password-expiry');
Route::post('/bulk/set-enabled', [UserController::class, 'bulkSetEnabled'])->name('bulk-set-enabled');
Route::post('/bulk/group', [UserController::class, 'bulkGroup'])->name('bulk-group');
Route::post('/bulk/force-password-change', [UserController::class, 'bulkForcePasswordChange'])->name('bulk-force-password-change');
Route::post('/bulk/unlock', [UserController::class, 'bulkUnlock'])->name('bulk-unlock');
Route::post('/bulk/attributes', [UserController::class, 'bulkAttributes'])->name('bulk-attributes');
Route::post('/bulk/offboard', [UserController::class, 'bulkOffboard'])->name('bulk-offboard');
Route::get('/{samaccountname}/structure', [UserController::class, 'structure'])->name('structure');
Route::get('/{samaccountname}/template', [UserController::class, 'template'])->name('template');
Route::get('/{samaccountname}', [UserController::class, 'show'])->name('show');
Route::put('/{samaccountname}', [UserController::class, 'update'])->name('update');
Route::delete('/{samaccountname}', [UserController::class, 'destroy'])->name('destroy');
Route::post('/{samaccountname}/reset-password', [UserController::class, 'resetPassword'])->name('reset-password');
Route::post('/{samaccountname}/move', [UserController::class, 'move'])->name('move');
Route::post('/{samaccountname}/password-expiry', [UserController::class, 'passwordExpiry'])->name('password-expiry');
Route::post('/{samaccountname}/toggle-enabled', [UserController::class, 'toggleEnabled'])->name('toggle-enabled');
Route::post('/{samaccountname}/set-enabled', [UserController::class, 'setEnabled'])->name('set-enabled');
Route::post('/{samaccountname}/force-password-change', [UserController::class, 'forcePasswordChange'])->name('force-password-change');
Route::post('/{samaccountname}/unlock', [UserController::class, 'unlock'])->name('unlock');
Route::post('/{samaccountname}/offboard', [UserController::class, 'offboardOne'])->name('offboard-one');
Route::post('/{samaccountname}/photo', [UserController::class, 'photo'])->name('photo');
Route::post('/{samaccountname}/groups', [UserController::class, 'addGroup'])->name('add-group');
Route::delete('/{samaccountname}/groups', [UserController::class, 'removeGroup'])->name('remove-group');
});
// --- Computers --------------------------------------------------------------
Route::prefix('computers')->name('computers.')->middleware('auth:ldap')->group(function () {
Route::get('/', [ComputerController::class, 'index'])->name('index');
// Stałe segmenty PRZED wzorcem /{cn}.
Route::post('/bulk/set-enabled', [ComputerController::class, 'bulkSetEnabled'])->name('bulk-set-enabled');
Route::post('/bulk/move', [ComputerController::class, 'bulkMove'])->name('bulk-move');
Route::get('/{cn}', [ComputerController::class, 'show'])->name('show');
Route::delete('/{cn}', [ComputerController::class, 'destroy'])->name('destroy');
Route::post('/{cn}/toggle-enabled', [ComputerController::class, 'toggleEnabled'])->name('toggle-enabled');
Route::post('/{cn}/reset-trust', [ComputerController::class, 'resetTrustAccount'])->name('reset-trust');
});
// --- Groups -----------------------------------------------------------------
Route::prefix('groups')->name('groups.')->middleware('auth:ldap')->group(function () {
Route::get('/', [GroupController::class, 'index'])->name('index');
Route::post('/', [GroupController::class, 'store'])->name('store');
Route::get('/search', [GroupController::class, 'search'])->name('search');
Route::get('/{cn}', [GroupController::class, 'show'])->name('show');
Route::put('/{cn}', [GroupController::class, 'update'])->name('update');
Route::delete('/{cn}', [GroupController::class, 'destroy'])->name('destroy');
Route::post('/{cn}/members', [GroupController::class, 'addMember'])->name('add-member');
Route::delete('/{cn}/members', [GroupController::class, 'removeMember'])->name('remove-member');
});
// --- Group Policy (read-only) --------------------------------------------
Route::prefix('gpo')->name('gpo.')->middleware('auth:ldap')->group(function () {
Route::get('/', [GpoController::class, 'index'])->name('index');
Route::get('/rsop', [GpoController::class, 'rsop'])->name('rsop');
Route::get('/{guid}', [GpoController::class, 'show'])->name('show');
});
// --- Audit log (read-only) ---------------------------------------------
Route::prefix('audit')->name('audit.')->middleware('auth:ldap')->group(function () {
Route::get('/', [AuditController::class, 'index'])->name('index');
});
// --- Panel settings -------------------------------------------------------
Route::prefix('settings')->name('config.')->middleware('auth:ldap')->group(function () {
Route::get('/', [ConfigController::class, 'index'])->name('index');
Route::put('/', [ConfigController::class, 'update'])->name('update');
Route::put('/attributes', [ConfigController::class, 'updateAttributes'])->name('attributes.update');
// Kopia zapasowa konfiguracji panelu (bez danych z AD).
Route::get('/export', [ConfigController::class, 'export'])->name('export');
Route::post('/import', [ConfigController::class, 'import'])->name('import');
});