Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.4] Apply policies on subtypes of the configured class #15757

Merged
merged 2 commits into from
Oct 5, 2016
Merged
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
58 changes: 23 additions & 35 deletions src/Illuminate/Auth/Access/Gate.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,15 @@ protected function callAfterCallbacks($user, $ability, array $arguments, $result
*/
protected function resolveAuthCallback($user, $ability, array $arguments)
{
if ($this->firstArgumentCorrespondsToPolicy($arguments)) {
return $this->resolvePolicyCallback($user, $ability, $arguments);
} elseif (isset($this->abilities[$ability])) {
if (isset($arguments[0])) {
$policy = $this->getPolicyFor($arguments[0]);

if ($policy !== null) {
return $this->resolvePolicyCallback($user, $ability, $arguments, $policy);
}
}

if (isset($this->abilities[$ability])) {
return $this->abilities[$ability];
} else {
return function () {
Expand All @@ -323,43 +329,23 @@ protected function resolveAuthCallback($user, $ability, array $arguments)
}
}

/**
* Determine if the first argument in the array corresponds to a policy.
*
* @param array $arguments
* @return bool
*/
protected function firstArgumentCorrespondsToPolicy(array $arguments)
{
if (! isset($arguments[0])) {
return false;
}

if (is_object($arguments[0])) {
return isset($this->policies[get_class($arguments[0])]);
}

return is_string($arguments[0]) && isset($this->policies[$arguments[0]]);
}

/**
* Resolve the callback for a policy check.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $ability
* @param array $arguments
* @param mixed $policy
* @return callable
*/
protected function resolvePolicyCallback($user, $ability, array $arguments)
protected function resolvePolicyCallback($user, $ability, array $arguments, $policy)
{
return function () use ($user, $ability, $arguments) {
$instance = $this->getPolicyFor($arguments[0]);

return function () use ($user, $ability, $arguments, $policy) {
// If we receive a non-null result from the before method, we will return it
// as the final result. This will allow developers to override the checks
// in the policy to return a result for all rules defined in the class.
if (method_exists($instance, 'before')) {
if (! is_null($result = $instance->before($user, $ability, ...$arguments))) {
if (method_exists($policy, 'before')) {
if (! is_null($result = $policy->before($user, $ability, ...$arguments))) {
return $result;
}
}
Expand All @@ -375,11 +361,11 @@ protected function resolvePolicyCallback($user, $ability, array $arguments)
array_shift($arguments);
}

if (! is_callable([$instance, $ability])) {
if (! is_callable([$policy, $ability])) {
return false;
}

return $instance->{$ability}($user, ...$arguments);
return $policy->{$ability}($user, ...$arguments);
};
}

Expand All @@ -388,20 +374,22 @@ protected function resolvePolicyCallback($user, $ability, array $arguments)
*
* @param object|string $class
* @return mixed
*
* @throws \InvalidArgumentException
*/
public function getPolicyFor($class)
{
if (is_object($class)) {
$class = get_class($class);
}

if (! isset($this->policies[$class])) {
throw new InvalidArgumentException("Policy not defined for [{$class}].");
if (isset($this->policies[$class])) {
return $this->resolvePolicy($this->policies[$class]);
}

return $this->resolvePolicy($this->policies[$class]);
foreach ($this->policies as $expected => $policy) {
if (is_subclass_of($class, $expected)) {
return $this->resolvePolicy($policy);
}
}
}

/**
Expand Down
30 changes: 29 additions & 1 deletion tests/Auth/AuthAccessGateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,24 @@ public function test_policy_classes_can_be_defined_to_handle_checks_for_given_ty
$this->assertTrue($gate->check('update', new AccessGateTestDummy));
}

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

$gate->policy(AccessGateTestDummy::class, AccessGateTestPolicy::class);

$this->assertTrue($gate->check('update', new AccessGateTestSubDummy));
}

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

$gate->policy(AccessGateTestDummyInterface::class, AccessGateTestPolicy::class);

$this->assertTrue($gate->check('update', new AccessGateTestSubDummy));
}

public function test_policy_converts_dash_to_camel()
{
$gate = $this->getBasicGate();
Expand Down Expand Up @@ -263,7 +281,17 @@ public function foo()
}
}

class AccessGateTestDummy
interface AccessGateTestDummyInterface
{
//
}

class AccessGateTestDummy implements AccessGateTestDummyInterface
{
//
}

class AccessGateTestSubDummy extends AccessGateTestDummy
{
//
}
Expand Down