diff --git a/docs/plugins/permissions.rst b/docs/plugins/permissions.rst index 964608ee..47927a32 100644 --- a/docs/plugins/permissions.rst +++ b/docs/plugins/permissions.rst @@ -1,14 +1,295 @@ Roles and permissions ##################### -.. vale off +Mautic defines custom Role permissions through Permission objects. + +How permissions work +******************** + +Mautic calculates permissions based on bits assigned to a Plugin level and permission. Bits are integers that increase by doubling the value - 1, 2, 4, 8, 16, 32, 64, 128, 512, 1024, and so forth. Avoid assigning numbers in between, such as 3 or 5, because the permission won't calculate correctly. + +For example, if ``HelloWorldBundle`` manages access to a ``worlds`` entity, the permission set for ``plugin:helloWorld:worlds`` resembles this setup: + +.. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Permission + - Bit + * - view + - 1 + * - edit + - 2 + * - create + - 4 + * - delete + - 8 + * - full + - 16 + +.. note:: + + The notation ``plugin:helloWorld:worlds:view`` typically requests permission in Mautic. This notation tells Mautic to verify the ``view`` permission for the Plugin ``HelloWorldBundle`` at the ``worlds`` level. Levels allow Plugins to set permissions for multiple areas. + +Mautic sums the bits for the permissions granted to a Role and stores the result in the database. For example, if a Role has ``view`` and ``edit`` access, the stored bit is 3. If given ``view`` and ``create`` access, the stored bit is 5. + +When permission verification is necessary - for instance ``plugin:helloWorld:worlds:create`` - Mautic verifies if the Role's generated bit for ``plugin:helloWorld:worlds`` includes bit 4. If so, Mautic grants permission. .. note:: - The content for this page requires a major update. The legacy page contains outdated and potentially inaccurate information. You can still access it in the :xref:`legacy repository`. + The ``full`` permission grants access to all prior permissions and should always be the highest bit. + +Using permissions +***************** + +.. code-block:: php + + get('mautic.security'); + + // Check if user is granted a single permission + if ($security->isGranted('plugin:helloWorld:worlds:view')) { + // do something + } + + // Check if user is granted multiple permissions (must be granted to all to be true) + if ($security->isGranted( + array( + 'plugin:helloWorld:worlds:view', + 'plugin:helloWorld:worlds:create', + ) + )) { + // do something + } + + // Check if user is granted to at least one permission + if ($security->isGranted( + array( + 'plugin:helloWorld:worlds:view', + 'plugin:helloWorld:worlds:edit', + ), + 'MATCH_ONE' + )) { + // do something + } + + // Get an array of user permissions + $permissions = $security->isGranted( + array( + 'plugin:helloWorld:worlds:view', + 'plugin:helloWorld:worlds:edit', + ), + 'RETURN_ARRAY' + ); + + if ($permissions['plugin:helloWorld:worlds:view']) { + // do something + } + + // Check if user has access to view leads + if ($security->isGranted('lead:leads:viewother')) { + // do something + } + +To determine if a User has a specific permission, use the Mautic security service, which you can obtain from the ``mautic.security`` service. + +Mautic uses specific notation to identify permissions: + +* **Core bundles**: use the format ``bundleName:permissionLevel:permission``. +* **Plugins**: append the ``plugin:`` prefix. For example, ``plugin:bundleName:permissionLevel:permission``. Plugins require this prefix because it directs Mautic to search for the permission class within the ``plugins/`` directory and the ``MauticPlugin`` namespace. + +Core bundles or Plugins set the permission level and permissions. For example, the core UserBundle has ``users`` and ``roles`` levels with ``view``, ``edit``, ``create``, ``delete``, and ``full`` permissions for each. To verify if a User has permission to edit Roles, use: + +.. code-block:: php + + $security->isGranted('user:roles:edit'); + +Creating custom permissions +*************************** + +A Plugin creates its own set of permissions by defining a Permission class. Each permission class must extend ``Mautic\CoreBundle\Security\Permissions\AbstractPermissions``. + +.. code-block:: php + + permissions = array( + 'worlds' => array( + 'use_telescope' => 1, + 'send_probe' => 2, + 'visit' => 4, + 'full' => 1024 + ) + ); + + // Add standard category permissions + $this->addStandardPermissions('categories'); + } + + public function buildForm(FormBuilderInterface &$builder, array $options, array $data) + { + // Add standard category form fields + $this->addStandardFormFields('helloWorld', 'categories', $builder, $data); + + // Add custom 'worlds' level form fields + $builder->add( + 'helloWorld:worlds', + 'permissionlist', + array( + 'choices' => array( + 'use_telescope' => 'plugin.helloworld.permissions.use_telescope', + 'send_probe' => 'plugin.helloworld.permissions.send_probe', + 'visit' => 'plugin.helloworld.permissions.visit', + 'full' => 'mautic.core.permissions.full', + ), + 'label' => 'plugin.helloworld.permissions', + 'data' => (!empty($data['worlds']) ? $data['worlds'] : array()), + 'bundle' => 'helloWorld', + 'level' => 'worlds' + ) + ); + } + + public function getName() + { + return 'helloWorld'; + } + } + +Most permission classes require three methods: ``__construct()``, ``buildForm()``, and ``getName()``. + +``__construct()`` +================= + +The constructor performs two tasks. It calls ``parent::__construct($params)`` or sets ``$this->params = $params;``. It then defines the ``$this->permissions`` array. This array organizes permissions into levels, where each level contains specific permissions assigned to bits. + +For example, in the code sample, a custom level of ``worlds`` includes ``use_telescope``, ``send_probe``, ``visit``, and ``full``. To verify if a User has the ``send_probe`` permission for the ``worlds`` level, use: + +.. code-block:: php + + $mauticSecurity->isGranted('plugin:helloWorld:worlds:send_probe'); + +Mautic provides helper methods for common permission sets: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Method + - Description + * - ``addStandardPermissions()`` + - Sets ``view``, ``edit``, ``create``, ``delete``, ``publish`` - with an option to exclude - and ``full`` permissions. + * - ``addExtendedPermissions()`` + - Sets creator-level restrictions - ``viewown``, ``viewother``, ``editown``, ``editother``, ``create``, ``deleteown``, ``deleteother``, ``publishown``, ``publishother``, and ``full``. + * - ``addManagePermission()`` + - Sets a single ``manage`` permission. + +``buildForm()`` +=============== + +.. vale off + +The ``buildForm()`` method appends permission toggles to the Role's Form. See :doc:`/plugin_extensions/forms` for details on Form builders and review the comments in the code sample for implementation details. + +.. vale on + +Mautic provides complementary helper methods for common permission sets: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Method + - Description + * - ``addStandardFormFields()`` + - Appends the standard permission sets to the Form. + * - ``addExtendedFormFields()`` + - Appends the extended - creator restricted - permissions to the Form. + * - ``addManageFormFields()`` + - Appends the single manager element to the Form. + +``getName()`` +============= + +This method is mandatory. The return value must match the ``bundleName`` and the filename. For example, if the bundle name is ``HelloWorldBundle``, this method returns ``helloWorld`` and the file is ``HelloWorldPermissions.php``. + +Permission aliases +****************** + +.. code-block:: php + + permissions`` property for that level doesn't define ``editown``. + +Manipulating permissions before saving +************************************** + +.. code-block:: php + + &$perms) { + foreach ($perms as $perm) { + $include = array(); + switch ($perm) { + case 'send_probe': + $include = array('use_telescope'); + break; + case 'visit': + $include = array('use_telescope', 'send_probe'); + break; + } + if (!empty($include)) { + foreach ($include as $r) { + list($ignore, $r) = $this->getSynonym($level, $r); + if ($this->isSupported($level, $r) && !in_array($r, $perms)) { + $perms[] = $r; + } + } + } + } + } + + return false; + } + +Plugins can adjust permissions based on other selections to prevent User error. For example, if a User has ``edit`` permission, they also require ``view`` permission. Use the ``analyzePermissions()`` method to modify permissions before Mautic persists them to the database. + +To re-adjust permissions based on factors outside the Plugin's control, return ``true`` from ``analyzePermissions()``. This triggers a second round of analysis after all other bundles and Plugins finish their processing. During this round, the ``$isSecondRound`` argument is ``true``. + +Advanced ``isGranted()`` logic +============================== - If you're interested in helping develop the new content for this page and others, consider joining the documentation efforts. +Override the parent method - ``public function isGranted($userPermissions, $name, $level)`` - to run custom logic rather than simple bit comparisons. Use this to define unique behavior for the class's own levels and individual permissions. - Please read the :xref:`dev docs contributing guidelines` and :xref:`Contributing to Mautic’s documentation` to get started. +Advanced ``isSupported()`` logic +================================ -.. vale on \ No newline at end of file +The same rules apply to the ``isSupported()`` method. This method determines if a bundle or Plugin includes the requested permission and permission level. Use this to provide backward compatibility - BC - support. \ No newline at end of file