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
95 lines
2.8 KiB
PHP
95 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Encryption\Encrypter;
|
|
|
|
/**
|
|
* Szyfrowanie / odszyfrowanie wartości atrybutów oznaczonych w konfiguracji
|
|
* panelu jako "szyfrowany" (np. msDS-cloudExtensionAttribute1/2).
|
|
*
|
|
* Format szyfrogramu jest zgodny z Laravel Crypt (AES-CBC + MAC), dzięki
|
|
* czemu panel odczyta wartości zaszyfrowane wcześniej wartością APP_KEY.
|
|
* Klucz z konfiguracji panelu:
|
|
* - "base64:...." dekodujące się do 16 lub 32 bajtów -> użyty wprost
|
|
* (tu wklej wartość APP_KEY, jeśli atrybuty były nią szyfrowane),
|
|
* - dowolny inny tekst (hasło) -> klucz = SHA-256 z tego tekstu (32 B).
|
|
* Puste = szyfrowanie wyłączone (wartości przechodzą bez zmian).
|
|
*/
|
|
class AttributeCipher
|
|
{
|
|
protected ?Encrypter $encrypter = null;
|
|
|
|
public function __construct(protected PanelSettingsService $settings) {}
|
|
|
|
public function isConfigured(): bool
|
|
{
|
|
return $this->settings->encryptionKey() !== '';
|
|
}
|
|
|
|
protected function keyBytes(): string
|
|
{
|
|
$key = $this->settings->encryptionKey();
|
|
|
|
if (str_starts_with($key, 'base64:')) {
|
|
$decoded = base64_decode(substr($key, 7), true);
|
|
|
|
if ($decoded !== false && in_array(strlen($decoded), [16, 32], true)) {
|
|
return $decoded;
|
|
}
|
|
}
|
|
|
|
return hash('sha256', $key, true); // 32 bajty
|
|
}
|
|
|
|
protected function encrypter(): Encrypter
|
|
{
|
|
if ($this->encrypter === null) {
|
|
$key = $this->keyBytes();
|
|
$this->encrypter = new Encrypter($key, strlen($key) === 16 ? 'aes-128-cbc' : 'aes-256-cbc');
|
|
}
|
|
|
|
return $this->encrypter;
|
|
}
|
|
|
|
/**
|
|
* Zwraca odszyfrowaną wartość. Jeśli szyfrowanie jest wyłączone albo
|
|
* wartość nie jest naszym szyfrogramem (np. zły klucz, dane wpisane
|
|
* ręcznie) - zwraca ją bez zmian.
|
|
*/
|
|
public function decrypt(?string $value): ?string
|
|
{
|
|
if ($value === null || $value === '' || ! $this->isConfigured()) {
|
|
return $value;
|
|
}
|
|
|
|
try {
|
|
return $this->encrypter()->decryptString($value);
|
|
} catch (\Throwable) {
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zwraca zaszyfrowaną wartość. Jeśli szyfrowanie jest wyłączone - zwraca
|
|
* bez zmian. Jeśli wartość jest już naszym szyfrogramem - nie szyfruje
|
|
* jej ponownie.
|
|
*/
|
|
public function encrypt(?string $value): ?string
|
|
{
|
|
if ($value === null || $value === '' || ! $this->isConfigured()) {
|
|
return $value;
|
|
}
|
|
|
|
try {
|
|
$this->encrypter()->decryptString($value);
|
|
|
|
return $value; // już zaszyfrowane naszym schematem
|
|
} catch (\Throwable) {
|
|
// wartość jawna - szyfrujemy
|
|
}
|
|
|
|
return $this->encrypter()->encryptString($value);
|
|
}
|
|
}
|