From 5c4541bc43f22b0d99c5cc6db38781060bff836f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 23 Dec 2016 10:50:32 -0600 Subject: [PATCH] Refactor the gate. --- src/Illuminate/Auth/Access/Gate.php | 125 ++++++++++++++++++---------- 1 file changed, 81 insertions(+), 44 deletions(-) diff --git a/src/Illuminate/Auth/Access/Gate.php b/src/Illuminate/Auth/Access/Gate.php index 7cb069c94ea0..a4e81e146c4f 100644 --- a/src/Illuminate/Auth/Access/Gate.php +++ b/src/Illuminate/Auth/Access/Gate.php @@ -64,7 +64,8 @@ class Gate implements GateContract * @param array $afterCallbacks * @return void */ - public function __construct(Container $container, callable $userResolver, array $abilities = [], array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = []) + public function __construct(Container $container, callable $userResolver, array $abilities = [], + array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = []) { $this->policies = $policies; $this->container = $container; @@ -225,7 +226,7 @@ public function authorize($ability, $arguments = []) } /** - * Get the raw result for the given ability for the current user. + * Get the raw result from the authorization callback. * * @param string $ability * @param array|mixed $arguments @@ -239,10 +240,20 @@ protected function raw($ability, $arguments = []) $arguments = is_array($arguments) ? $arguments : [$arguments]; - if (is_null($result = $this->callBeforeCallbacks($user, $ability, $arguments))) { + // First we will call the "before" callbacks for the Gate. If any of these give + // back a non-null response, we will immediately return that result in order + // to let the developers override all checks for some authorization cases. + $result = $this->callBeforeCallbacks( + $user, $ability, $arguments + ); + + if (is_null($result)) { $result = $this->callAuthCallback($user, $ability, $arguments); } + // After calling the authorization callback, we will call the "after" callbacks + // that are registered with the Gate, which allows a developer to do logging + // if that is required for this application. Then we'll return the result. $this->callAfterCallbacks( $user, $ability, $arguments, $result ); @@ -313,9 +324,7 @@ protected function callAfterCallbacks($user, $ability, array $arguments, $result protected function resolveAuthCallback($user, $ability, array $arguments) { if (isset($arguments[0])) { - $policy = $this->getPolicyFor($arguments[0]); - - if ($policy !== null) { + if (! is_null($policy = $this->getPolicyFor($arguments[0]))) { return $this->resolvePolicyCallback($user, $ability, $arguments, $policy); } } @@ -329,6 +338,40 @@ protected function resolveAuthCallback($user, $ability, array $arguments) } } + /** + * Get a policy instance for a given class. + * + * @param object|string $class + * @return mixed + */ + public function getPolicyFor($class) + { + if (is_object($class)) { + $class = get_class($class); + } + + if (isset($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); + } + } + } + + /** + * Build a policy class instance of the given type. + * + * @param object|string $class + * @return mixed + */ + public function resolvePolicy($class) + { + return $this->container->make($class); + } + /** * Resolve the callback for a policy check. * @@ -341,66 +384,60 @@ protected function resolveAuthCallback($user, $ability, array $arguments) protected function resolvePolicyCallback($user, $ability, array $arguments, $policy) { 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($policy, 'before')) { - if (! is_null($result = $policy->before($user, $ability, ...$arguments))) { - return $result; - } + // This callback will be responsible for calling the policy's before method and + // running this policy method if necessary. This is used to when objects are + // mapped to policy objects in the user's configurations or on this class. + $result = $this->callPolicyBefore( + $policy, $user, $ability, $arguments + ); + + // When we receive a non-null result from this before method, we will return it + // as the "final" results. This will allow developers to override the checks + // in this policy to return the result for all rules defined in the class. + if (! is_null($result)) { + return $result; } - if (strpos($ability, '-') !== false) { - $ability = Str::camel($ability); - } + $ability = $this->formatAbilityToMethod($ability); - // If the first argument is a string, that means they are passing a class name - // to the policy. We will remove the first argument from this argument list - // because the policy already knows what type of models it can authorize. + // If this first argument is a string, that means they are passing a class name + // to the policy. We will remove the first argument from this argument array + // because this policy already knows what type of models it can authorize. if (isset($arguments[0]) && is_string($arguments[0])) { array_shift($arguments); } - if (! is_callable([$policy, $ability])) { - return false; - } - - return $policy->{$ability}($user, ...$arguments); + return is_callable([$policy, $ability]) + ? $policy->{$ability}($user, ...$arguments) + : false; }; } /** - * Get a policy instance for a given class. + * Call the "before" method on the given policy, if applicable. * - * @param object|string $class + * @param mixed $policy + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string $ability + * @param array $arguments * @return mixed */ - public function getPolicyFor($class) + protected function callPolicyBefore($policy, $user, $ability, $arguments) { - if (is_object($class)) { - $class = get_class($class); - } - - if (isset($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); - } + if (method_exists($policy, 'before')) { + return $policy->before($user, $ability, ...$arguments); } } /** - * Build a policy class instance of the given type. + * Format the policy ability into a method name. * - * @param object|string $class - * @return mixed + * @param string $ability + * @return string */ - public function resolvePolicy($class) + protected function formatAbilityToMethod($ability) { - return $this->container->make($class); + return strpos($ability, '-') !== false ? Str::camel($ability) : $ability; } /**