-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2381551
Showing
41 changed files
with
9,707 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/vendor | ||
.phpunit.* | ||
.php_cs.cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
$finder = PhpCsFixer\Finder::create() | ||
->in(__DIR__) | ||
; | ||
|
||
return PhpCsFixer\Config::create() | ||
->setRules([ | ||
'@Symfony' => true, | ||
'array_syntax' => ['syntax' => 'short'], | ||
]) | ||
->setFinder($finder) | ||
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Alchemy\AclBundle; | ||
|
||
interface AclObjectInterface | ||
{ | ||
public function getId(): string; | ||
public function getAclOwnerId(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Alchemy\AclBundle\Admin; | ||
|
||
use Symfony\Component\Routing\Annotation\Route; | ||
|
||
trait PermissionTrait | ||
{ | ||
protected PermissionView $permissionView; | ||
|
||
/** | ||
* @required | ||
*/ | ||
public function setPermissionView(PermissionView $permissionView): void | ||
{ | ||
$this->permissionView = $permissionView; | ||
} | ||
|
||
public function permissionsAction() | ||
{ | ||
return $this->render('@AlchemyAcl/permissions/entity/acl.html.twig', | ||
$this->permissionView->getViewParameters( | ||
$this->permissionView->getObjectKey($this->entity['class']), | ||
$this->request->query->get('id') | ||
)); | ||
} | ||
|
||
/** | ||
* @Route(path="/aces/{type}/global", name="admin_global_permissions") | ||
*/ | ||
public function globalPermissionsAction(string $type) | ||
{ | ||
return $this->render( | ||
'@AlchemyAcl/permissions/global/acl.html.twig', | ||
$this->permissionView->getViewParameters($type, null) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Alchemy\AclBundle\Admin; | ||
|
||
use Alchemy\AclBundle\Entity\AccessControlEntry; | ||
use Alchemy\AclBundle\Mapping\ObjectMapping; | ||
use Alchemy\RemoteAuthBundle\Repository\GroupRepositoryInterface; | ||
use Alchemy\RemoteAuthBundle\Repository\UserRepositoryInterface; | ||
use Alchemy\AclBundle\Repository\PermissionRepositoryInterface; | ||
use Alchemy\AclBundle\Security\PermissionInterface; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
class PermissionView | ||
{ | ||
private ObjectMapping $objectMapping; | ||
private PermissionRepositoryInterface $repository; | ||
private UserRepositoryInterface $userRepository; | ||
private GroupRepositoryInterface $groupRepository; | ||
private EntityManagerInterface $em; | ||
|
||
public function __construct( | ||
ObjectMapping $objectMapping, | ||
PermissionRepositoryInterface $repository, | ||
UserRepositoryInterface $userRepository, | ||
GroupRepositoryInterface $groupRepository, | ||
EntityManagerInterface $em | ||
) { | ||
$this->objectMapping = $objectMapping; | ||
$this->repository = $repository; | ||
$this->userRepository = $userRepository; | ||
$this->groupRepository = $groupRepository; | ||
$this->em = $em; | ||
} | ||
|
||
public function getObjectKey(string $entityClass): string | ||
{ | ||
return $this->objectMapping->getObjectKey($entityClass); | ||
} | ||
|
||
public function getViewParameters(string $objectKey, ?string $id): array | ||
{ | ||
$permissions = PermissionInterface::PERMISSIONS; | ||
$aces = []; | ||
if (null !== $id) { | ||
$aces = array_merge($aces, $this->repository->getObjectAces($objectKey, null)); | ||
} | ||
$aces = array_merge($aces, $this->repository->getObjectAces($objectKey, $id)); | ||
|
||
$users = [ | ||
AccessControlEntry::USER_WILDCARD => 'All users', | ||
]; | ||
foreach ($this->userRepository->getUsers() as $user) { | ||
$users[$user['id']] = $user['username']; | ||
} | ||
$groups = []; | ||
foreach ($this->groupRepository->getGroups() as $group) { | ||
$groups[$group['id']] = $group['name']; | ||
} | ||
|
||
$aces = array_map(function (AccessControlEntry $ace) use ($users, $groups, $permissions): array { | ||
$name = $ace->getUserId(); | ||
switch ($ace->getUserType()) { | ||
case AccessControlEntry::TYPE_USER_VALUE: | ||
$name = $ace->getUserId() ? ($users[$ace->getUserId()] ?? $name) : AccessControlEntry::USER_WILDCARD; | ||
break; | ||
case AccessControlEntry::TYPE_GROUP_VALUE: | ||
$name = $groups[$ace->getUserId()] ?? $name; | ||
break; | ||
} | ||
|
||
return [ | ||
'userType' => $ace->getUserTypeString(), | ||
'userId' => $ace->getUserId() ?? AccessControlEntry::USER_WILDCARD, | ||
'name' => $name, | ||
'objectId' => $ace->getObjectId(), | ||
'permissions' => array_map(fn(int $p): bool => $ace->hasPermission($p), $permissions), | ||
]; | ||
}, $aces); | ||
|
||
$objectTitle = null; | ||
if ($id) { | ||
$object = $this->em->getRepository($this->objectMapping->getClassName($objectKey))->find($id); | ||
if (null !== $object && method_exists($object, '__toString')) { | ||
$objectTitle = (string)$object; | ||
} | ||
} | ||
|
||
$params = [ | ||
'USER_WILDCARD' => AccessControlEntry::USER_WILDCARD, | ||
'permissions' => $permissions, | ||
'aces' => $aces, | ||
'users' => $users, | ||
'groups' => $groups, | ||
'object_type' => $objectKey, | ||
'object_title' => $objectTitle, | ||
]; | ||
|
||
if (null !== $id) { | ||
$params['object_id'] = $id; | ||
} | ||
|
||
return $params; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Alchemy\AclBundle; | ||
|
||
use Symfony\Component\HttpKernel\Bundle\Bundle; | ||
|
||
class AlchemyAclBundle extends Bundle | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Alchemy\AclBundle\Controller; | ||
|
||
use Alchemy\AclBundle\AclObjectInterface; | ||
use Alchemy\AclBundle\Mapping\ObjectMapping; | ||
use Alchemy\AclBundle\Model\AccessControlEntryInterface; | ||
use Alchemy\RemoteAuthBundle\Repository\GroupRepositoryInterface; | ||
use Alchemy\RemoteAuthBundle\Repository\UserRepositoryInterface; | ||
use Alchemy\AclBundle\Repository\PermissionRepositoryInterface; | ||
use Alchemy\AclBundle\Security\PermissionInterface; | ||
use Alchemy\AclBundle\Security\PermissionManager; | ||
use Alchemy\AclBundle\Serializer\AceSerializer; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
use Symfony\Component\Routing\Annotation\Route; | ||
|
||
class PermissionController extends AbstractController | ||
{ | ||
private PermissionManager $permissionManager; | ||
private EntityManagerInterface $em; | ||
private ObjectMapping $objectMapping; | ||
|
||
public function __construct(PermissionManager $permissionManager, EntityManagerInterface $em, ObjectMapping $objectMapping) | ||
{ | ||
$this->permissionManager = $permissionManager; | ||
$this->em = $em; | ||
$this->objectMapping = $objectMapping; | ||
} | ||
|
||
private function validateAuthorization(?Request $request = null): void | ||
{ | ||
if ($this->isGranted('ROLE_ADMIN')) { | ||
return; | ||
} | ||
|
||
if ($request instanceof Request) { | ||
$objectType = $request->get('objectType'); | ||
$objectId = $request->get('objectId'); | ||
|
||
if ($objectType && $objectId) { | ||
$object = $this->em->find($this->objectMapping->getClassName($objectType), $objectId); | ||
|
||
if ( | ||
$object instanceof AclObjectInterface | ||
&& $this->isGranted(PermissionInterface::OWNER, $object) | ||
) { | ||
return; | ||
} | ||
} | ||
} | ||
|
||
throw new AccessDeniedHttpException(); | ||
} | ||
|
||
/** | ||
* @Route("/ace", methods={"PUT"}, name="ace") | ||
*/ | ||
public function setAce(Request $request): Response | ||
{ | ||
$this->validateAuthorization($request); | ||
|
||
$objectType = $request->request->get('objectType'); | ||
$objectId = $request->request->get('objectId'); | ||
$userType = $request->request->get('userType'); | ||
$userId = $request->request->get('userId'); | ||
$mask = (int) $request->request->get('mask', 0); | ||
|
||
$objectId = !empty($objectId) ? $objectId : null; | ||
|
||
$this->permissionManager->updateOrCreateAce($userType, $userId, $objectType, $objectId, $mask); | ||
|
||
return new JsonResponse(true); | ||
} | ||
|
||
/** | ||
* @Route("/aces", methods={"GET"}, name="aces_index") | ||
*/ | ||
public function indexAces( | ||
Request $request, | ||
PermissionRepositoryInterface $repository, | ||
AceSerializer $aceSerializer | ||
): Response | ||
{ | ||
$this->validateAuthorization($request); | ||
|
||
$params = [ | ||
'objectType' => $request->query->get('objectType', false), | ||
'objectId' => $request->query->get('objectId', false), | ||
'userType' => $request->query->get('userType', false), | ||
'userId' => $request->query->get('userId', false), | ||
]; | ||
|
||
$params = array_filter($params, function ($entry): bool { | ||
return false !== $entry; | ||
}); | ||
$params = array_map(function ($p): ?string { | ||
return '' === $p || 'null' === $p ? null: $p; | ||
}, $params); | ||
|
||
if (!empty($params['userType'])) { | ||
$params['userType'] = AccessControlEntryInterface::USER_TYPES[$params['userType']] ?? false; | ||
if (false === $params['userType']) { | ||
throw new BadRequestHttpException('Invalid userType'); | ||
} | ||
} | ||
|
||
$aces = $repository->findAces($params); | ||
|
||
return new JsonResponse(array_map(function (AccessControlEntryInterface $ace) use ($aceSerializer): array { | ||
return $aceSerializer->serialize($ace); | ||
}, $aces)); | ||
} | ||
|
||
/** | ||
* @Route("/ace", methods={"DELETE"}, name="ace_delete") | ||
*/ | ||
public function deleteAce(Request $request): Response | ||
{ | ||
$this->validateAuthorization($request); | ||
$objectType = $request->request->get('objectType'); | ||
$objectId = $request->request->get('objectId'); | ||
$userType = $request->request->get('userType'); | ||
$userId = $request->request->get('userId'); | ||
|
||
$objectId = !empty($objectId) ? $objectId : null; | ||
|
||
$this->permissionManager->deleteAce($userType, $userId, $objectType, $objectId); | ||
|
||
return new JsonResponse(true); | ||
} | ||
|
||
/** | ||
* @Route("/users", methods={"GET"}, name="users") | ||
*/ | ||
public function getUsers(Request $request, UserRepositoryInterface $repository): Response | ||
{ | ||
$this->validateAuthorization(); | ||
$limit = $request->query->get('limit'); | ||
$offset = $request->query->get('offset'); | ||
|
||
return new JsonResponse($repository->getUsers($limit, $offset)); | ||
} | ||
|
||
/** | ||
* @Route("/groups", methods={"GET"}, name="groups") | ||
*/ | ||
public function getGroups(Request $request, GroupRepositoryInterface $repository): Response | ||
{ | ||
$this->validateAuthorization(); | ||
$limit = $request->query->get('limit'); | ||
$offset = $request->query->get('offset'); | ||
|
||
return new JsonResponse($repository->getGroups($limit, $offset)); | ||
} | ||
} |
Oops, something went wrong.