Skip to content

Commit

Permalink
[8.x] Add gate policy callback (#39185)
Browse files Browse the repository at this point in the history
* wip

* wip

* Apply fixes from StyleCI

* formatting

* wip

* Apply fixes from StyleCI

Co-authored-by: Taylor Otwell <taylorotwell@gmail.com>
  • Loading branch information
cbl and taylorotwell authored Oct 14, 2021
1 parent a01e9ed commit 02c5dc5
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/Illuminate/Auth/Access/Gate.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ public function denies($ability, $arguments = [])
*/
public function check($abilities, $arguments = [])
{
if (is_array($abilities) && class_exists($abilities[0])) {
$abilities = [$abilities];
}

return collect($abilities)->every(function ($ability) use ($arguments) {
return $this->inspect($ability, $arguments)->allowed();
});
Expand All @@ -293,6 +297,13 @@ public function check($abilities, $arguments = [])
*/
public function any($abilities, $arguments = [])
{
// Gate::any([Policy::class, ['view', 'create']], $post)...
if (isset($abilities[1]) && is_array($abilities[1])) {
$abilities = collect($abilities[1])->map(function ($ability) use ($abilities) {
return [$abilities[0], $ability];
})->all();
}

return collect($abilities)->contains(function ($ability) use ($arguments) {
return $this->check($ability, $arguments);
});
Expand Down Expand Up @@ -556,6 +567,19 @@ protected function resolveAuthCallback($user, $ability, array $arguments)
return $callback;
}

if (is_array($ability)) {
[$class, $method] = $ability;

if ($this->canBeCalledWithUser($user, $class, $method)) {
return $this->getCallableFromClassAndMethod($class, $method);
}
}

if (class_exists($ability) &&
$this->canBeCalledWithUser($user, $ability, '__invoke')) {
return $this->getCallableFromClassAndMethod($ability);
}

if (isset($this->stringCallbacks[$ability])) {
[$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);

Expand Down Expand Up @@ -781,6 +805,20 @@ protected function resolveUser()
return call_user_func($this->userResolver);
}

/**
* Get a callable from a class and method.
*
* @param string $class
* @param string $method
* @return \Closure
*/
protected function getCallableFromClassAndMethod($class, $method = '__invoke')
{
return function (...$params) use ($class, $method) {
return $this->container->make($class)->{$method}(...$params);
};
}

/**
* Get all of the defined abilities.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Illuminate/Foundation/Auth/Access/AuthorizesRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ protected function parseAbilityAndArguments($ability, $arguments)
return [$ability, $arguments];
}

if (is_array($ability)) {
return [$ability, $arguments];
}

$method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];

return [$this->normalizeGuessedAbilityName($method), $ability];
Expand Down
40 changes: 40 additions & 0 deletions tests/Auth/AuthAccessGateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,46 @@ public function testEveryAbilityCheckFailsIfNonePass()
$this->assertFalse($gate->check(['edit', 'update'], new AccessGateTestDummy));
}

public function testInspectPolicyCallback()
{
$gate = $this->getBasicGate();

$response = $gate->inspect([AccessGateTestPolicyWithAllPermissions::class, 'edit'], new AccessGateTestDummy);

$this->assertFalse($response->denied());
$this->assertTrue($response->allowed());
}

public function testInspectPolicyCallbackInvoke()
{
$gate = $this->getBasicGate();

$response = $gate->inspect(AccessGateTestGuestInvokableClass::class, new AccessGateTestDummy);

$this->assertFalse($response->denied());
$this->assertTrue($response->allowed());
}

public function testCheckUsingPolicyCallback()
{
$gate = $this->getBasicGate();

$this->assertTrue($gate->check(
[AccessGateTestPolicyWithAllPermissions::class, 'edit'],
new AccessGateTestDummy)
);
}

public function testAnyUsingPolicyCallback()
{
$gate = $this->getBasicGate();

$this->assertTrue($gate->any(
[AccessGateTestPolicyWithAllPermissions::class, ['edit', 'update']],
new AccessGateTestDummy)
);
}

/**
* @dataProvider hasAbilitiesTestDataProvider
*
Expand Down
12 changes: 12 additions & 0 deletions tests/Foundation/FoundationAuthorizesRequestsTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ protected function tearDown(): void
Container::setInstance(null);
}

public function testCallableGateCheck()
{
unset($_SERVER['_test.authorizes.trait']);

$gate = $this->getBasicGate();

$response = (new FoundationTestAuthorizeTraitClass)->authorize([FoundationAuthorizesRequestTestPolicy::class, 'create']);

$this->assertInstanceOf(Response::class, $response);
$this->assertTrue($_SERVER['_test.authorizes.trait.policy']);
}

public function testBasicGateCheck()
{
unset($_SERVER['_test.authorizes.trait']);
Expand Down

0 comments on commit 02c5dc5

Please sign in to comment.