返回

OroCRM 实体创建后修改访问控制列表(ACL)权限

php

OROCRM 中创建实体后修改访问控制列表 (ACL)

我碰到了一个问题, 就是在 OROCRM 里创建完实体之后,想修改它的权限,折腾了半天,现在把我的处理经过总结一下。

一、 问题的产生

一开始,我创建实体的时候,权限配置是这样的:

/**
 * @ORM\Entity
 * @ORM\Table(name="my_table")
 * @ORM\HasLifecycleCallbacks()
 * @Config(
 *     routeView="my_route_view",
 *     defaultValues={
 *           "security"={
 *               "type"="ACL",
 *               "group_name"="",
 *                "permissions"="VIEW"
 *           },
 *          "entity"={
 *               "icon"="fa-briefcase"
 *           },
 *           "dataaudit"={
 *               "auditable"=false
 *           }
 *       }
 *  )
 *
 */

看到了吧,permissions 那里,我只给了 VIEW,也就是查看的权限。 结果,系统就只给我生成了查看权限的 ACL。

后来我需要给这个实体加上创建、更新、删除的权限。 我试了两种方法:

  1. permissions 属性去掉:
/**
 * @ORM\Entity
 * @ORM\Table(name="pb_inventory_synchronization_queue")
 * @ORM\HasLifecycleCallbacks()
 * @Config(
 *     routeView="pb_inventory_synchronization_queue_view",
 *     defaultValues={
 *           "security"={
 *               "type"="ACL",
 *               "group_name"=""
 *           },
 *          "entity"={
 *               "icon"="fa-briefcase"
 *           },
 *           "dataaudit"={
 *               "auditable"=false
 *           }
 *       }
 *  )
 *
 */
  1. 把所有权限都明确写出来:
/**
 * @ORM\Entity
 * @ORM\Table(name="pb_inventory_synchronization_queue")
 * @ORM\HasLifecycleCallbacks()
 * @Config(
 *     routeView="pb_inventory_synchronization_queue_view",
 *     defaultValues={
 *           "security"={
 *               "type"="ACL",
 *               "group_name"="",
 *                "permissions"="VIEW;EDIT;DELETE;CREATED;"
 *           },
 *          "entity"={
 *               "icon"="fa-briefcase"
 *           },
 *           "dataaudit"={
 *               "auditable"=false
 *           }
 *       }
 *  )
 *
 */

可惜,这两种办法都不好使。 在角色管理界面,还是只能看到查看权限。 通过 API 创建、编辑、删除的时候,直接报 403 错误,提示:

No access to this type of entities.

二、问题的原因

OROCRM 在创建实体的时候,会根据 @Config 注解里的 security 部分生成 ACL。 如果 permissions 属性只指定了部分权限, 它就只生成这些权限的 ACL, 后面再想改, 直接在注解里改是不行的。 OROCRM 不会因为你改了注解,就自动更新 ACL。

三、 解决方案

要解决这个问题,得用 OROCRM 提供的命令行工具,或者直接操作数据库。

1. 使用 oro:acl:update 命令

这是最推荐的方法。

  • 原理: oro:acl:update 命令会重新扫描所有实体类的 @Config 注解,并更新 ACL。

  • 操作步骤:

    1. 修改实体类的 @Config 注解,把 permissions 设置成你想要的权限,比如 "permissions"="VIEW;CREATE;UPDATE;DELETE",或者直接把 "permission" 属性去掉. 让 OROCRM 使用默认权限.

    2. 在命令行里,进入到你的 OROCRM 项目根目录。

    3. 执行以下命令:

      php bin/console oro:acl:update --env=prod
      

      --env=prod 表示在生产环境下执行。如果是开发环境,改成 --env=dev

    4. 清理缓存:

      php bin/console cache:clear --env=prod
      
    5. 登录 OROCRM 后台管理检查权限设置。

  • 进阶技巧:

    • 如果只想更新某个特定实体的 ACL, 可以用 --class 参数指定实体类名:

      php bin/console oro:acl:update --class="Acme\\Bundle\\Entity\\MyEntity" --env=prod
      

      Acme\\Bundle\\Entity\\MyEntity 换成你自己的实体类名。

    • oro:acl:update 只会更新现有的 ACL, 不会删除多余的。如果有需要, 可以配合 --flush 选项,强制刷新.

2. 使用 oro:entity-config:update 命令

这个命令用来更新实体配置,包括 ACL。

  • 原理: oro:entity-config:update 会重新读取实体配置,并更新数据库里的相关记录。

  • 操作步骤:

    1. 修改实体类的 @Config 注解,配置好permissions

    2. 运行命令:

      php bin/console oro:entity-config:update <entity-class-name> --env=prod
      

      <entity-class-name> 换成你的实体类名。

    3. 清理缓存:

       php bin/console cache:clear --env=prod
      

3.直接修改数据库 (不推荐, 仅供参考)

这种方法比较危险,容易出错,不到万不得已,别用.

  • 原理: OROCRM 的 ACL 信息存储在几个表里,主要是 acl_classesacl_entriesacl_object_identitiesacl_security_identities。 可以直接修改这些表里的数据。

  • 操作步骤(非常粗略,可能有遗漏):

    1. 找到你的实体对应的 acl_classes 表里的记录, 记录下 id
    2. acl_entries 表里, 找到 class_id 等于上面那个 id 的记录, 这里就是你的实体的各项权限。
    3. 根据需要, 添加、修改或删除 acl_entries 表里的记录。比如, 要添加创建权限, 就插入一条新记录, permission 字段设置为 CREATE
    4. 可能还需要修改 acl_object_identitiesacl_security_identities 表,具体怎么改, 取决于你的权限设置。
    5. 改完之后,一定要清理缓存!

4. 使用Migration

  • 原理: 使用数据库迁移脚本修改ACL定义。

  • 操作步骤:

    1. 创建一个 Migration:
      php bin/console make:migration
      
      随便起一个名字。
    2. 在新生成的 Migration 文件(src/Migrations/Version<版本号>.php)中的up()函数下加入代码:
       <?php
    
       declare(strict_types=1);
    
       namespace DoctrineMigrations;
    
       use Doctrine\DBAL\Schema\Schema;
       use Doctrine\Migrations\AbstractMigration;
       use Oro\Bundle\SecurityBundle\Acl\Extension\EntityAclExtension;
       use Oro\Bundle\SecurityBundle\Acl\Persistence\AclManager;
       use Symfony\Component\DependencyInjection\ContainerAwareInterface;
       use Symfony\Component\DependencyInjection\ContainerAwareTrait;
    
    
       final class Version20231117171005 extends AbstractMigration implements ContainerAwareInterface
       {
         use ContainerAwareTrait;
         public function getDescription(): string
           {
               return 'Modify ACL';
           }
    
          public function up(Schema $schema): void
           {
               /** @var AclManager $aclManager */
    
               $aclManager = $this->container->get('oro_security.acl.manager');
               if ($aclManager->isAclEnabled()) {
                 $sid = $aclManager->getSid('IS_AUTHENTICATED_FULLY'); // Replace 'IS_AUTHENTICATED_FULLY' by you actual user.
                  $oid = $aclManager->getOid('entity:Your\Entity\Namespace'); // Replace "Your\Entity\Namespace" to your current entity
                 $this->extendEntityAcl(
                       $aclManager,
                        $sid,
                        $oid,
                       [
                         'CREATE' => 1, // Enable Create permission at organization level. Replace by bit mask you want.
                         'DELETE' => 1, // Enable Delete.
                         'VIEW'   => 2,
                          'EDIT'   => 8
                      ]
                 );
    
                  $aclManager->flush();
    
               }
    
           }
          private function extendEntityAcl(AclManager $aclManager, $sid, $oid, array $permissions, int $mask = 0): void
           {
               $extension = $aclManager->getExtensionSelector()->selectByObjectIdentity($oid);
               if (!$extension instanceof EntityAclExtension) {
                   // we can extend only entity ACLs
                   return;
               }
    
               $rootOid = $extension->getRootObjectIdentity($oid);
               $acl = $aclManager->getAcl($rootOid, [$sid]);
    
               $aces = $acl->getClassAces(); //get all ACE for class. It work also for getObjectAces.
               if(empty($aces))
               {
    
                   $acl = $extension->addPermission($acl, $sid, key($permissions) , $mask,0,false); // Insert new ACE permission
    
               }
               else{
                     foreach ($permissions as $permission => $permissionMask) {
    
                           $extension->setPermission($aces[0], $sid,  $permission,$permissionMask ,0);
    
                       }
    
               }
    
          }
           public function down(Schema $schema): void
          {
              //  $this->addSql('ALTER TABLE your_entity_ DROP new_field');
          }
    
       }
        ```
      3. 修改`oid`: Replace "Your\Entity\Namespace" to your entity class namespace.
      4. 修改`sid`: 获得用户的 Security identity (SID)。如果是针对 Role 设置权限,代码类似 `$aclManager->getSid( new RoleSecurityIdentity($role))` ,其中 `$role` 是 `Oro\Bundle\UserBundle\Entity\Role` 对象。更简单的方法是用 `$aclManager->getSid('IS_AUTHENTICATED_FULLY');`. 保证对应实体有用户访问过,才会生成对应的 SID。如果遇到 "SID does not exist." 错误, 先随便用个有权限的用户登录,访问下相关页面,让系统创建这个 SID。
       5. 在 extendEntityAcl 里面的 $permission 里面的数组配置修改权限,以及权限的范围 (Organization, Business Unit, User). 参考:`Oro\Bundle\SecurityBundle\Model\AclPermission` 和  `Oro\Bundle\SecurityBundle\Acl\AccessLevel` 。
    3. 执行迁移:
    
       ```bash
       php bin/console doctrine:migrations:migrate
       ```
       刷新缓存。
    
       ```bash
       php bin/console cache:clear
       ```
    
    

四、 总结

  • 修改已创建实体的 ACL,直接改注解不行,要用命令行工具或者直接操作数据库,使用Migration也是不错的选择。
  • oro:acl:update 命令最方便。
  • Migration 方式更为推荐.
  • 改完之后,一定要清理缓存。

做 OROCRM 开发,遇到权限问题是家常便饭。希望我的这点总结对大家有点帮助。