【问题标题】:Symfony/sfDoctrineGuard Conditional PermissionsSymfony/sfDoctrineGuard 条件权限
【发布时间】:2010-09-12 23:46:11
【问题描述】:

我正在考虑实现条件权限的最佳方式,即用户和团队是 m-to-m。但是每个 Team 也与 User 表有一对一的“Leader”关系。

为简单起见,假设我们有两个权限级别,“用户”和“管理员”。那么假设只有特定的团队管理任务,即群组电子邮件,只能由该团队的负责人发送。

目前,特定于团队负责人的每个操作都会查询数据库以检查当前用户是否是团队负责人(请记住,一个用户可能领导多个团队)。我个人不喜欢到处看到“if($user->isLeader($team))”。

我考虑过在登录时(ala phpBB)的用户会话中设置由用户领导的团队列表,或者使用 symfony 过滤器来做同样的事情。

但是,在第一种方法中,如果其他用户可能更换团队负责人,数据可能会变得陈旧。第二种方法需要在每次页面加载时进行额外的数据库查询。

有更好的想法吗? 注意:一个项目中有多个应用需要共享相同的权限模型(即后端和api)

【问题讨论】:

    标签: symfony1 doctrine symfony-1.4 sfdoctrineguard


    【解决方案1】:

    我看到了 2 个选项:

    覆盖sfDoctrineGuardUser::getGuardUser()

    覆盖 sfDoctrineGuardUser 的 getGuardUser 方法以在获取用户时获取团队。这需要一个额外的左连接,但它可以节省您以后的查询。

    /**
    * Overridden getGuardUser to automatically fetch Teams with User
    * @see plugins/sfDoctrineGuardPlugin/lib/user/sfGuardSecurityUser#getGuardUser()
    */
    public function getGuardUser()
    {
      if (!$this->user && $id = $this->getAttribute('user_id', null, 'sfGuardSecurityUser'))
      {
        $this->user = sfGuardUserTable::findUserByIdWithTeams($id); //write this query to fetch a user with his teams
    
        if (!$this->user)
        {
          // the user does not exist anymore in the database
          $this->signOut();
    
          throw new sfException('The user does not exist anymore in the database.');
        }
      }
    
      return $this->user;
    }
    

    然后,您可以通过过滤器或覆盖hasCredential,根据他的团队添加用户的凭据。

    使缓存的团队状态无效

    如果您不想要任何额外的查询,我看到的唯一额外选项是使用全局缓存。这将涉及以下内容:

    • 在登录时缓存用户的团队或基于团队的凭据
    • 删除或添加团队负责人时,请执行apc_add(team_invalid_[team_id], true, [length_of_session]) 之类的操作
    • 无论何时添加用户凭据(通过第一个选项中描述的方法之一),检查缓存以查看它们是否无效。

    【讨论】:

    • 这与我所追求的非常接近。我可能会使用 memcache 实现类似的解决方案。
    【解决方案2】:

    我通过重写 myUser 中的 hasCredential 方法以在需要某个凭据时执行自定义检查来实现类似的效果。

    例如:

    public function hasCredential($credential, $useAnd = true) {
    
      // make sure the symfony core always passes the team leader permission, we handle it later ourselves
      $this->addCredential('team_leader');
      $ret = parent::hasCredential($credential, $useAnd);
    
      // if other checks have failed, return false now, no point continuing
      if (!$ret) return false;
    
      if ($credential == 'team_leader' || (is_array($credential) && in_array('team_leader', $credential))) {
        // do stuff here. in this example, we get the object from a route and check the user is a leader
        $route = sfcontext::getinstance()->getRequest()->getAttribute('sf_route');
        if (!$route instanceof sfObjectRoute) {
          throw new sfConfigurationException(sprintf('team_leader credential cannot be used against routes of class %s - must be sfObjectRoute', get_class($route)));
        }
        return $this->isLeader($route->getObject());
      }
      return $ret;
    }
    

    然后您可以将“team_leader”凭据添加到 security.yml 中,就像其他任何操作一样。

    显然这取决于你使用 sfObjectRoutes,所以如果你不能这样做并且不能适应你正在使用的东西,它可能不是那么合适,但我认为当你可以使用它时它是一个很好的解决方案!

    如果您担心额外的查询,可以考虑在 isLeader 调用周围包装一些缓存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 2018-10-30
      • 2011-11-25
      • 1970-01-01
      • 2011-10-01
      • 1970-01-01
      • 2014-11-02
      相关资源
      最近更新 更多