Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/Models/Role.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,20 @@ public function permissions(): BelongsToMany
*/
public function users(): BelongsToMany
{
$guard = $this->attributes['guard_name'] ?? config('auth.defaults.guard');
$modelClass = getModelForGuard($guard);

if (! is_string($modelClass) || $modelClass === '') {
throw new \InvalidArgumentException(
sprintf(
"Guard '%s' is not configured with an associated provider model.",
(string) $guard
)
);
}

return $this->morphedByMany(
getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
$modelClass,
'model',
config('permission.table_names.model_has_roles'),
app(PermissionRegistrar::class)->pivotRole,
Expand Down
2 changes: 1 addition & 1 deletion src/PermissionRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public function getPermissionsTeamId()
public function registerPermissions(Gate $gate): bool
{
$gate->before(function (Authorizable $user, string $ability, array &$args = []) {
if (is_string($args[0] ?? null) && ! class_exists($args[0])) {
if (is_string($args[0] ?? null) && array_key_exists($args[0], config('auth.guards', []))) {
$guard = array_shift($args);
}
if (method_exists($user, 'checkPermissionTo')) {
Expand Down
9 changes: 6 additions & 3 deletions src/Traits/HasRoles.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,13 @@ public function hasExactRoles($roles, ?string $guard = null): bool
$roles = [$roles->name];
}

$roles = collect()->make($roles)->map(fn ($role) => $role instanceof Role ? $role->name : $role
);
$roles = collect()->make($roles)->map(fn ($role) => $role instanceof Role ? $role->name : $role);

$currentCount = $guard
? $this->roles->where('guard_name', $guard)->count()
: $this->roles->count();

return $this->roles->count() == $roles->count() && $this->hasAllRoles($roles, $guard);
return $currentCount == $roles->count() && $this->hasAllRoles($roles, $guard);
}

/**
Expand Down
22 changes: 22 additions & 0 deletions tests/HasRolesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,28 @@ public function it_can_determine_that_a_user_has_exact_all_of_the_given_roles()
$this->assertFalse($this->testUser->hasExactRoles(['testRole', 'second role', 'third role'], 'fakeGuard'));
}

/** @test */
#[Test]
public function has_exact_roles_honours_guard_and_ignores_extra_roles_on_other_guards()
{
$this->testUser->assignRole('testRole');
$apiRole = app(Role::class)->create(['name' => 'apiRole', 'guard_name' => 'api']);
$this->testUser->assignRole($apiRole);

$this->assertTrue($this->testUser->hasExactRoles('testRole', 'web'));
}

/** @test */
#[Test]
public function role_users_relation_throws_when_guard_has_no_configured_model()
{
$this->expectException(\InvalidArgumentException::class);

$badRole = app(Role::class)->create(['name' => 'badRole', 'guard_name' => 'missing']);

$badRole->users();
}

/** @test */
#[Test]
public function it_can_determine_that_a_user_does_not_have_a_role_from_another_guard()
Expand Down
14 changes: 14 additions & 0 deletions tests/MultipleGuardsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ public function the_gate_can_grant_permission_to_a_user_by_passing_a_guard_name(
$this->assertTrue($this->testUser->cannot('admin-permission', 'web'));
}

/** @test */
#[Test]
public function it_does_not_treat_non_guard_strings_as_guard_arguments()
{
$this->testUser->givePermissionTo(app(Permission::class)::create([
'name' => 'do_misc',
'guard_name' => 'web',
]));

$this->assertTrue($this->testUser->can('do_misc', 'notAGuardName'));

$this->assertFalse($this->testUser->can('do_misc', 'admin'));
}

/** @test */
#[Test]
public function it_can_honour_guardName_function_on_model_for_overriding_guard_name_property()
Expand Down
5 changes: 5 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@

abstract class TestCase extends Orchestra
{
// Polyfill for older Orchestra Testbench versions in prefer-lowest CI matrix
// that expect a static $latestResponse property on the base TestCase.
// Newer versions manage this internally; declaring it here is harmless.
public static $latestResponse;

/** @var \Spatie\Permission\Tests\TestModels\User */
protected $testUser;

Expand Down