First public release. - docs: document the audit log, user offboarding, CSV export and the "require a reason for destructive actions" setting across README, docs/ and CLAUDE/AGENTS.md; note retention for panel_audit - docs: fill the publishing placeholders (repo URL, security contact, issue-template links) and add a semantic-versioning policy - move agent/contributor context into CLAUDE/ (AGENTS.md + a README); root CLAUDE.md is now a thin @-import pointer - add a favicon (shield-lock, matching the login screen) to the login page and the app layout - CHANGELOG: promote [Unreleased] to [1.0.0]; keep the earlier internal build as [0.1.0] Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UfuvfwexztdS7LDpGuKsuK
71 lines
2.1 KiB
PHP
71 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\AuditLog;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
/**
|
|
* Zapis wpisów do dziennika zmian panelu (`panel_audit`).
|
|
*
|
|
* Nigdy nie przerywa operacji AD - błąd zapisu logu jest tylko raportowany.
|
|
* Tożsamość administratora bierzemy z guarda 'ldap' (fallback: profil w sesji).
|
|
*/
|
|
class AuditLogger
|
|
{
|
|
/**
|
|
* @param array{
|
|
* target_type?: ?string,
|
|
* target?: ?string,
|
|
* target_dn?: ?string,
|
|
* reason?: ?string,
|
|
* changes?: ?array<string, mixed>,
|
|
* context?: ?array<string, mixed>
|
|
* } $opts
|
|
*/
|
|
public function log(string $action, array $opts = []): void
|
|
{
|
|
try {
|
|
[$actor, $actorName] = $this->resolveActor();
|
|
|
|
AuditLog::create([
|
|
'actor' => $actor,
|
|
'actor_name' => $actorName,
|
|
'action' => $action,
|
|
'target_type' => $opts['target_type'] ?? null,
|
|
'target' => $opts['target'] ?? null,
|
|
'target_dn' => $opts['target_dn'] ?? null,
|
|
'reason' => ($opts['reason'] ?? null) ?: null,
|
|
'changes' => ! empty($opts['changes']) ? $opts['changes'] : null,
|
|
'context' => ! empty($opts['context']) ? $opts['context'] : null,
|
|
'ip' => request()?->ip(),
|
|
'created_at' => now(),
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
report($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{0: string, 1: string} [login, wyświetlana nazwa]
|
|
*/
|
|
protected function resolveActor(): array
|
|
{
|
|
$user = Auth::guard('ldap')->user();
|
|
|
|
if ($user) {
|
|
$login = (string) ($user->getFirstAttribute('samaccountname') ?? 'unknown');
|
|
$name = (string) ($user->getFirstAttribute('cn') ?? $login);
|
|
|
|
return [$login, $name];
|
|
}
|
|
|
|
$profile = session('ad_admin');
|
|
if (is_array($profile) && ! empty($profile['login'])) {
|
|
return [(string) $profile['login'], (string) ($profile['display_name'] ?? $profile['login'])];
|
|
}
|
|
|
|
return ['system', 'system'];
|
|
}
|
|
}
|