1

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

为简单起见,假设我们有两个权限级别,“用户”和“管理员”。然后让我们说,只有特定的团队管理任务,即组电子邮件,只能由该团队的领导发送。

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

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

但是,在第一种方法中,在另一个用户可能更换团队领导的情况下,数据可能会变得陈旧。第二种方法需要在每次页面加载时进行额外的数据库查询。

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

4

2 回答 2

0

我通过覆盖 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 调用周围包装一些缓存。

于 2010-09-13T09:28:23.753 回答
0

我看到 2 个选项:

覆盖sfDoctrineGuardUser::getGuardUser()

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

/**
* 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

使缓存的团队状态无效

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

  • 在登录时缓存用户的团队或基于团队的凭据
  • When a team leader is removed or added, do something like apc_add(team_invalid_[team_id], true, [length_of_session])
  • Whenever the user's credentials are added (via one of the methods described in the first option), check the cache to see if they are invalid.
于 2010-09-14T03:14:17.057 回答