Security

Mautic 提供了一种通过权限对象为角色定义自定义权限的机制。

权限的工作方式

权限是根据分配给插件级别的位计算得出的。 位是整数,其值以双倍递增。 1、2、4、8、16、32、64、128、512、1024 等等。 位的值不应出现中间数字,例如 3 或 5,因为在这种情况下,权限将无法正确计算。

例如,假设 HelloWorldBundle 需要管理用户 Worlds 实体的访问权限。 plugin:helloWorld:worlds 的权限集可能如下所示:

Permission

Bit

view

1

edit

2

create

4

delete

8

full

16

Note

plugin:helloWorld:worlds:view 是 Mautic 中请求权限的常用表示法。 该表示法告诉 Mautic 验证插件、HelloWorldBundle,在 worlds 层级的 view 权限。 层级允许插件为多个区域设置权限。

Mautic 会对分配给角色的权限计算出位的总和,并将结果存储在数据库中。 例如,如果一个角色具有查看和编辑访问权限,则存储的位是 3。 如果它具有查看和创建访问权限,则存储的位是 5。

当验证权限时,例如 plugin:helloWorld:worlds:create,Mautic 会检查角色的生成的位是否至少为 4,用于 plugin:helloWorld:worlds。 如果是,则授予该权限。

Note

full 权限授予访问级别内所有先前权限的权限,因此始终应该是最高的位。

使用权限

您可以在控制器和服务中使用以下方式:

<?php
declare(strict_types=1);

/** @var \Mautic\CoreBundle\Security\Permissions\CorePermissions */
$security = $this->get('mautic.security');

// 检查用户是否具有单个权限
if ($security->isGranted('plugin:helloWorld:worlds:view')) {
    // do something
}

// 检查用户是否具有多个权限(必须授予所有才能为真)
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:create',
    )
)
) {
    //do something
}

// 检查用户是否至少具有一个权限
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:edit',
    ),
    'MATCH_ONE'
)
) {
    //do something
}

// 获取用户权限的数组
$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 Mautic’s security service which has the mautic.security alias. The class is Mautic\CoreBundle\Security\Permissions\CorePermissions.

As suggested in the preceding section, Mautic uses a special permission notation to refer to a specific permission. For core bundles, use bundleName:permissionLevel:permission. For Plugins, append plugin:, for example plugin:bundleName:permissionLevel:permission. plugin: tells Mautic to look for the permission class in the plugins/ directory and the MauticPlugin namespace.

Mautic’s core bundles and external Plugins are responsible for setting the permission level and permissions. For example, Mautic’s core UserBundle has users and roles levels with view, edit, create, delete and full permissions for each.

Note

To validate whether a User has permissions to edit Roles, use $mauticSecurity->isGranted('user:roles:edit');

Creating custom permissions

It’s possible to define your own custom permissions within your Plugin. Make sure to extend Mautic\CoreBundle\Security\Permissions\AbstractPermissions like in the following example:

<?php
// plugins/HelloWorldBundle/Security/Permissions/HelloWorldPermissions.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Security\Permissions;

use Symfony\Component\Form\FormBuilderInterface;
use Mautic\CoreBundle\Security\Permissions\AbstractPermissions;

class HelloWorldPermissions extends AbstractPermissions
{
    public function __construct($params)
    {
        parent::__construct($params);

        $this->permissions = array(

            // Custom level
            'worlds' => array(

                // Custom permissions
                'use_telescope' => 1,
                'send_probe'    => 2,
                'visit'         => 4,
                // Full will almost always be included and should be significantly higher than the
                // others in case new permissions need to be added later
                'full'          => 1024
            )
        );

        // Add standard category permissions
        $this->addStandardPermissions('categories');
    }

    /**
     * Append the permission form fields to the Role form
     *
     * @param FormBuilderInterface $builder
     * @param array                $options
     * @param array                $data
     */
    public function buildForm(FormBuilderInterface &$builder, array $options, array $data)
    {
        // Add standard category form fields
        $this->addStandardFormFields('helloWorld', 'categories', $builder, $data);
// 添加自定义的 ‘worlds’ 级别表单字段

$builder->add(

// 表单元素名称应为 bundleName:permissionLevel ‘helloWorld:worlds’,

// 始终应该是 permissionlist 类型 ‘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 名称(用于构建前端表单) ‘bundle’ => ‘helloWorld’,

// 权限级别(用于构建前端表单) ‘level’ => ‘worlds’

)

);

}

/**
  • 权限集标识符;应为 bundleName

  • @return string

*/

public function getName() {

return ‘helloWorld’;

}

}

您可以将权限类注册到您的 config.php 文件中,如下所示。请确保将其放在 services.permissions 组中,以便 Mautic 可以正确地识别它。

<?php
// plugins/HelloWorldBundle/Config/config.php

return [
    ...
    'services' => [
        'permissions' => [
            'marketplace.permissions' => [
                'class' => \MauticPlugin\HelloWorldBundle\Security\Permissions\WorldsPermissions::class,
            ],
        ],
    ],
];

您可以通过查看 Mautic\CoreBundle\Security\Permissions\AbstractPermissions 的 PHPDoc 来了解更多关于可用选项的信息,但以下是最重要的信息:

__construct()

构造方法应该执行两个操作。它应该调用 parent::__construct($params) 并且/或者它应该设置 $this->params = $params;.

然后,它应该定义 $this->permissions$this->permissions 是一个权限级别的数组,每个级别都是一个包含分配给位的权限的数组。 例如,在代码块中,自定义权限级别 worlds 被定义为具有 use_telescopesend_probevisitfull 权限。 要验证用户是否对级别 worlds 和权限 send_probe 拥有权限,可以使用 $mauticSecurity->isGranted('plugin:helloWorld:worlds:send_probe')

Mautic 提供了一些用于常见权限集的辅助方法:

    • Method
      • Description

      • addStandardPermissions()

      • Set view, edit, create, delete, publish - with option to exclude, and full permissions.

      • addExtendedPermissions()

      • Set creator level restrictions: viewown, viewother, editown, editother, create, deleteown, deleteother, publishown - with option to exclude, publishother - with option to exclude, and full

      • addManagePermission()

      • Add a single manage permission, which is the same as full. Use this in cases where you only need a single permission for everything, also known as an “all or nothing” approach.

buildForm()

The buildForm() method appends the permission toggles to the Role’s Form. See components/forms:Forms for details on Form builders. Review the comments in the code sample.

There are complimentary helper methods for the common permission sets:

Method

Description

addStandardFormFields()

Appends the standard permission sets to the Form

addExtendedFormFields()

Appends the extended, aka creator restricted, permissions to the Form

addManageFormFields()

Appends the single manager element to the Form

getName()

This method is absolutely required and should match both the bundleName and the name of the file. For example, if HelloWorldBundle is the bundle’s name, then this would be helloWorld with a filename of HelloWorldPermissions.php.

Permission aliases

<?php

 protected function getSynonym($name, $level)
 {
     if ($name == 'send_satellite') {
         // Set real permission name
        $name = 'send_probe';
     }

     return array($name, $level);
 }

To add a permission alias, use the getSynonym() method. Basically this method gets called before each requested permission gets determined, giving opportunity to change the permission level or name as needed.

For example, parent::getSynonym() will recognize editown as edit if editown isn’t defined in the permission class’ $this->permissions property for the requested level.

Manipulating permissions before saving

<?php
/**
  • @param array $permissions 插件特定的权限

  • @param $allPermissions 所有角色的权限

  • @param bool $isSecondRound 是否是第二轮,在所有权限类更新权限之后

  • @return bool 返回 true 如果需要进行第二轮;默认为 false

*/

public function analyzePermissions(array &$permissions, $allPermissions, $isSecondRound = false) {

foreach ($permissions as $level => &$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;

}

}

}

}

}

// 如果此方法需要在其他模块调整权限后进行第二轮,则返回 true;默认为 false return false;

}

插件可以根据其他选定的权限来调整权限,以防止“用户错误”。 例如,如果一个用户具有“编辑”的权限,那么该用户也需要具有“查看”的权限,无论该权限是否在角色表单中被选中。 您可以使用 analyzePermissions() 方法来实现这一点,这为插件提供了修改权限的机会,可以在将权限持久化到数据库之前,根据其他选择进行调整。

有时,可能需要基于一个超出插件控制范围的权限来进行重新调整。 在这种情况下,analyzePermissions() 可以返回 true,并且它会在所有权限被其他模块和插件分析之后再次调用。 在这种情况下,参数 $isSecondRound 为 true。

高级 isGranted 逻辑

如果需要执行除了简单比较位之外的其他逻辑,则权限类可以覆盖父类的 public function isGranted($userPermissions, $name, $level) 方法,并根据其自身的权限级别和单个权限执行必要的操作。

高级 isSupported 逻辑

同样适用于 isSupported() 方法,您可以使用它来确定一个模块或插件是否包含请求的权限和权限级别。 您还可以使用此方法提供向后兼容性支持。