如果用户属于许多组织,则单独的角色/权限

Posted

技术标签:

【中文标题】如果用户属于许多组织,则单独的角色/权限【英文标题】:Separate roles/permissions if user belongs to many organizations 【发布时间】:2019-06-24 14:45:23 【问题描述】:

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

我尝试设置两个保护,一个用于主用户帐户,另一个用于用户和他们登录的组织之间的枢轴模型。所以基本上当他们使用主用户模型登录应用程序时,我会询问他们想登录哪个组织,当他们选择组织时,我还将在数据透视模型上设置一个身份验证会话,将用户链接到组织并访问该模型之外的角色。这可行,但必须管理身份验证会话有点痛苦。

// 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);
    

我希望用户能够使用单次登录来登录应用程序,但也能够对不同的组织拥有不同的权限。

【问题讨论】:

Spatie Laravel 权限做得很好,但不能响应所有用例。您应该考虑实施自己的角色/权限系统。一旦您深入了解并提供更多灵活性,这并不难。 我想知道在 model_has_roles 和 model_has_permissions 表中添加一个 organization_id 是否会是票。然后我应该能够过滤该 organization_id 以获取用户模型的角色和权限。嗯.. 你可以试试,但考虑一下:包的基础是分配角色/权限和检查角色/权限。通过添加一个 organization_id,您不能分配也不能检查包。你需要自己做。包中唯一留给您的是缓存和模型/迁移的基础。在我看来,不要过度依赖包是件好事。尤其是当它们没有达到预期的行为时。 【参考方案1】:

我通过执行以下操作解决了这个问题。我欢迎大家对这种方法的看法提供反馈!

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

    我们公司模型有如下关系和方法
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);
    

    我们修改了枢轴模型以具有 Spatie HasRoles 特征。这允许我们将角色分配给CompanyUser,而不是 Auth 用户。您还需要指定默认的守卫或 Spatie 权限。
class CompanyUser extends Pivot

    use HasRoles;

    protected $guard_name = 'web';

    在用户模型上,我创建了HasCompanies 特征。这提供了关系并提供了一种将角色分配给新公司用户的方法。此外,它还覆盖了门 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);
    

现在我们可以这样做了:

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

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

$ownerRoll->givePermissionTo($permission);
    使用所有者用户创建一个新公司,然后将该公司切换到该所有者的活动公司。
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();

所以这一切都有效,我主要担心的是:

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

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

【讨论】:

以上是关于如果用户属于许多组织,则单独的角色/权限的主要内容,如果未能解决你的问题,请参考以下文章

SqlServer 添加用户 添加角色 分配权限

如何为 GitHub 组织添加自定义用户角色?

RBAC介绍

数据权限设计思路(摘抄)

Oracle 用户,角色,权限等

Oracle 用户,角色,权限等