1

我有一种情况,用户可以属于许多团队/公司,并且在该团队/公司中,他们可以根据他们登录的角色和权限具有不同的角色和权限。我想出了以下解决方案,并希望得到一些反馈!

注意:目前我只使用model_has_roles具有 Spatie 权限的表,并始终用于$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. 我们修改枢轴模型以具有 SpatieHasRoles特征。这允许我们为 Auth 用户分配一个角色,CompanyUser而不是 Auth 用户。您还需要指定默认的守卫或 Spatie 权限。
class CompanyUser extends Pivot
{
    use HasRoles;

    protected $guard_name = 'web';
}
  1. 在用户模型上,我创建了HasCompaniesTrait。这提供了关系并提供了一种将角色分配给新公司用户的方法。此外,它覆盖了门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();
    }

    private function companyWithPivot(Company $company)
    {
        return $this->teams()->where('companies.id', $company->id)->first();
    }

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

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

    public function hasRoleForCurrentCompany(string $roles, Company $company = null, string $guard = null): bool
    {
        if(! $company){
            if(! $company = $this->currentCompany){
                throw new Exception('Cannot check role for current company because it has not been set');
            }
        }

        if($company = $this->companyWithPivot($company)){
            /** @var CompanyUser $companyUser */
            $companyUser = $company->pivot;
            return $companyUser->hasRole($roles, $guard);
        }

        return false;
    }

    public function can($ability, $arguments = []): bool
    {
        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;
            }

            // Still run through the gate as this will check for gate bypass
            return app(Gate::class)->forUser($this)->check('N/A', []);
        }

        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 用户的权限,而任何拥有公司的用户都应该只拥有公司用户模型的权限

4

0 回答 0