如果用户属于多个组织,则将角色/权限分开

时间:2019-06-24 14:45:23

标签: laravel spatie

我有一个用户可以属于多个组织的应用程序。我想以一种方式设置用户,使每个组织可以拥有不同的角色/权限。我正在使用Laravel并计划实施Spatie / laravel-permission。实现此目的的最佳方法是什么?

我尝试设置两个保护措施,一个用于主要用户帐户,另一个用于在用户与他们登录的组织之间的数据透视模型。因此,基本上,当他们使用主用户模型登录应用程序时,我问他们要登录哪个组织,当他们选择组织时,我还将在数据透视模型上建立一个身份验证会话,以将用户链接到组织并使用该模型访问角色。这行得通,但是必须管理auth会话有点麻烦。

// User Model
class User extends Authenticatable
{
    public function organizationUsers()
    {
        return $this->hasMany(OrganizationUser::class);
    }
}
// OrganizationUser Model

class Organziationuser extends Authenticatable
{
    use HasRoles;

    public $guard_name = 'organization_user';

    public function organization()
    {
        return $this->belongsTo(Organization::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

我希望用户能够使用一个登录名登录到应用程序,但是也能够对不同的组织具有不同的权限。

1 个答案:

答案 0 :(得分:0)

通过执行以下操作可以解决此问题。我欢迎人们对此方法提出意见!

注意:目前,我仅使用具有Spatie权限的model_has_roles表,并始终使用$user->can('Permission')来检查权限。

  1. 我们的公司模型具有以下关系和方法
class Company extends Model
{
    public function owner(): HasOne
    {
        return $this->hasOne(User::class, 'id', 'user_id');
    }

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(
            User::class, 'company_users', 'company_id', 'user_id'
        )->using(CompanyUser::class);
    }

    public function addTeamMember(User $user)
    {
        $this->users()->detach($user);

        $this->users()->attach($user);
    }
}
  1. 我们修改了枢轴模型以具有Spatie HasRoles特征。这使我们可以为CompanyUser分配一个角色,而不是Auth User。您还需要指定默认的Guard或Spatie权限Squarks。
class CompanyUser extends Pivot
{
    use HasRoles;

    protected $guard_name = 'web';
}
  1. 在用户模型上,我创建了HasCompanies特性。这提供了关系,并提供了将角色分配给新公司用户的方法。此外,它会覆盖gate can()方法。

用户可以属于许多公司,但一次只能拥有一个活跃的公司(即他们正在查看的公司)。我们使用current_company_id列对此进行定义。

同样重要的是要确保数据透视表ID被拉过(这将不是标准的),因为这是我们在Spatie model_has_roles table中所使用的。

trait HasCompanies
{
    public function companies(): HasMany
    {
        return $this->hasMany(Company::class);
    }

    public function currentCompany(): HasOne
    {
        return $this->hasOne(Company::class, 'id', 'current_company_id');
    }

    public function teams(): BelongsToMany
    {
        return $this->belongsToMany(
            Company::class, 'company_users', 'user_id', 'company_id'
        )->using(CompanyUser::class)->withPivot('id');
    }

    public function switchCompanies(Company $company): void
    {
        $this->current_company_id = $company->id;
        $this->save();
    }

    public function assignRolesForCompany(Company $company, ...$roles)
    {
        if($company = $this->teams()->where('companies.id', $company->id)->first()){
            /** @var CompanyUser $companyUser */
            $companyUser = $company->pivot;
            $companyUser->assignRole($roles);
            return;
        }

        throw new Exception('Roles could not be assigned to company user');
    }

    public function can($ability, $arguments = [])
    {
        if(isset($this->current_company_id)){
            /** @var CompanyUser $companyUser */
            $companyUser = $this->teams()->where('companies.id', $this->current_company_id)->first()->pivot;

            if($companyUser->hasPermissionTo($ability)){
                return true;
            }

            // We still run through the gate on fail, as this will check for gate bypass. i.e. Super User
            return app(Gate::class)->forUser($this)->check('InvalidPermission');
        }

        return app(Gate::class)->forUser($this)->check($ability, $arguments);
    }
}

现在我们可以执行以下操作:

  1. 创建角色和权限
/** @var Role $ownerRoll */
$ownerRoll = Role::create(['name' => 'Owner']);

/** @var Permission $permission */
$permission = Permission::create([
    'name' => 'Create Company',
    'guard_name' => 'web',
]);

$ownerRoll->givePermissionTo($permission);
  1. 使用拥有的用户创建新公司,然后将该公司切换为该所有者的活跃公司。
public function store(CompanyStoreRequest $request)
{
    DB::transaction(function () use($request) {
        /** @var User $owner */
        $owner = User::findOrFail($request->user_id);

        /** @var Company $company */
        $company = $owner->companies()->create($request->validated());
        $company->addTeamMember($owner);

        $owner->assignRolesForCompany($company, 'Owner');
        $owner->switchCompanies($company);
    });

    return redirect()->back();
}

所有这些都有效,我主要担心的是:

  1. 我们正在覆盖can方法。可能还有其他未捕获的授权方法/门功能。

  2. 我们有2组model_permissions。 Auth用户和公司用户。我认为我需要进行一些检查,以确保仅将正确类型的用户分配给角色。在此阶段,所有管理员用户都将具有分配给其auth用户的权限,而拥有公司的任何用户都应仅对公司用户模型具有权限