Skip to content

Commit

Permalink
Merge pull request #3551 from nextcloud/feat/noid/acl-circles
Browse files Browse the repository at this point in the history
include circles to acl
  • Loading branch information
icewind1991 authored Feb 6, 2025
2 parents 4ac49eb + 84efc70 commit 973ae92
Show file tree
Hide file tree
Showing 18 changed files with 960 additions and 61 deletions.
4 changes: 3 additions & 1 deletion lib/ACL/UserMapping/IUserMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
namespace OCA\GroupFolders\ACL\UserMapping;

interface IUserMapping {
/** @return 'user'|'group'|'dummy' */
/** @return 'user'|'group'|'dummy'|'circle' */
public function getType(): string;

public function getId(): string;

public function getDisplayName(): string;

public function getKey(): string;
}
9 changes: 9 additions & 0 deletions lib/ACL/UserMapping/IUserMappingManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,13 @@ interface IUserMappingManager {
public function getMappingsForUser(IUser $user, bool $userAssignable = true): array;

public function mappingFromId(string $type, string $id): ?IUserMapping;

/**
* Check if a user is a member of one of the provided user mappings
*
* @param IUser $user
* @param IUserMapping[] $mappings
* @return bool
*/
public function userInMappings(IUser $user, array $mappings): bool;
}
6 changes: 5 additions & 1 deletion lib/ACL/UserMapping/UserMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class UserMapping implements IUserMapping {
private readonly string $displayName;

/**
* @param 'user'|'group'|'dummy' $type
* @param 'user'|'group'|'dummy'|'circle' $type
*/
public function __construct(
private readonly string $type,
Expand All @@ -33,4 +33,8 @@ public function getId(): string {
public function getDisplayName(): string {
return $this->displayName;
}

public function getKey(): string {
return $this->getType() . ':' . $this->getId();
}
}
108 changes: 101 additions & 7 deletions lib/ACL/UserMapping/UserMappingManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,128 @@

namespace OCA\GroupFolders\ACL\UserMapping;

use OCA\Circles\CirclesManager;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Probes\CircleProbe;
use OCP\AutoloadNotAllowedException;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Server;
use Psr\Container\ContainerExceptionInterface;
use Psr\Log\LoggerInterface;

class UserMappingManager implements IUserMappingManager {
public function __construct(
private readonly IGroupManager $groupManager,
private readonly IUserManager $userManager,
private readonly LoggerInterface $logger,
) {
}

public function getMappingsForUser(IUser $user, bool $userAssignable = true): array {
$groupMappings = array_values(array_map(fn (IGroup $group): UserMapping => new UserMapping('group', $group->getGID(), $group->getDisplayName()), $this->groupManager->getUserGroups($user)));
$circleMappings = array_values(array_map(fn (Circle $circle): UserMapping => new UserMapping('circle', $circle->getSingleId(), $circle->getDisplayName()), $this->getUserCircles($user->getUID())));

return array_merge([
new UserMapping('user', $user->getUID(), $user->getDisplayName()),
], $groupMappings);
], $groupMappings, $circleMappings);
}

public function mappingFromId(string $type, string $id): ?IUserMapping {
$mappingObject = ($type === 'group' ? $this->groupManager : $this->userManager)->get($id);
if ($mappingObject) {
$displayName = $mappingObject->getDisplayName();
/** @var 'user'|'group' $type */
return new UserMapping($type, $id, $displayName);
} else {
switch ($type) {
case 'group':
$displayName = $this->groupManager->get($id)?->getDisplayName();
break;
case 'user':
$displayName = $this->userManager->get($id)?->getDisplayName();
break;
case 'circle':
$displayName = $this->getCircle($id)?->getDisplayName();
break;
default:
return null;
}
if ($displayName === null) {
return null;
}

return new UserMapping($type, $id, $displayName);
}



/**
* returns the Circle from its single Id, or NULL if not available
*/
private function getCircle(string $groupId): ?Circle {
$circlesManager = $this->getCirclesManager();
if ($circlesManager === null) {
return null;
}

$circlesManager->startSuperSession();
$probe = new CircleProbe();
$probe->includeSystemCircles();
$probe->includeSingleCircles();
try {
return $circlesManager->getCircle($groupId, $probe);
} catch (CircleNotFoundException) {
} catch (\Exception $e) {
$this->logger->warning('', ['exception' => $e]);
} finally {
$circlesManager->stopSession();
}

return null;
}

/**
* returns list of circles a user is member of
*/
private function getUserCircles(string $userId): array {
$circlesManager = $this->getCirclesManager();
if ($circlesManager === null) {
return [];
}

$circlesManager->startSession($circlesManager->getLocalFederatedUser($userId));
try {
return $circlesManager->probeCircles();
} catch (\Exception $e) {
$this->logger->warning('', ['exception' => $e]);
} finally {
$circlesManager->stopSession();
}

return [];
}

public function getCirclesManager(): ?CirclesManager {
try {
return Server::get(CirclesManager::class);
} catch (ContainerExceptionInterface|AutoloadNotAllowedException) {
return null;
}
}

public function userInMappings(IUser $user, array $mappings): bool {
foreach ($mappings as $mapping) {
if ($mapping->getType() === 'user' && $mapping->getId() === $user->getUID()) {
return true;
}
}

$mappingKeys = array_map(fn (IUserMapping $mapping) => $mapping->getKey(), $mappings);

$userMappings = $this->getMappingsForUser($user);
foreach ($userMappings as $userMapping) {
if (in_array($userMapping->getKey(), $mappingKeys, true)) {
return true;
}
}
return false;
}
}
2 changes: 1 addition & 1 deletion lib/Command/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}, array_keys($folder['groups']), array_values($folder['groups']));
$folder['groups'] = implode("\n", $groupStrings);
$folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled';
$manageStrings = array_map(fn (array $manage): string => $manage['id'] . ' (' . $manage['type'] . ')', $folder['manage']);
$manageStrings = array_map(fn (array $manage): string => $manage['displayname'] . ' (' . $manage['type'] . ')', $folder['manage']);
$folder['manage'] = implode("\n", $manageStrings);

return $folder;
Expand Down
8 changes: 5 additions & 3 deletions lib/Controller/FolderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

/**
* @psalm-import-type GroupFoldersGroup from ResponseDefinitions
* @psalm-import-type GroupFoldersCircle from ResponseDefinitions
* @psalm-import-type GroupFoldersUser from ResponseDefinitions
* @psalm-import-type GroupFoldersFolder from ResponseDefinitions
* @psalm-import-type InternalFolderOut from FolderManager
Expand Down Expand Up @@ -474,16 +475,15 @@ private function folderDataForXML(array $data): array {
*
* @param int $id The ID of the Groupfolder
* @param string $search String to search by
* @return DataResponse<Http::STATUS_OK, array{users: list<GroupFoldersUser>, groups: list<GroupFoldersGroup>}, array{}>
* @return DataResponse<Http::STATUS_OK, array{users: list<GroupFoldersUser>, groups: list<GroupFoldersGroup>, circles: list<GroupFoldersCircle>}, array{}>
* @throws OCSForbiddenException Not allowed to search
*
* 200: ACL Mappings returned
*/
#[NoAdminRequired]
#[FrontpageRoute(verb: 'GET', url: '/folders/{id}/search')]
public function aclMappingSearch(int $id, string $search = ''): DataResponse {
$users = [];
$groups = [];
$users = $groups = $circles = [];

if ($this->user === null) {
throw new OCSForbiddenException();
Expand All @@ -492,11 +492,13 @@ public function aclMappingSearch(int $id, string $search = ''): DataResponse {
if ($this->manager->canManageACL($id, $this->user) === true) {
$groups = $this->manager->searchGroups($id, $search);
$users = $this->manager->searchUsers($id, $search);
$circles = $this->manager->searchCircles($id, $search);
}

return new DataResponse([
'users' => $users,
'groups' => $groups,
'circles' => $circles
]);
}
}
Loading

0 comments on commit 973ae92

Please sign in to comment.