0
0
Fork 0
mirror of https://github.com/verdigado/organization_folders.git synced 2024-12-06 11:22:41 +01:00

split principal types into different subclasses, added principal factory, implemented principal friendlyNames and FullHierarchyNames, added parentOrganizationId attribute to organizations

This commit is contained in:
Jonathan Treffler 2024-11-26 18:19:05 +01:00
parent 225072bff7
commit 89ff5415dd
15 changed files with 321 additions and 47 deletions

View file

@ -11,6 +11,7 @@ use OCA\OrganizationFolders\Service\ResourceService;
use OCA\OrganizationFolders\Service\ResourceMemberService;
use OCA\OrganizationFolders\OrganizationProvider\OrganizationProviderManager;
use OCA\OrganizationFolders\Interface\TableSerializable;
use OCA\OrganizationFolders\Model\PrincipalFactory;
abstract class BaseCommand extends Base {
@ -21,6 +22,7 @@ abstract class BaseCommand extends Base {
protected readonly ResourceService $resourceService,
protected readonly ResourceMemberService $resourceMemberService,
protected readonly OrganizationProviderManager $organizationProviderManager,
protected readonly PrincipalFactory $principalFactory,
) {
parent::__construct();
}

View file

@ -31,11 +31,13 @@ class CreateOrganizationFolderMember extends BaseCommand {
$principalType = PrincipalType::fromNameOrValue($input->getOption('principal-type'));
$principalId = $input->getOption('principal-id');
$principal = $this->principalFactory->buildPrincipal($principalType, $principalId);
try {
$member = $this->organizationFolderMemberService->create(
organizationFolderId: $organizationFolderId,
permissionLevel: $permissionLevel,
principal: new Principal($principalType, $principalId),
principal: $principal,
);
$this->writeTableInOutputFormat($input, $output, [$this->formatTableSerializable($member)]);

View file

@ -31,11 +31,13 @@ class CreateResourceMember extends BaseCommand {
$principalType = PrincipalType::fromNameOrValue($input->getOption('principal-type'));
$principalId = $input->getOption('principal-id');
$principal = $this->principalFactory->buildPrincipal($principalType, $principalId);
try {
$member = $this->resourceMemberService->create(
resourceId: $resourceId,
permissionLevel: $permissionLevel,
principal: new Principal($principalType, $principalId),
principal: $principal,
);
$this->writeTableInOutputFormat($input, $output, [$this->formatTableSerializable($member)]);

View file

@ -2,7 +2,6 @@
namespace OCA\OrganizationFolders\Controller;
use OCA\OrganizationFolders\Model\Principal;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
@ -11,6 +10,7 @@ use OCA\OrganizationFolders\Service\ResourceService;
use OCA\OrganizationFolders\Service\ResourceMemberService;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\Enum\ResourceMemberPermissionLevel;
use OCA\OrganizationFolders\Model\PrincipalFactory;
class ResourceMemberController extends BaseController {
use Errors;
@ -18,6 +18,7 @@ class ResourceMemberController extends BaseController {
public function __construct(
private ResourceMemberService $service,
private ResourceService $resourceService,
private PrincipalFactory $principalFactory,
private string $userId,
) {
parent::__construct();
@ -46,10 +47,12 @@ class ResourceMemberController extends BaseController {
$this->denyAccessUnlessGranted(['UPDATE_MEMBERS'], $resource);
$principal = $this->principalFactory->buildPrincipal(PrincipalType::fromNameOrValue($principalType), $principalId);
$resourceMember = $this->service->create(
resourceId: $resourceId,
permissionLevel: ResourceMemberPermissionLevel::fromNameOrValue($permissionLevel),
principal: new Principal(PrincipalType::fromNameOrValue($principalType), $principalId),
principal: $principal,
);
return $resourceMember;

View file

@ -14,8 +14,11 @@ use OCA\OrganizationFolders\Model\Principal;
class OrganizationFolderMember extends Entity implements JsonSerializable, TableSerializable {
protected $organizationFolderId;
protected $permissionLevel;
protected $principalType;
protected $principalId;
/**
* @var Principal
*/
protected $principal;
protected $createdTimestamp;
protected $lastUpdatedTimestamp;
@ -26,20 +29,33 @@ class OrganizationFolderMember extends Entity implements JsonSerializable, Table
$this->addType('createdTimestamp','integer');
$this->addType('lastUpdatedTimestamp','integer');
}
public function getPrincipal(): Principal {
return new Principal(PrincipalType::from($this->principalType), $this->principalId);
}
public function setPrincipal(Principal $principal) {
$principalType = $principal->getType();
if($principalType === PrincipalType::GROUP || $principalType === PrincipalType::ROLE) {
$this->setPrincipalType($principalType->value);
} else {
throw new \Exception("individual users are not allowed as organization folder members");
}
if($principal->getType() === PrincipalType::GROUP || $principal->getType() === PrincipalType::ROLE) {
if(!isset($this->principal) || $this->principal->getType() !== $principal->getType()) {
$this->markFieldUpdated("principalType");
$principalTypeUpdated = true;
}
$this->setPrincipalId($principal->getId());
if(!isset($this->principal) || $this->principal->getId() !== $principal->getId()) {
$this->markFieldUpdated("principalId");
$principalIdUpdated = true;
}
if($principalTypeUpdated || $principalIdUpdated) {
$this->principal = $principal;
}
} else {
throw new \Exception("individual users are not allowed as organization folder members");
}
}
public function getPrincipalType(): int {
return $this->principal?->getType()->value;
}
public function getPrincipalId(): string|null {
return $this->principal?->getId();
}
public function setPermissionLevel(int $permissionLevel) {
@ -72,8 +88,9 @@ class OrganizationFolderMember extends Entity implements JsonSerializable, Table
'Id' => $this->id,
'Organization Folder Id' => $this->organizationFolderId,
'Permission Level' => OrganizationFolderMemberPermissionLevel::from($this->permissionLevel)->name,
'Principal Type' => PrincipalType::from($this->principalType)->name,
'Principal Id' => $this->principalId,
'Principal Type' => $this->principal?->getType()->name,
'Principal Id' => $this->principal?->getId(),
'Principal Friendly Name' => $this->principal?->getFriendlyName(),
'Created' => $this->createdTimestamp,
'LastUpdated' => $this->lastUpdatedTimestamp,
];

View file

@ -8,13 +8,44 @@ use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\Model\PrincipalFactory;
class OrganizationFolderMemberMapper extends QBMapper {
public const ORGANIZATIONFOLDER_MEMBERS_TABLE = "organizationfolders_members";
public function __construct(IDBConnection $db) {
public function __construct(
protected PrincipalFactory $principalFactory,
IDBConnection $db,
) {
parent::__construct($db, self::ORGANIZATIONFOLDER_MEMBERS_TABLE, OrganizationFolderMember::class);
}
/**
*
* @param array $row the row which should be converted to an entity
* @return OrganizationFolderMember the entity
* @psalm-return OrganizationFolderMember the entity
*/
protected function mapRowToEntity(array $row): OrganizationFolderMember {
$member = new OrganizationFolderMember();
$member->setId($row["id"]);
$member->setOrganizationFolderId($row["organization_folder_id"]);
$member->setPermissionLevel($row["permission_level"]);
$principalType = PrincipalType::from($row["principal_type"]);
$principal = $this->principalFactory->buildPrincipal($principalType, $row["principal_id"]);
$member->setPrincipal($principal);
$member->setCreatedTimestamp($row["created_timestamp"]);
$member->setLastUpdatedTimestamp($row["last_updated_timestamp"]);
$member->resetUpdatedFields();
return $member;
}
/**
* @param int $id
* @return Entity|OrganizationFolderMember

View file

@ -14,8 +14,11 @@ use OCA\OrganizationFolders\Model\Principal;
class ResourceMember extends Entity implements JsonSerializable, TableSerializable {
protected $resourceId;
protected $permissionLevel;
protected $principalType;
protected $principalId;
/**
* @var Principal
*/
protected $principal;
protected $createdTimestamp;
protected $lastUpdatedTimestamp;
@ -26,14 +29,29 @@ class ResourceMember extends Entity implements JsonSerializable, TableSerializab
$this->addType('createdTimestamp','integer');
$this->addType('lastUpdatedTimestamp','integer');
}
public function getPrincipal(): Principal {
return new Principal(PrincipalType::from($this->principalType), $this->principalId);
}
public function setPrincipal(Principal $principal) {
$this->setPrincipalType($principal->getType()->value);
$this->setPrincipalId($principal->getId());
if(!isset($this->principal) || $this->principal->getType() !== $principal->getType()) {
$this->markFieldUpdated("principalType");
$principalTypeUpdated = true;
}
if(!isset($this->principal) || $this->principal->getId() !== $principal->getId()) {
$this->markFieldUpdated("principalId");
$principalIdUpdated = true;
}
if($principalTypeUpdated || $principalIdUpdated) {
$this->principal = $principal;
}
}
public function getPrincipalType(): int {
return $this->principal?->getType()->value;
}
public function getPrincipalId(): string|null {
return $this->principal?->getId();
}
public function setPermissionLevel(int $permissionLevel) {
@ -55,7 +73,7 @@ class ResourceMember extends Entity implements JsonSerializable, TableSerializab
'id' => $this->id,
'resourceId' => $this->resourceId,
'permissionLevel' => $this->permissionLevel,
'principal' => $this->getPrincipal(),
'principal' => $this->principal,
'createdTimestamp' => $this->createdTimestamp,
'lastUpdatedTimestamp' => $this->lastUpdatedTimestamp,
];
@ -66,8 +84,9 @@ class ResourceMember extends Entity implements JsonSerializable, TableSerializab
'Id' => $this->id,
'Resource Id' => $this->resourceId,
'Permission Level' => ResourceMemberPermissionLevel::from($this->permissionLevel)->name,
'Principal Type' => PrincipalType::from($this->principalType)->name,
'Principal Id' => $this->principalId,
'Principal Type' => $this->principal?->getType()->name,
'Principal Id' => $this->principal?->getId(),
'Principal Friendly Name' => $this->principal?->getFriendlyName(),
'Created' => $this->createdTimestamp,
'LastUpdated' => $this->lastUpdatedTimestamp,
];

View file

@ -8,13 +8,44 @@ use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\Model\PrincipalFactory;
class ResourceMemberMapper extends QBMapper {
public const RESOURCE_MEMBERS_TABLE = "organizationfolders_resource_members";
public function __construct(IDBConnection $db) {
public function __construct(
protected PrincipalFactory $principalFactory,
IDBConnection $db,
) {
parent::__construct($db, self::RESOURCE_MEMBERS_TABLE, ResourceMember::class);
}
/**
*
* @param array $row the row which should be converted to an entity
* @return ResourceMember the entity
* @psalm-return ResourceMember the entity
*/
protected function mapRowToEntity(array $row): ResourceMember {
$resourceMember = new ResourceMember();
$resourceMember->setId($row["id"]);
$resourceMember->setResourceId($row["resource_id"]);
$resourceMember->setPermissionLevel($row["permission_level"]);
$principalType = PrincipalType::from($row["principal_type"]);
$principal = $this->principalFactory->buildPrincipal($principalType, $row["principal_id"]);
$resourceMember->setPrincipal($principal);
$resourceMember->setCreatedTimestamp($row["created_timestamp"]);
$resourceMember->setLastUpdatedTimestamp($row["last_updated_timestamp"]);
$resourceMember->resetUpdatedFields();
return $resourceMember;
}
/**
* @param int $id
* @return Entity|ResourceMember

View file

@ -0,0 +1,36 @@
<?php
namespace OCA\OrganizationFolders\Model;
use OCP\IGroupManager;
use \OCP\IGroup;
use OCA\OrganizationFolders\Enum\PrincipalType;
class GroupPrincipal extends Principal {
private ?IGroup $group;
public function __construct(
private IGroupManager $groupManager,
private string $id,
) {
try {
$this->group = $this->groupManager->get($id);
$this->valid = !is_null($this->group);
} catch (\Exception $e) {
$this->valid = false;
}
}
public function getType(): PrincipalType {
return PrincipalType::GROUP;
}
public function getId(): string {
return $this->id;
}
public function getFriendlyName(): string {
return $this->group?->getDisplayName();
}
}

View file

@ -9,6 +9,7 @@ class Organization implements \JsonSerializable, TableSerializable {
private int $id,
private string $friendlyName,
private string $membersGroup,
private ?int $parentOrganizationId = null,
) {
}
@ -24,6 +25,10 @@ class Organization implements \JsonSerializable, TableSerializable {
return $this->membersGroup;
}
public function getParentOrganizationId(): ?int {
return $this->parentOrganizationId;
}
public function jsonSerialize(): array {
return [
'id' => $this->id,

View file

@ -4,26 +4,30 @@ namespace OCA\OrganizationFolders\Model;
use OCA\OrganizationFolders\Enum\PrincipalType;
class Principal implements \JsonSerializable {
public function __construct(
private PrincipalType $type,
private string $id,
) {
// check if id fits format
}
abstract class Principal implements \JsonSerializable {
protected bool $valid;
public function getType(): PrincipalType {
return $this->type;
}
abstract public function getType(): PrincipalType;
public function getId(): string {
return $this->id;
abstract public function getId(): string;
abstract public function getFriendlyName(): string;
/**
* @return array
* @psalm-return string[]
*/
public function getFullHierarchyNames(): array {
return [$this->getFriendlyName()];
}
public function jsonSerialize(): array {
return [
'type' => $this->type->value,
'id' => $this->id,
'type' => $this->getType(),
'id' => $this->getId(),
'valid' => $this->valid,
'friendlyName' => $this->getFriendlyName(),
'fullHierarchyNames' => $this->getFullHierarchyNames(),
];
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace OCA\OrganizationFolders\Model;
use OCP\IUserManager;
use OCP\IGroupManager;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\OrganizationProvider\OrganizationProviderManager;
class PrincipalFactory {
public function __construct(
protected IUserManager $userManager,
protected IGroupManager $groupManager,
protected OrganizationProviderManager $organizationProviderManager,
) {
}
public function buildPrincipal(PrincipalType $type, string $id): Principal {
if($type === PrincipalType::USER) {
return new UserPrincipal($this->userManager, $id);
} else if ($type === PrincipalType::GROUP) {
return new GroupPrincipal($this->groupManager, $id);
} else if ($type === PrincipalType::ROLE) {
[$organizationProviderId, $roleId] = explode(":", $id, 2);
if(!(isset($organizationProviderId) && isset($roleId))) {
throw new \Exception("Invalid id format for principal of type role");
}
return new RolePrincipal($this->organizationProviderManager, $organizationProviderId, $roleId);
} else {
throw new \Exception("invalid PrincipalType");
}
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace OCA\OrganizationFolders\Model;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\OrganizationProvider\OrganizationProviderManager;
class RolePrincipal extends Principal {
private ?OrganizationRole $role;
public function __construct(
private OrganizationProviderManager $organizationProviderManager,
private string $providerId,
private string $roleId,
) {
try {
$this->role = $this->organizationProviderManager->getOrganizationProvider($providerId)->getRole($roleId);
$this->valid = !is_null($this->role);
} catch (\Exception $e) {
$this->valid = false;
}
}
public function getType(): PrincipalType {
return PrincipalType::ROLE;
}
public function getId(): string {
return $this->providerId . ":" . $this->roleId;
}
public function getFriendlyName(): string {
return $this->role->getFriendlyName();
}
public function getFullHierarchyNames(): array {
$result = [];
$result[] = $this->getFriendlyName();
$organizationProvider = $this->organizationProviderManager->getOrganizationProvider($this->providerId);
$organization = $organizationProvider->getOrganization($this->role->getOrganizationId());
$result[] = $organization->getFriendlyName();
while($organization->getParentOrganizationId() && $organization = $organizationProvider->getOrganization($organization->getParentOrganizationId())) {
$result[] = $organization->getFriendlyName();
}
return array_reverse($result);
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace OCA\OrganizationFolders\Model;
use OCP\IUserManager;
use \OCP\IUser;
use OCA\OrganizationFolders\Enum\PrincipalType;
class UserPrincipal extends Principal {
private ?IUser $user;
public function __construct(
private IUserManager $userManager,
private string $id,
) {
try {
$this->user = $this->userManager->get($id);
$this->valid = !is_null($this->user);
} catch (\Exception $e) {
$this->valid = false;
}
}
public function getType(): PrincipalType {
return PrincipalType::USER;
}
public function getId(): string {
return $this->id;
}
public function getFriendlyName(): string {
return $this->user?->getDisplayName();
}
}

View file

@ -17,6 +17,7 @@ use OCA\OrganizationFolders\Db\FolderResource;
use OCA\OrganizationFolders\Db\ResourceMapper;
use OCA\OrganizationFolders\Model\OrganizationFolder;
use \OCA\OrganizationFolders\Model\Principal;
use OCA\OrganizationFolders\Model\PrincipalFactory;
use OCA\OrganizationFolders\Enum\ResourceMemberPermissionLevel;
use OCA\OrganizationFolders\Enum\PrincipalType;
use OCA\OrganizationFolders\Errors\InvalidResourceType;
@ -35,6 +36,7 @@ class ResourceService {
protected OrganizationProviderManager $organizationProviderManager,
protected OrganizationFolderService $organizationFolderService,
protected ContainerInterface $container,
protected PrincipalFactory $principalFactory,
) {
}
@ -215,7 +217,7 @@ class ResourceService {
$inheritingPrincipals = [];
foreach($inheritingGroups as $inheritingGroup) {
$inheritingPrincipals[] = new Principal(PrincipalType::GROUP, $inheritingGroup);
$inheritingPrincipals[] = $this->principalFactory->buildPrincipal(PrincipalType::GROUP, $inheritingGroup);
}
return $this->recursivelySetFolderResourceALCs($topLevelFolderResources, "", $inheritingPrincipals);