refactored permissions for simpler customizations (#808)
This commit is contained in:
@@ -11,10 +11,6 @@ namespace App\Controller;
|
||||
|
||||
use App\Constants;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
@@ -43,11 +39,6 @@ class AboutController extends AbstractController
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function debugAction()
|
||||
{
|
||||
return $this->getAboutView();
|
||||
}
|
||||
|
||||
protected function getAboutView(array $additional = [])
|
||||
{
|
||||
$phpInfo = $this->getPhpInfo();
|
||||
unset($phpInfo[0]);
|
||||
@@ -89,8 +80,7 @@ class AboutController extends AbstractController
|
||||
],
|
||||
'info' => $phpInfo,
|
||||
'settings' => $settings,
|
||||
],
|
||||
$additional
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
@@ -119,28 +109,6 @@ class AboutController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/flush-cache", name="system_flush_cache", methods={"GET"})
|
||||
*
|
||||
* @Security("is_granted('system_actions')")
|
||||
*/
|
||||
public function rebuildContainer(KernelInterface $kernel)
|
||||
{
|
||||
$application = new Application($kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
$input = new ArrayInput([
|
||||
'command' => 'cache:clear',
|
||||
'--env' => $kernel->getEnvironment(),
|
||||
'-n',
|
||||
]);
|
||||
|
||||
$output = new BufferedOutput();
|
||||
$application->run($input, $output);
|
||||
|
||||
return $this->getAboutView(['content_action' => $output->fetch()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author https://php.net/manual/en/function.phpinfo.php#117961
|
||||
* @return array
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Entity\User;
|
||||
use App\Form\Toolbar\UserToolbarForm;
|
||||
use App\Form\UserCreateType;
|
||||
use App\Repository\Query\UserQuery;
|
||||
use App\Security\RolePermissionManager;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -173,6 +174,22 @@ class UserController extends AbstractController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/permissions", name="admin_user_permissions", methods={"GET", "POST"})
|
||||
* @Security("is_granted('role_permissions')")
|
||||
*
|
||||
* @param RolePermissionManager $manager
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function permissions(RolePermissionManager $manager)
|
||||
{
|
||||
return $this->render('user/permissions.html.twig', [
|
||||
'roles' => $manager->getRoles(),
|
||||
'permissions' => $manager->getPermissions(),
|
||||
'manager' => $manager,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserQuery $query
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
|
||||
@@ -71,14 +71,8 @@ class AppExtension extends Extension
|
||||
*/
|
||||
protected function createPermissionParameter(array $config, ContainerBuilder $container)
|
||||
{
|
||||
$roles = [];
|
||||
foreach ($config['maps'] as $role => $sets) {
|
||||
if (!isset($config['roles'][$role])) {
|
||||
$exception = new InvalidConfigurationException(
|
||||
'Configured permission set includes unknown role "' . $role . '"'
|
||||
);
|
||||
$exception->setPath('kimai.permissions.maps.' . $role);
|
||||
throw $exception;
|
||||
}
|
||||
foreach ($sets as $set) {
|
||||
if (!isset($config['sets'][$set])) {
|
||||
$exception = new InvalidConfigurationException(
|
||||
@@ -87,12 +81,59 @@ class AppExtension extends Extension
|
||||
$exception->setPath('kimai.permissions.maps.' . $role);
|
||||
throw $exception;
|
||||
}
|
||||
$config['roles'][$role] = array_unique(array_merge($config['roles'][$role], $config['sets'][$set]));
|
||||
$roles[$role] = array_merge($roles[$role] ?? [], $this->getFilteredPermissions(
|
||||
$this->extractSinglePermissionsFromSet($config, $set)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// delete forbidden permissions from roles
|
||||
foreach (array_keys($config['maps']) as $name) {
|
||||
$config['roles'][$name] = $this->getFilteredPermissions(
|
||||
array_unique(array_merge($roles[$name], $config['roles'][$name] ?? []))
|
||||
);
|
||||
}
|
||||
|
||||
$container->setParameter('kimai.permissions', $config['roles']);
|
||||
}
|
||||
|
||||
protected function getFilteredPermissions(array $permissions): array
|
||||
{
|
||||
$deleteFromArray = array_filter($permissions, function ($permission) {
|
||||
return $permission[0] == '!';
|
||||
});
|
||||
|
||||
return array_filter($permissions, function ($permission) use ($deleteFromArray) {
|
||||
if ($permission[0] == '!') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !in_array('!' . $permission, $deleteFromArray);
|
||||
});
|
||||
}
|
||||
|
||||
protected function extractSinglePermissionsFromSet(array $permissions, string $name): array
|
||||
{
|
||||
if (!isset($permissions['sets'][$name])) {
|
||||
throw new InvalidConfigurationException('Unknown permission set "' . $name . '"');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($permissions['sets'][$name] as $permissionName) {
|
||||
if ($permissionName[0] == '@') {
|
||||
$result = array_merge(
|
||||
$result,
|
||||
$this->extractSinglePermissionsFromSet($permissions, substr($permissionName, 1))
|
||||
);
|
||||
} else {
|
||||
$result[] = $permissionName;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
|
||||
@@ -434,7 +434,6 @@ class Configuration implements ConfigurationInterface
|
||||
->arrayNode('sets')
|
||||
->requiresAtLeastOneElement()
|
||||
->useAttributeAsKey('key')
|
||||
->performNoDeepMerging()
|
||||
->arrayPrototype()
|
||||
->useAttributeAsKey('key')
|
||||
->isRequired()
|
||||
@@ -445,7 +444,6 @@ class Configuration implements ConfigurationInterface
|
||||
->arrayNode('maps')
|
||||
->requiresAtLeastOneElement()
|
||||
->useAttributeAsKey('key')
|
||||
->performNoDeepMerging()
|
||||
->arrayPrototype()
|
||||
->useAttributeAsKey('key')
|
||||
->isRequired()
|
||||
@@ -456,13 +454,18 @@ class Configuration implements ConfigurationInterface
|
||||
->arrayNode('roles')
|
||||
->requiresAtLeastOneElement()
|
||||
->useAttributeAsKey('key')
|
||||
->performNoDeepMerging()
|
||||
->arrayPrototype()
|
||||
->useAttributeAsKey('key')
|
||||
->isRequired()
|
||||
->prototype('scalar')->end()
|
||||
->defaultValue([])
|
||||
->end()
|
||||
->defaultValue([
|
||||
'ROLE_USER' => [],
|
||||
'ROLE_TEAMLEAD' => [],
|
||||
'ROLE_ADMIN' => [],
|
||||
'ROLE_SUPER_ADMIN' => [],
|
||||
])
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||
@@ -55,7 +56,7 @@ class UserCreateType extends UserEditType
|
||||
public function __configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => 'Kimai:User',
|
||||
'class' => User::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,10 @@ class RolePermissionManager
|
||||
*/
|
||||
protected $permissions = [];
|
||||
/**
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
protected $knownPermissions = [];
|
||||
|
||||
/**
|
||||
* @param array $permissions
|
||||
*/
|
||||
public function __construct(array $permissions)
|
||||
{
|
||||
$this->permissions = $permissions;
|
||||
@@ -33,30 +30,12 @@ class RolePermissionManager
|
||||
$this->knownPermissions = array_unique($this->knownPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function isRegisteredPermission($permission)
|
||||
public function isRegisteredPermission(string $permission): bool
|
||||
{
|
||||
return in_array($permission, $this->knownPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @return bool
|
||||
*/
|
||||
public function roleHasPermission($role)
|
||||
{
|
||||
return isset($this->permissions[$role]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPermission($role, $permission)
|
||||
public function hasPermission(string $role, string $permission): bool
|
||||
{
|
||||
if (!isset($this->permissions[$role])) {
|
||||
return false;
|
||||
@@ -64,4 +43,19 @@ class RolePermissionManager
|
||||
|
||||
return in_array($permission, $this->permissions[$role]);
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return array_keys($this->permissions);
|
||||
}
|
||||
|
||||
public function roleHasPermission(string $role): bool
|
||||
{
|
||||
return isset($this->permissions[$role]);
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return $this->knownPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ class Extensions extends AbstractExtension
|
||||
'profile-stats' => 'far fa-chart-bar',
|
||||
'profile' => 'fas fa-user-edit',
|
||||
'warning' => 'fas fa-exclamation-triangle',
|
||||
'permissions' => 'fas fa-user-lock',
|
||||
'back' => 'fas fa-long-arrow-alt-left',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user