Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Content\FileExplorerAcl;
use OpenNIT\Content\FileExplorerManager;
/**
* Deckt den Zugriffsschutz des Datei-Papierkorbs ab.
*
* Gelöschte Einträge behalten ihre Ordner-Zuordnung, damit sich die Rechte
* eines Papierkorb-Stapels weiterhin über den Ordnerbaum bestimmen lassen –
* die Vererbung läuft dabei über den gespeicherten Pfad, der auch für
* gelöschte Ordner erhalten bleibt.
*/
return static function (): void {
/**
* Datenbank-Doppel mit Ordnerbaum, Rechteeinträgen und Papierkorb-Stapeln.
*
* @param array<int,array{path:string,created_by:?int}> $folders
* @param array<int,array<int,array{principal_type:string,principal_id:int,permission:string}>> $acl
* @param array<string,array{folders:int[],files:array<int,?int>}> $batches
*/
$db = function (array $folders, array $acl, array $batches = []): object {
return new class ($folders, $acl, $batches) extends \OpenNIT\Core\Database {
public function __construct(
private array $folders,
private array $acl,
private array $batches
) {
// Verbindung bewusst nicht aufbauen – die Abfragen werden bedient.
}
public function fetchAll(string $sql, array $params = []): array
{
if (str_contains($sql, 'file_explorer_folder_acl')) {
$rows = [];
foreach ($this->acl as $folderId => $entries) {
foreach ($entries as $e) {
$rows[] = $e + ['folder_id' => $folderId];
}
}
return $rows;
}
if (str_contains($sql, 'SELECT id, path, created_by FROM file_explorer_folders')) {
$rows = [];
foreach ($this->folders as $id => $f) {
$rows[] = ['id' => $id, 'path' => $f['path'], 'created_by' => $f['created_by']];
}
return $rows;
}
if (str_contains($sql, 'SELECT id FROM file_explorer_folders WHERE deleted_batch')) {
$batch = (string)($params[0] ?? '');
return array_map(
static fn(int $id): array => ['id' => $id],
$this->batches[$batch]['folders'] ?? []
);
}
if (str_contains($sql, 'SELECT DISTINCT folder_id FROM file_explorer_files WHERE deleted_batch')) {
$batch = (string)($params[0] ?? '');
return array_map(
static fn(?int $fid): array => ['folder_id' => $fid],
$this->batches[$batch]['files'] ?? []
);
}
return [];
}
};
};
/**
* Erzeugt den Manager ohne Konstruktor – der legt sonst das
* Ablageverzeichnis an, was für die reine Abfragelogik nicht nötig ist.
*/
$bauManager = static function (object $datenbank): FileExplorerManager {
$klasse = new ReflectionClass(FileExplorerManager::class);
$mgr = $klasse->newInstanceWithoutConstructor();
$feld = $klasse->getProperty('db');
$feld->setAccessible(true);
$feld->setValue($mgr, $datenbank);
return $mgr;
};
/** Ordnerbaum: /1/ öffentlich, /1/2/ mit Leserecht nur für Rolle 7. */
$baum = [
1 => ['path' => '/1/', 'created_by' => 99],
2 => ['path' => '/1/2/', 'created_by' => 99],
];
$rechte = [
2 => [['principal_type' => 'role', 'principal_id' => 7, 'permission' => 'view']],
];
// ----------------------------------------------------------------
// Rechte eines gelöschten Ordners
// ----------------------------------------------------------------
test('Ein gelöschter Ordner behält die Rechte seines Bereichs', function () use ($db, $baum, $rechte) {
$acl = new FileExplorerAcl($db($baum, $rechte));
// Rolle 7 darf lesen, aber nicht verwalten – Stufe „view".
assertTrue($acl->canView(2, 1, [7], false));
assertFalse($acl->canManage(2, 1, [7], false));
// Ohne passende Rolle bleibt der Bereich vollständig verschlossen.
assertFalse($acl->canView(2, 2, [8], false));
assertFalse($acl->canUpload(2, 2, [8], false));
});
test('Bereiche ohne Rechteeintrag bleiben für alle offen', function () use ($db, $baum, $rechte) {
$acl = new FileExplorerAcl($db($baum, $rechte));
assertTrue($acl->canManage(1, 2, [8], false));
assertTrue($acl->canManage(null, 2, [8], false));
});
test('Ohne jeden Rechteeintrag ändert sich für bestehende Instanzen nichts', function () use ($db, $baum) {
$acl = new FileExplorerAcl($db($baum, []));
assertTrue($acl->canManage(2, 2, [8], false));
assertTrue($acl->canView(2, 2, [8], false));
});
// ----------------------------------------------------------------
// Zuordnung eines Papierkorb-Stapels
// ----------------------------------------------------------------
test('Ein Stapel benennt alle Bereiche, aus denen er stammt', function () use ($db, $baum, $rechte, $bauManager) {
$stapel = [
'b1' => ['folders' => [2], 'files' => []],
'b2' => ['folders' => [], 'files' => [2, null]],
];
$mgr = $bauManager($db($baum, $rechte, $stapel));
assertSame([2], $mgr->batchAclScopes('b1'));
// Einzeldateien: ihr Ablageordner, `null` steht für die Wurzel.
$scopes = $mgr->batchAclScopes('b2');
assertTrue(in_array(2, $scopes, true), 'Ordner der Datei fehlt');
assertTrue(in_array(null, $scopes, true), 'Wurzel fehlt');
});
test('Ein leerer Stapel benennt keinen Bereich', function () use ($db, $baum, $rechte, $bauManager) {
$mgr = $bauManager($db($baum, $rechte, []));
assertSame([], $mgr->batchAclScopes('unbekannt'));
});
};