<?php
namespace Nellapp\Bundle\SDKBundle\Permission;
use DateTime;
use Nellapp\Bundle\SDKBundle\Channel\Entity\ChannelInterface;
use Nellapp\Bundle\SDKBundle\Channel\Entity\ChannelUserData\AdminChannelUserDataInterface;
use Nellapp\Bundle\SDKBundle\Channel\Entity\ChannelUserData\ChannelUserDataInterface;
use Nellapp\Bundle\SDKBundle\Permission\Entity\ChannelPermissionResourceInterface;
use Nellapp\Bundle\SDKBundle\Permission\Entity\ChannelResourceInterface;
use Nellapp\Bundle\SDKBundle\Permission\Entity\ChannelUserPermissionInterface;
use Nellapp\Bundle\SDKBundle\Permission\Enum\ChannelUserPermissionEnumInterface;
use Nellapp\Bundle\SDKBundle\Permission\Security\Hierarchy\ChannelUserPermissionHierarchyInterface;
use Nellapp\Bundle\SDKBundle\Permission\UserOwner\UserOwnerGetter;
use Nellapp\Bundle\SDKBundle\Sync\ApiClient\Model\ChannelUserData;
class ChannelUserPermissionService
{
public function __construct(
private ChannelUserPermissionManager $channelUserPermissionManager,
private UserOwnerGetter $userOwnerGetter,
)
{
}
public function hasAccess(ChannelUserDataInterface $channelUserData, ChannelInterface|ChannelResourceInterface $subject, string $attribute): bool
{
if (!$channelUserData instanceof AdminChannelUserDataInterface) {
return false;
}
if ($subject instanceof ChannelInterface && $this->userOwnerGetter->isOwnerOf($channelUserData->getUser(), $subject)) {
return true;
}
if ($subject instanceof ChannelInterface && $channelUserData->getUser() === $subject->getOwner()) {
return true;
}
$availableAttribute = $this->isAvailableAttribute($subject, $attribute);
if ($availableAttribute === false) {
return false;
}
$channelUserPermissionInterface = $this->getChannelUserPermissionInterface($channelUserData, $subject);
if (!$channelUserPermissionInterface) {
return false;
}
if (!$this->isChannelUserPermissionActive($channelUserPermissionInterface)) {
return false;
}
return in_array($attribute, $this->getAvailablePermissions($subject, $channelUserPermissionInterface));
}
public function getChannelUserPermissionInterface(ChannelUserDataInterface $channelUserData, ChannelInterface|ChannelResourceInterface $subject): ?ChannelUserPermissionInterface
{
if (!$channelUserData instanceof AdminChannelUserDataInterface) {
return null;
}
if ($subject instanceof ChannelInterface) {
return $channelUserData;
}
$channelResourcePermissionRepository = $this->channelUserPermissionManager->getChannelPermissionResourceRepository($subject::getPermissionResourceClass());
return $channelResourcePermissionRepository?->findOneByChannelUserDataAndResource($channelUserData, $subject);
}
public function isChannelUserPermissionActive(ChannelUserPermissionInterface $channelUserPermission): bool
{
$startAt = $channelUserPermission->getStartAt();
$expireAt = $channelUserPermission->getExpireAt();
return $this->isBetween($startAt, $expireAt);
}
public function isBetween(?\DateTimeInterface $startAt, ?\DateTimeInterface $expireAt): bool
{
$now = new DateTime();
if ($startAt !== null && $startAt > $now) {
return false;
}
if ($expireAt !== null) {
$expireAtEnd = DateTime::createFromInterface($expireAt);
$expireAtEnd->setTime(23, 59, 59);
return $expireAtEnd >= $now;
}
return true;
}
private function getAvailablePermissions(ChannelInterface|ChannelResourceInterface $subject, ChannelUserPermissionInterface $channelUserPermissionInterface): array
{
$permissionHierarchies = $this->channelUserPermissionManager->getChannelUserPermissionHierarchies($subject);
if (empty($permissionHierarchies)) {
return $channelUserPermissionInterface->getPerms();
}
return array_unique(array_merge(...array_map(function (ChannelUserPermissionHierarchyInterface $channelUserPermissionHierarchy) use ($channelUserPermissionInterface) {
return $channelUserPermissionHierarchy->getReachableRoleNames($channelUserPermissionInterface->getPerms());
}, $permissionHierarchies)));
}
private function getAvailablePermissionForSubject(ChannelInterface|ChannelResourceInterface $subject): array
{
$channelUserPermissionEnums = $this->channelUserPermissionManager->getChannelUserPermissionEnums($subject);
if (empty($channelUserPermissionEnums)) {
return [];
}
return array_unique(array_merge(...array_map(function (ChannelUserPermissionEnumInterface $channelUserPermissionEnum) {
return $channelUserPermissionEnum->getChoices();
}, $channelUserPermissionEnums)));
}
private function getAvailablePermissionsWithHierarchyForSubject(ChannelInterface|ChannelResourceInterface $subject): array
{
$permissionHierarchies = $this->channelUserPermissionManager->getChannelUserPermissionHierarchies($subject);
if (empty($permissionHierarchies)) {
return $this->getAvailablePermissionForSubject($subject);
}
return array_unique(array_merge(...array_map(function (ChannelUserPermissionHierarchyInterface $channelUserPermissionHierarchy) use ($subject) {
return $channelUserPermissionHierarchy->getReachableRoleNames($this->getAvailablePermissionForSubject($subject));
}, $permissionHierarchies)));
}
/**
* @param array $availablePermissions
* @param string $attribute
* @return array
*
* Check if given attribute exists in the list of available permissions for the resource
*/
private function isAvailableAttribute(ChannelInterface|ChannelResourceInterface $subject, string $attribute): bool
{
return in_array($attribute, $this->getAvailablePermissionsWithHierarchyForSubject($subject));
}
/**
* @param ChannelResourceInterface|ChannelInterface $subject
* @param string|string[] $perms
* @return ChannelUserData[]
*/
public function getAllChannelUserDataForGivenPermissions(ChannelResourceInterface|ChannelInterface $subject, string|array $perms): array
{
if (!is_array($perms)) {
$perms = [$perms];
}
if ($subject instanceof ChannelResourceInterface) {
return $this->getChannelUserDataForChannelResourceWithPermissions($subject, $perms);
}
return $this->getChannelUserDataForChannelWithPermissions($subject, $perms);
}
private function getChannelUserDataForChannelWithPermissions(ChannelInterface $channel, array $perms): array
{
return [$channel->getOwner()->getChannelUserDataByChannel($channel)];
}
private function getChannelUserDataForChannelResourceWithPermissions(ChannelResourceInterface $channelResource, array $perms): array
{
$channelUserPermissionResourceRepository = $this->channelUserPermissionManager
->getChannelPermissionResourceRepository($channelResource::getPermissionResourceClass());
if ($channelUserPermissionResourceRepository === null) {
return [];
}
$qb = $channelUserPermissionResourceRepository->queryAllByResource($channelResource);
foreach ($perms as $perm) {
$qb
->andWhere('FIND_IN_SET(:perm, c_u_p.perms) > 0')
->setParameter('perm', $perm);
}
$channelPermissionResourceInterfaceList = $qb
->addSelect('channel_user_data')
->innerJoin('c_u_p.channelUserData', 'channel_user_data')
->getQuery()
->getResult();
return array_map(function (ChannelPermissionResourceInterface $channelPermissionResourceInterface) {
return $channelPermissionResourceInterface->getChannelUserData();
}, $channelPermissionResourceInterfaceList);
}
public function getAllUserForGivenPermissions(ChannelResourceInterface|ChannelInterface $subject, string|array $perms): array
{
return array_map(function (ChannelUserDataInterface $channelUserData) {
return $channelUserData->getUser();
}, $this->getAllChannelUserDataForGivenPermissions($subject, $perms));
}
}