release(v1.9.12): feat(pro-acl): add user groups and group-aware ACL
This commit is contained in:
166
src/controllers/AclAdminController.php
Normal file
166
src/controllers/AclAdminController.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
// src/controllers/AclAdminController.php
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/lib/ACL.php';
|
||||
require_once PROJECT_ROOT . '/src/models/FolderModel.php';
|
||||
|
||||
class AclAdminController
|
||||
{
|
||||
|
||||
public function getUserGrants(string $user): array
|
||||
{
|
||||
if (!preg_match(REGEX_USER, $user)) {
|
||||
throw new InvalidArgumentException('Invalid user');
|
||||
}
|
||||
|
||||
$folders = [];
|
||||
try {
|
||||
$rows = FolderModel::getFolderList();
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $r) {
|
||||
$f = is_array($r) ? ($r['folder'] ?? '') : (string)$r;
|
||||
if ($f !== '') $folders[$f] = true;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// ignore, fall back to ACL file
|
||||
}
|
||||
|
||||
if (empty($folders)) {
|
||||
$aclPath = rtrim(META_DIR, "/\\") . DIRECTORY_SEPARATOR . 'folder_acl.json';
|
||||
if (is_file($aclPath)) {
|
||||
$data = json_decode((string)@file_get_contents($aclPath), true);
|
||||
if (is_array($data['folders'] ?? null)) {
|
||||
foreach ($data['folders'] as $name => $_) {
|
||||
$folders[$name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$folderList = array_keys($folders);
|
||||
if (!in_array('root', $folderList, true)) {
|
||||
array_unshift($folderList, 'root');
|
||||
}
|
||||
|
||||
$has = function(array $arr, string $u): bool {
|
||||
foreach ($arr as $x) {
|
||||
if (strcasecmp((string)$x, $u) === 0) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
$out = [];
|
||||
foreach ($folderList as $f) {
|
||||
$rec = ACL::explicitAll($f);
|
||||
|
||||
$isOwner = $has($rec['owners'], $user);
|
||||
$canViewAll = $isOwner || $has($rec['read'], $user);
|
||||
$canViewOwn = $has($rec['read_own'], $user);
|
||||
$canShare = $isOwner || $has($rec['share'], $user);
|
||||
$canUpload = $isOwner || $has($rec['write'], $user) || $has($rec['upload'], $user);
|
||||
|
||||
if (
|
||||
$canViewAll || $canViewOwn || $canUpload || $canShare || $isOwner
|
||||
|| $has($rec['create'], $user) || $has($rec['edit'], $user) || $has($rec['rename'], $user)
|
||||
|| $has($rec['copy'], $user) || $has($rec['move'], $user) || $has($rec['delete'], $user)
|
||||
|| $has($rec['extract'], $user) || $has($rec['share_file'], $user) || $has($rec['share_folder'], $user)
|
||||
) {
|
||||
$out[$f] = [
|
||||
'view' => $canViewAll,
|
||||
'viewOwn' => $canViewOwn,
|
||||
'write' => $has($rec['write'], $user) || $isOwner,
|
||||
'manage' => $isOwner,
|
||||
'share' => $canShare,
|
||||
'create' => $isOwner || $has($rec['create'], $user),
|
||||
'upload' => $isOwner || $has($rec['upload'], $user) || $has($rec['write'], $user),
|
||||
'edit' => $isOwner || $has($rec['edit'], $user) || $has($rec['write'], $user),
|
||||
'rename' => $isOwner || $has($rec['rename'], $user) || $has($rec['write'], $user),
|
||||
'copy' => $isOwner || $has($rec['copy'], $user) || $has($rec['write'], $user),
|
||||
'move' => $isOwner || $has($rec['move'], $user) || $has($rec['write'], $user),
|
||||
'delete' => $isOwner || $has($rec['delete'], $user) || $has($rec['write'], $user),
|
||||
'extract' => $isOwner || $has($rec['extract'], $user)|| $has($rec['write'], $user),
|
||||
'shareFile' => $isOwner || $has($rec['share_file'], $user) || $has($rec['share'], $user),
|
||||
'shareFolder' => $isOwner || $has($rec['share_folder'], $user) || $has($rec['share'], $user),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function saveUserGrantsPayload(array $payload): array
|
||||
{
|
||||
|
||||
$normalizeCaps = function (array $row): array {
|
||||
$bool = function ($v) {
|
||||
return !empty($v) && $v !== 'false' && $v !== 0;
|
||||
};
|
||||
$k = [
|
||||
'view','viewOwn','upload','manage','share',
|
||||
'create','edit','rename','copy','move','delete','extract',
|
||||
'shareFile','shareFolder','write'
|
||||
];
|
||||
$out = [];
|
||||
foreach ($k as $kk) {
|
||||
$out[$kk] = $bool($row[$kk] ?? false);
|
||||
}
|
||||
|
||||
if ($out['shareFolder'] && !$out['view']) {
|
||||
$out['view'] = true;
|
||||
}
|
||||
if ($out['shareFile'] && !$out['view'] && !$out['viewOwn']) {
|
||||
$out['viewOwn'] = true;
|
||||
}
|
||||
|
||||
return $out;
|
||||
};
|
||||
|
||||
$sanitizeGrantsMap = function (array $grants) use ($normalizeCaps): array {
|
||||
$out = [];
|
||||
foreach ($grants as $folder => $caps) {
|
||||
if (!is_string($folder)) $folder = (string)$folder;
|
||||
if (!is_array($caps)) $caps = [];
|
||||
$out[$folder] = $normalizeCaps($caps);
|
||||
}
|
||||
return $out;
|
||||
};
|
||||
|
||||
$validUser = function (string $u): bool {
|
||||
return ($u !== '' && preg_match(REGEX_USER, $u));
|
||||
};
|
||||
|
||||
// Single-user mode
|
||||
if (isset($payload['user'], $payload['grants']) && is_array($payload['grants'])) {
|
||||
$user = trim((string)$payload['user']);
|
||||
if (!$validUser($user)) {
|
||||
throw new InvalidArgumentException('Invalid user');
|
||||
}
|
||||
|
||||
$grants = $sanitizeGrantsMap($payload['grants']);
|
||||
|
||||
return ACL::applyUserGrantsAtomic($user, $grants);
|
||||
}
|
||||
|
||||
// Batch mode
|
||||
if (isset($payload['changes']) && is_array($payload['changes'])) {
|
||||
$updated = [];
|
||||
foreach ($payload['changes'] as $chg) {
|
||||
if (!is_array($chg)) continue;
|
||||
$user = trim((string)($chg['user'] ?? ''));
|
||||
$gr = $chg['grants'] ?? null;
|
||||
if (!$validUser($user) || !is_array($gr)) continue;
|
||||
|
||||
try {
|
||||
$res = ACL::applyUserGrantsAtomic($user, $sanitizeGrantsMap($gr));
|
||||
$updated[$user] = $res['updated'] ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
$updated[$user] = ['error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
return ['ok' => true, 'updated' => $updated];
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Invalid payload: expected {user,grants} or {changes:[{user,grants}]}');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user