Spaces:
No application file
No application file
| namespace Mautic\CoreBundle\Security\Permissions; | |
| use Mautic\UserBundle\Form\Type\PermissionListType; | |
| use Symfony\Component\Form\FormBuilderInterface; | |
| abstract class AbstractPermissions | |
| { | |
| /** | |
| * @var array | |
| */ | |
| protected $permissions = []; | |
| public function __construct( | |
| protected array $params | |
| ) { | |
| } | |
| /** | |
| * This method is called before the permissions object is used. | |
| * Define permissions with `addExtendedPermissions` here instead of constructor. | |
| */ | |
| public function definePermissions(): void | |
| { | |
| // Override this method in the final class | |
| } | |
| /** | |
| * Returns bundle's permissions array. | |
| * | |
| * @return array | |
| */ | |
| public function getPermissions() | |
| { | |
| return $this->permissions; | |
| } | |
| /** | |
| * Checks to see if the requested permission is supported by the bundle. | |
| * | |
| * @param string $name | |
| * @param string $level | |
| * | |
| * @return bool | |
| */ | |
| public function isSupported($name, $level = '') | |
| { | |
| [$name, $level] = $this->getSynonym($name, $level); | |
| if (empty($level)) { | |
| // verify permission name only | |
| return isset($this->permissions[$name]); | |
| } | |
| // verify permission name and level as well | |
| return isset($this->permissions[$name][$level]); | |
| } | |
| /** | |
| * Allows permission classes to be disabled if criteria is not met (such as bundle is disabled). | |
| */ | |
| public function isEnabled(): bool | |
| { | |
| return true; | |
| } | |
| /** | |
| * Returns the value assigned to a specific permission. | |
| * | |
| * @param string $name | |
| * @param string $perm | |
| * | |
| * @return int | |
| */ | |
| public function getValue($name, $perm) | |
| { | |
| return ($this->isSupported($name, $perm)) ? $this->permissions[$name][$perm] : 0; | |
| } | |
| /** | |
| * Builds the bundle's specific form elements for its permissions. | |
| */ | |
| public function buildForm(FormBuilderInterface &$builder, array $options, array $data): void | |
| { | |
| } | |
| /** | |
| * Returns the name of the permission set (should be the bundle identifier). | |
| * | |
| * @return string|void | |
| */ | |
| abstract public function getName(); | |
| /** | |
| * Takes an array from PermissionRepository::getPermissionsByRole() and converts the bitwise integers to an array | |
| * of permission names that can be used in forms, for example. | |
| * | |
| * @return mixed | |
| */ | |
| public function convertBitsToPermissionNames(array $permissions) | |
| { | |
| static $permissionLevels = []; | |
| $bundle = $this->getName(); | |
| if (!in_array($bundle, $permissionLevels)) { | |
| $permissionLevels[$bundle] = []; | |
| if (isset($permissions[$bundle])) { | |
| if ($this->isEnabled()) { | |
| foreach ($permissions[$bundle] as $details) { | |
| $permName = $details['name']; | |
| $permBitwise = $details['bitwise']; | |
| // ensure the permission still exists | |
| if ($this->isSupported($permName)) { | |
| $levels = $this->permissions[$permName]; | |
| // ensure that at least keys exist | |
| $permissionLevels[$bundle][$permName] = []; | |
| // $permissionLevels[$bundle][$permName]["$bundle:$permName"] = $permId; | |
| foreach ($levels as $levelName => $levelBit) { | |
| // compare bit against levels to see if it is a match | |
| if ($levelBit & $permBitwise) { | |
| // bitwise compares so add the level | |
| $permissionLevels[$bundle][$permName][] = $levelName; | |
| continue; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return $permissionLevels[$bundle]; | |
| } | |
| /** | |
| * Allows the bundle permission class to utilize synonyms for permissions. | |
| * | |
| * @param string $name | |
| * @param string $level | |
| * | |
| * @return array | |
| */ | |
| protected function getSynonym($name, $level) | |
| { | |
| if (in_array($level, ['viewown', 'viewother'])) { | |
| if (isset($this->permissions[$name]['view'])) { | |
| $level = 'view'; | |
| } | |
| } elseif ('view' == $level) { | |
| if (isset($this->permissions[$name]['viewown'])) { | |
| $level = 'viewown'; | |
| } | |
| } elseif (in_array($level, ['editown', 'editother'])) { | |
| if (isset($this->permissions[$name]['edit'])) { | |
| $level = 'edit'; | |
| } | |
| } elseif ('edit' == $level) { | |
| if (isset($this->permissions[$name]['editown'])) { | |
| $level = 'editown'; | |
| } | |
| } elseif (in_array($level, ['deleteown', 'deleteother'])) { | |
| if (isset($this->permissions[$name]['delete'])) { | |
| $level = 'delete'; | |
| } | |
| } elseif ('delete' == $level) { | |
| if (isset($this->permissions[$name]['deleteown'])) { | |
| $level = 'deleteown'; | |
| } | |
| } elseif (in_array($level, ['publishown', 'publishother'])) { | |
| if (isset($this->permissions[$name]['publish'])) { | |
| $level = 'publish'; | |
| } | |
| } elseif ('publish' == $level) { | |
| if (isset($this->permissions[$name]['publishown'])) { | |
| $level = 'publishown'; | |
| } | |
| } | |
| return [$name, $level]; | |
| } | |
| /** | |
| * Determines if the user has access to the specified permission. | |
| * | |
| * @param array $userPermissions | |
| * @param string $name | |
| * @param string $level | |
| * | |
| * @return bool | |
| */ | |
| public function isGranted($userPermissions, $name, $level) | |
| { | |
| [$name, $level] = $this->getSynonym($name, $level); | |
| if (!isset($userPermissions[$name])) { | |
| // the user doesn't have implicit access | |
| return false; | |
| } elseif (isset($this->permissions[$name]['full']) && $this->permissions[$name]['full'] & $userPermissions[$name]) { | |
| return true; | |
| } else { | |
| // otherwise test for specific level | |
| $result = ($this->permissions[$name][$level] & $userPermissions[$name]); | |
| return ($result) ? true : false; | |
| } | |
| } | |
| /** | |
| * @param bool $isSecondRound | |
| * | |
| * @return bool Return true if a second round is required after all other bundles have analyzed it's permissions | |
| */ | |
| public function analyzePermissions(array &$permissions, $allPermissions, $isSecondRound = false): bool | |
| { | |
| $hasViewAccess = false; | |
| foreach ($permissions as $level => &$perms) { | |
| foreach ($perms as $perm) { | |
| $required = []; | |
| switch ($perm) { | |
| case 'editother': | |
| case 'edit': | |
| $required = ['viewother', 'viewown']; | |
| break; | |
| case 'deleteother': | |
| case 'delete': | |
| $required = ['editother', 'viewother', 'viewown']; | |
| break; | |
| case 'publishother': | |
| case 'publish': | |
| $required = ['viewother', 'viewown']; | |
| break; | |
| case 'viewother': | |
| case 'editown': | |
| case 'deleteown': | |
| case 'publishown': | |
| case 'create': | |
| $required = ['viewown']; | |
| break; | |
| } | |
| foreach ($required as $r) { | |
| [$ignore, $r] = $this->getSynonym($level, $r); | |
| if ($this->isSupported($level, $r) && !in_array($r, $perms)) { | |
| $perms[] = $r; | |
| } | |
| } | |
| } | |
| $hasViewAccess = (!$hasViewAccess && (in_array('view', $perms) || in_array('viewown', $perms))); | |
| } | |
| // check categories for view permissions and add it if the user has view access to the other permissions | |
| if (isset($this->permissions['categories']) && $hasViewAccess && (!isset($permissions['categories']) || !in_array('view', $permissions['categories']))) { | |
| $permissions['categories'][] = 'view'; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Generates an array of granted and total permissions. | |
| * | |
| * @return array | |
| */ | |
| public function getPermissionRatio(array $data) | |
| { | |
| $totalAvailable = $totalGranted = 0; | |
| foreach ($this->permissions as $level => $perms) { | |
| $perms = array_keys($perms); | |
| $totalAvailable += count($perms); | |
| if (in_array('full', $perms)) { | |
| if (1 === count($perms)) { | |
| // full is the only permission so count as 1 | |
| if (!empty($data[$level]) && !in_array('full', $data[$level])) { | |
| ++$totalGranted; | |
| } | |
| } else { | |
| // remove full from total count | |
| --$totalAvailable; | |
| if (!empty($data[$level]) && in_array('full', $data[$level])) { | |
| // user has full access so sum perms minus full | |
| $totalGranted += count($perms) - 1; | |
| // move on to the next level | |
| continue; | |
| } | |
| } | |
| } | |
| if (isset($data[$level])) { | |
| $totalGranted += count($data[$level]); | |
| } | |
| } | |
| return [$totalGranted, $totalAvailable]; | |
| } | |
| /** | |
| * Gives the bundle an opportunity to change how JavaScript calculates permissions granted. | |
| */ | |
| public function parseForJavascript(array &$perms): void | |
| { | |
| } | |
| /** | |
| * @param array<int|string> $permissions | |
| */ | |
| protected function addCustomPermission(string $level, array $permissions): void | |
| { | |
| $this->permissions[$level] = $permissions; | |
| } | |
| /** | |
| * Adds a custom permission to the form builder, i.e. config only bundles. | |
| * | |
| * @param array<string> $choices | |
| * @param array<string> $data | |
| */ | |
| protected function addCustomFormFields(string $bundle, string $level, FormBuilderInterface &$builder, string $label, array $choices, array $data): void | |
| { | |
| $builder->add("$bundle:$level", PermissionListType::class, [ | |
| 'choices' => $choices, | |
| 'label' => $label, | |
| 'data' => (!empty($data[$level]) ? $data[$level] : []), | |
| 'bundle' => $bundle, | |
| 'level' => $level, | |
| ]); | |
| } | |
| /** | |
| * Adds the standard permission set of view, edit, create, delete, publish and full. | |
| * | |
| * @param array $permissionNames | |
| * @param bool $includePublish | |
| */ | |
| protected function addStandardPermissions($permissionNames, $includePublish = true) | |
| { | |
| if (!is_array($permissionNames)) { | |
| $permissionNames = [$permissionNames]; | |
| } | |
| foreach ($permissionNames as $p) { | |
| $this->permissions[$p] = [ | |
| 'view' => 4, | |
| 'edit' => 16, | |
| 'create' => 32, | |
| 'delete' => 128, | |
| 'full' => 1024, | |
| ]; | |
| if ($includePublish) { | |
| $this->permissions[$p]['publish'] = 512; | |
| } | |
| } | |
| } | |
| /** | |
| * Adds the standard permission set of view, edit, create, delete, publish and full to the form builder. | |
| * | |
| * @param string $bundle | |
| * @param string $level | |
| * @param FormBuilderInterface $builder | |
| * @param array $data | |
| * @param bool $includePublish | |
| */ | |
| protected function addStandardFormFields($bundle, $level, &$builder, $data, $includePublish = true) | |
| { | |
| $choices = [ | |
| 'mautic.core.permissions.view' => 'view', | |
| 'mautic.core.permissions.edit' => 'edit', | |
| 'mautic.core.permissions.create' => 'create', | |
| 'mautic.core.permissions.delete' => 'delete', | |
| ]; | |
| if ($includePublish) { | |
| $choices['mautic.core.permissions.publish'] = 'publish'; | |
| } | |
| $choices['mautic.core.permissions.full'] = 'full'; | |
| $label = ('categories' == $level) ? 'mautic.category.permissions.categories' : "mautic.$bundle.permissions.$level"; | |
| $builder->add( | |
| "$bundle:$level", | |
| PermissionListType::class, | |
| [ | |
| 'choices' => $choices, | |
| 'label' => $label, | |
| 'bundle' => $bundle, | |
| 'level' => $level, | |
| 'data' => (!empty($data[$level]) ? $data[$level] : []), | |
| ] | |
| ); | |
| } | |
| /** | |
| * @param string $bundle | |
| * @param string $level | |
| * | |
| * @return string | |
| */ | |
| protected function getLabel($bundle, $level) | |
| { | |
| return ('categories' === $level) ? 'mautic.category.permissions.categories' : "mautic.{$bundle}.permissions.{$level}"; | |
| } | |
| /** | |
| * Add a single full permission. | |
| * | |
| * @param array $permissionNames | |
| */ | |
| protected function addManagePermission($permissionNames) | |
| { | |
| if (!is_array($permissionNames)) { | |
| $permissionNames = [$permissionNames]; | |
| } | |
| foreach ($permissionNames as $p) { | |
| $this->permissions[$p] = [ | |
| 'manage' => 1024, | |
| ]; | |
| } | |
| } | |
| /** | |
| * Adds a single full permission to the form builder, i.e. config only bundles. | |
| * | |
| * @param string $bundle | |
| * @param string $level | |
| * @param FormBuilderInterface $builder | |
| * @param array $data | |
| */ | |
| protected function addManageFormFields($bundle, $level, &$builder, $data) | |
| { | |
| $choices = [ | |
| 'mautic.core.permissions.manage' => 'manage', | |
| ]; | |
| $builder->add( | |
| "$bundle:$level", | |
| PermissionListType::class, | |
| [ | |
| 'choices' => $choices, | |
| 'label' => "mautic.$bundle.permissions.$level", | |
| 'data' => (!empty($data[$level]) ? $data[$level] : []), | |
| 'bundle' => $bundle, | |
| 'level' => $level, | |
| ] | |
| ); | |
| } | |
| /** | |
| * Adds the standard permission set of viewown, viewother, editown, editother, create, deleteown, deleteother, | |
| * publishown, publishother and full. | |
| * | |
| * @param array|string $permissionNames | |
| * @param bool $includePublish | |
| */ | |
| protected function addExtendedPermissions($permissionNames, $includePublish = true) | |
| { | |
| if (!is_array($permissionNames)) { | |
| $permissionNames = [$permissionNames]; | |
| } | |
| foreach ($permissionNames as $p) { | |
| $this->permissions[$p] = [ | |
| 'viewown' => 2, | |
| 'viewother' => 4, | |
| 'editown' => 8, | |
| 'editother' => 16, | |
| 'create' => 32, | |
| 'deleteown' => 64, | |
| 'deleteother' => 128, | |
| 'full' => 1024, | |
| ]; | |
| if ($includePublish) { | |
| $this->permissions[$p]['publishown'] = 256; | |
| $this->permissions[$p]['publishother'] = 512; | |
| } | |
| } | |
| } | |
| /** | |
| * Adds the standard permission set of viewown, viewother, editown, editother, create, deleteown, deleteother, | |
| * publishown, publishother and full to the form builder. | |
| * | |
| * @param string $bundle | |
| * @param string $level | |
| * @param FormBuilderInterface $builder | |
| * @param array $data | |
| * @param bool $includePublish | |
| */ | |
| protected function addExtendedFormFields($bundle, $level, &$builder, $data, $includePublish = true) | |
| { | |
| $choices = [ | |
| 'mautic.core.permissions.viewown' => 'viewown', | |
| 'mautic.core.permissions.viewother' => 'viewother', | |
| 'mautic.core.permissions.editown' => 'editown', | |
| 'mautic.core.permissions.editother' => 'editother', | |
| 'mautic.core.permissions.create' => 'create', | |
| 'mautic.core.permissions.deleteown' => 'deleteown', | |
| 'mautic.core.permissions.deleteother' => 'deleteother', | |
| 'mautic.core.permissions.full' => 'full', | |
| ]; | |
| if ($includePublish) { | |
| $choices['mautic.core.permissions.publishown'] = 'publishown'; | |
| $choices['mautic.core.permissions.publishother'] = 'publishother'; | |
| } | |
| $builder->add( | |
| "$bundle:$level", | |
| PermissionListType::class, | |
| [ | |
| 'choices' => $choices, | |
| 'choices_as_values' => true, | |
| 'label' => $this->getLabel($bundle, $level), | |
| 'data' => (!empty($data[$level]) ? $data[$level] : []), | |
| 'bundle' => $bundle, | |
| 'level' => $level, | |
| ] | |
| ); | |
| } | |
| } | |