Added various useful presets for permissions

This commit is contained in:
Jan Böhmer 2022-11-14 00:02:37 +01:00
parent 5829d42968
commit 1de88e0494
13 changed files with 369 additions and 231 deletions

View file

@ -51,6 +51,7 @@ use App\Form\AdminPages\GroupAdminForm;
use App\Services\EntityExporter;
use App\Services\EntityImporter;
use App\Services\StructuralElementRecursionHelper;
use App\Services\UserSystem\PermissionPresetsHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
@ -73,8 +74,27 @@ class GroupController extends BaseAdminController
* @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="group_edit")
* @Route("/{id}/", requirements={"id"="\d+"})
*/
public function edit(Group $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
public function edit(Group $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, ?string $timestamp = null): Response
{
//Handle permissions presets
if ($request->request->has('permission_preset')) {
$this->denyAccessUnlessGranted('edit_permissions', $entity);
if ($this->isCsrfTokenValid('group'.$entity->getId(), $request->request->get('_token'))) {
$preset = $request->request->get('permission_preset');
$permissionPresetsHelper->applyPreset($entity, $preset);
$em->flush();
$this->addFlash('success', 'user.edit.permission_success');
//We need to stop the execution here, or our permissions changes will be overwritten by the form values
return $this->redirectToRoute('group_edit', ['id' => $entity->getID()]);
} else {
$this->addFlash('danger', 'csfr_invalid');
}
}
return $this->_edit($entity, $request, $em, $timestamp);
}

View file

@ -54,6 +54,7 @@ use App\Form\UserAdminForm;
use App\Services\EntityExporter;
use App\Services\EntityImporter;
use App\Services\StructuralElementRecursionHelper;
use App\Services\UserSystem\PermissionPresetsHelper;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use InvalidArgumentException;
@ -101,7 +102,7 @@ class UserController extends AdminPages\BaseAdminController
*
* @throws Exception
*/
public function edit(User $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, ?string $timestamp = null): Response
{
//Handle 2FA disabling
@ -132,6 +133,25 @@ class UserController extends AdminPages\BaseAdminController
}
}
//Handle permissions presets
if ($request->request->has('permission_preset')) {
$this->denyAccessUnlessGranted('edit_permissions', $entity);
if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) {
$preset = $request->request->get('permission_preset');
$permissionPresetsHelper->applyPreset($entity, $preset);
$em->flush();
$this->addFlash('success', 'user.edit.permission_success');
//We need to stop the execution here, or our permissions changes will be overwritten by the form values
return $this->redirectToRoute('user_edit', ['id' => $entity->getID()]);
} else {
$this->addFlash('danger', 'csfr_invalid');
}
}
return $this->_edit($entity, $request, $em, $timestamp);
}

View file

@ -88,6 +88,16 @@ final class PermissionData implements \JsonSerializable
return $this;
}
/**
* Resets the saved permissions and set all operations to inherit (which means they are not defined).
* @return $this
*/
public function resetPermissions(): self
{
$this->data = [];
return $this;
}
/**
* Creates a new Permission Data Instance using the given JSON encoded data
* @param string $json

View file

@ -64,6 +64,7 @@ class GroupAdminForm extends BaseEntityAdminForm
'mapped' => false,
'data' => $builder->getData(),
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
'show_presets' => $this->security->isGranted('edit_permissions', $entity) && !$is_new,
]);
}
}

View file

@ -66,6 +66,7 @@ class PermissionsType extends AbstractType
{
$resolver->setDefaults([
'show_legend' => true,
'show_presets' => false,
'constraints' => static function (Options $options) {
if (!$options['disabled']) {
return [new NoLockout()];
@ -80,6 +81,7 @@ class PermissionsType extends AbstractType
public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['show_legend'] = $options['show_legend'];
$view->vars['show_presets'] = $options['show_presets'];
}
public function buildForm(FormBuilderInterface $builder, array $options): void

View file

@ -227,6 +227,7 @@ class UserAdminForm extends AbstractType
'mapped' => false,
'data' => $builder->getData(),
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
'show_presets' => $this->security->isGranted('edit_permissions', $entity) && !$is_new,
])
;
/*->add('comment', CKEditorType::class, ['required' => false,

View file

@ -206,7 +206,7 @@ class PermissionResolver
/**
* This functions sets all operations mentioned in the alsoSet value of a permission, so that the structure is always valid.
* @param User $user
* @param HasPermissionsInterface $user
* @return void
*/
public function ensureCorrectSetOperations(HasPermissionsInterface $user): void
@ -261,6 +261,26 @@ class PermissionResolver
}
}
/**
* Sets all operations of the given permissions to the given value.
* Please note that you have to call ensureCorrectSetOperations() after this function, to ensure that all alsoSet values are set.
*
* @param HasPermissionsInterface $perm_holder
* @param string $permission
* @param bool|null $new_value
* @return void
*/
public function setAllOperationsOfPermission(HasPermissionsInterface $perm_holder, string $permission, ?bool $new_value): void
{
if (!$this->isValidPermission($permission)) {
throw new InvalidArgumentException(sprintf('A permission with that name is not existing! Got %s.', $permission));
}
foreach ($this->permission_structure['perms'][$permission]['operations'] as $op_key => $op) {
$this->setPermission($perm_holder, $permission, $op_key, $new_value);
}
}
protected function generatePermissionStructure()
{
$cache = new ConfigCache($this->cache_file, $this->is_debug);

View file

@ -0,0 +1,143 @@
<?php
namespace App\Services\UserSystem;
use App\Entity\UserSystem\PermissionData;
use App\Entity\UserSystem\User;
use App\Security\Interfaces\HasPermissionsInterface;
use App\Services\PermissionResolver;
class PermissionPresetsHelper
{
private PermissionResolver $permissionResolver;
public function __construct(PermissionResolver $permissionResolver)
{
$this->permissionResolver = $permissionResolver;
}
/**
* Apply the given preset to the permission holding entity (like a user)
* The permission data will be reset during the process and then the preset will be applied.
*
* @param
* @param string $preset_name The name of the preset to use
* @return PermissionData
*/
public function applyPreset(HasPermissionsInterface $perm_holder, string $preset_name): HasPermissionsInterface
{
//We need to reset the permission data first (afterwards all values are inherit)
$perm_holder->getPermissions()->resetPermissions();
switch($preset_name) {
case 'all_inherit':
//Do nothing, all values are inherit after reset
break;
case 'all_forbid':
$this->allForbid($perm_holder);
break;
case 'all_allow':
$this->allAllow($perm_holder);
break;
case 'read_only':
$this->readOnly($perm_holder);
break;
case 'editor':
$this->editor($perm_holder);
break;
case 'admin':
$this->admin($perm_holder);
break;
default:
throw new \InvalidArgumentException('Unknown permission preset name: '.$preset_name);
}
//Ensure that permissions are valid (alsoSet values are set), this allows us to use the permission inheritance system to keep the presets short
$this->permissionResolver->ensureCorrectSetOperations($perm_holder);
return $perm_holder;
}
private function admin(HasPermissionsInterface $perm_holder): void
{
//Apply everything from editor permission
$this->editor($perm_holder);
//Allow user and group access
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'users', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'groups', PermissionData::ALLOW);
//Allow access to system log and server infos
$this->permissionResolver->setPermission($perm_holder, 'system', 'show_logs', PermissionData::ALLOW);
$this->permissionResolver->setPermission($perm_holder, 'system', 'server_infos', PermissionData::ALLOW);
}
private function editor(HasPermissionsInterface $permHolder): HasPermissionsInterface
{
//Apply everything from read-only
$this->readOnly($permHolder);
//Set datastructures
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'parts', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'categories', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'storelocations', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'footprints', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'manufacturers', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'attachment_types', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'currencies', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'measurement_units', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'suppliers', PermissionData::ALLOW);
//Attachments permissions
$this->permissionResolver->setPermission($permHolder, 'attachments', 'show_private', PermissionData::ALLOW);
//Labels permissions (allow all except use twig)
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'labels', PermissionData::ALLOW);
$this->permissionResolver->setPermission($permHolder,'labels', 'use_twig', PermissionData::INHERIT);
//Self permissions
$this->permissionResolver->setPermission($permHolder, 'self', 'edit_infos', PermissionData::ALLOW);
//Various other permissions
$this->permissionResolver->setPermission($permHolder, 'tools', 'lastActivity', PermissionData::ALLOW);
return $permHolder;
}
private function readOnly(HasPermissionsInterface $perm_holder): HasPermissionsInterface
{
//It is sufficient to only set the read operation to allow, read operations for datastructures are inherited
$this->permissionResolver->setPermission($perm_holder, 'parts', 'read', PermissionData::ALLOW);
//Set tools permissions
$this->permissionResolver->setPermission($perm_holder, 'tools', 'statistics', PermissionData::ALLOW);
$this->permissionResolver->setPermission($perm_holder, 'tools', 'label_scanner', PermissionData::ALLOW);
$this->permissionResolver->setPermission($perm_holder, 'tools', 'reel_calculator', PermissionData::ALLOW);
//Set attachments permissions
$this->permissionResolver->setPermission($perm_holder, 'attachments', 'list_attachments', PermissionData::ALLOW);
//Set user (self) permissions
$this->permissionResolver->setPermission($perm_holder, 'self', 'show_permissions', PermissionData::ALLOW);
//Label permissions
$this->permissionResolver->setPermission($perm_holder, 'labels', 'create_labels', PermissionData::ALLOW);
$this->permissionResolver->setPermission($perm_holder, 'labels', 'edit_options', PermissionData::ALLOW);
return $perm_holder;
}
private function AllForbid(HasPermissionsInterface $perm_holder): HasPermissionsInterface
{
$this->permissionResolver->setAllPermissions($perm_holder, PermissionData::DISALLOW);
return $perm_holder;
}
private function AllAllow(HasPermissionsInterface $perm_holder): HasPermissionsInterface
{
$this->permissionResolver->setAllPermissions($perm_holder, PermissionData::ALLOW);
return $perm_holder;
}
}