Skip to content

Commit

Permalink
When caching queries, always check roles in the cache
Browse files Browse the repository at this point in the history
Closes #418
  • Loading branch information
JosephSilber committed Apr 29, 2019
1 parent 84f6fa1 commit 5a8c806
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 17 deletions.
61 changes: 49 additions & 12 deletions src/BaseClipboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,36 +34,73 @@ public function check(Model $authority, $ability, $model = null)
*/
public function checkRole(Model $authority, $roles, $boolean = 'or')
{
$available = $this->getRoles($authority)
->intersect(Models::role()->getRoleNames($roles));
$count = $this->countMatchingRoles($authority, $roles);

if ($boolean == 'or') {
return $available->count() > 0;
return $count > 0;
} elseif ($boolean === 'not') {
return $available->count() === 0;
return $count === 0;
}

return $available->count() == count((array) $roles);
return $count == count((array) $roles);
}

/**
* Get the given authority's roles.
* Count the authority's roles matching the given roles.
*
* @param \Illuminate\Database\Eloquent\Model $authority
* @return \Illuminate\Support\Collection
* @param array|string $roles
* @return int
*/
public function getRoles(Model $authority)
protected function countMatchingRoles($authority, $roles)
{
$collection = $authority->roles()->get(['name'])->pluck('name');
$lookups = $this->getRolesLookup($authority);

return count(array_filter($roles, function ($role) use ($lookups) {
switch (true) {
case is_string($role):
return $lookups['names']->has($role);
case is_numeric($role):
return $lookups['ids']->has($role);
case $role instanceof Model:
return $lookups['ids']->has($role->getKey());
}

throw new InvalidArgumentException('Invalid model identifier');
}));
}

/**
* Get the given authority's roles' IDs and names.
*
* @param \Illuminate\Database\Eloquent\Model $authority
* @return array
*/
public function getRolesLookup(Model $authority)
{
$roles = $authority->roles()->pluck(
'name', Models::role()->getQualifiedKeyName()
);

// In Laravel 5.1, "pluck" returns an Eloquent collection,
// so we call "toBase" on it. In 5.2, "pluck" returns a
// base instance, so there is no "toBase" available.
if (method_exists($collection, 'toBase')) {
$collection = $collection->toBase();
if (method_exists($roles, 'toBase')) {
$roles = $roles->toBase();
}

return $collection;
return ['ids' => $roles, 'names' => $roles->flip()];
}

/**
* Get the given authority's roles' names.
*
* @param \Illuminate\Database\Eloquent\Model $authority
* @return \Illuminate\Support\Collection
*/
public function getRoles(Model $authority)
{
return $this->getRolesLookup($authority)['names']->keys();
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/CachedClipboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,17 @@ public function getFreshAbilities(Model $authority, $allowed)
}

/**
* Get the given authority's roles.
* Get the given authority's roles' IDs and names.
*
* @param \Illuminate\Database\Eloquent\Model $authority
* @return \Illuminate\Support\Collection
* @return array
*/
public function getRoles(Model $authority)
public function getRolesLookup(Model $authority)
{
$key = $this->getCacheKey($authority, 'roles');

return $this->sear($key, function () use ($authority) {
return parent::getRoles($authority);
return parent::getRolesLookup($authority);
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/Database/Concerns/HasRoles.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function roles()
/**
* Get all of the model's assigned roles.
*
* @return \Illuminate\Database\Eloquent\Collection
* @return \Illuminate\Support\Collection
*/
public function getRoles()
{
Expand Down
23 changes: 23 additions & 0 deletions tests/CachedClipboardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ function it_caches_roles()
$this->assertFalse($bouncer->is($user)->a('moderator'));
}

/**
* @test
*/
function it_always_checks_roles_in_the_cache()
{
$bouncer = $this->bouncer($user = User::create());
$admin = $bouncer->role()->create(['name' => 'admin']);

$bouncer->assign($admin)->to($user);

$this->assertTrue($bouncer->is($user)->an('admin'));

$this->db()->connection()->enableQueryLog();

$this->assertTrue($bouncer->is($user)->an($admin));
$this->assertTrue($bouncer->is($user)->an('admin'));
$this->assertTrue($bouncer->is($user)->an($admin->id));

$this->assertEmpty($this->db()->connection()->getQueryLog());

$this->db()->connection()->disableQueryLog();
}

/**
* @test
*/
Expand Down

0 comments on commit 5a8c806

Please sign in to comment.