/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php

https://gitlab.com/ealexis.t/trends · PHP · 453 lines · 200 code · 57 blank · 196 comment · 19 complexity · 901951529b55ca83adfdff589ee74be3 MD5 · raw file

  1. <?php
  2. namespace Illuminate\Auth\Access;
  3. use Illuminate\Support\Str;
  4. use InvalidArgumentException;
  5. use Illuminate\Contracts\Container\Container;
  6. use Illuminate\Contracts\Auth\Access\Gate as GateContract;
  7. class Gate implements GateContract
  8. {
  9. use HandlesAuthorization;
  10. /**
  11. * The container instance.
  12. *
  13. * @var \Illuminate\Contracts\Container\Container
  14. */
  15. protected $container;
  16. /**
  17. * The user resolver callable.
  18. *
  19. * @var callable
  20. */
  21. protected $userResolver;
  22. /**
  23. * All of the defined abilities.
  24. *
  25. * @var array
  26. */
  27. protected $abilities = [];
  28. /**
  29. * All of the defined policies.
  30. *
  31. * @var array
  32. */
  33. protected $policies = [];
  34. /**
  35. * All of the registered before callbacks.
  36. *
  37. * @var array
  38. */
  39. protected $beforeCallbacks = [];
  40. /**
  41. * All of the registered after callbacks.
  42. *
  43. * @var array
  44. */
  45. protected $afterCallbacks = [];
  46. /**
  47. * Create a new gate instance.
  48. *
  49. * @param \Illuminate\Contracts\Container\Container $container
  50. * @param callable $userResolver
  51. * @param array $abilities
  52. * @param array $policies
  53. * @param array $beforeCallbacks
  54. * @param array $afterCallbacks
  55. * @return void
  56. */
  57. public function __construct(Container $container, callable $userResolver, array $abilities = [], array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [])
  58. {
  59. $this->policies = $policies;
  60. $this->container = $container;
  61. $this->abilities = $abilities;
  62. $this->userResolver = $userResolver;
  63. $this->afterCallbacks = $afterCallbacks;
  64. $this->beforeCallbacks = $beforeCallbacks;
  65. }
  66. /**
  67. * Determine if a given ability has been defined.
  68. *
  69. * @param string $ability
  70. * @return bool
  71. */
  72. public function has($ability)
  73. {
  74. return isset($this->abilities[$ability]);
  75. }
  76. /**
  77. * Define a new ability.
  78. *
  79. * @param string $ability
  80. * @param callable|string $callback
  81. * @return $this
  82. *
  83. * @throws \InvalidArgumentException
  84. */
  85. public function define($ability, $callback)
  86. {
  87. if (is_callable($callback)) {
  88. $this->abilities[$ability] = $callback;
  89. } elseif (is_string($callback) && Str::contains($callback, '@')) {
  90. $this->abilities[$ability] = $this->buildAbilityCallback($callback);
  91. } else {
  92. throw new InvalidArgumentException("Callback must be a callable or a 'Class@method' string.");
  93. }
  94. return $this;
  95. }
  96. /**
  97. * Create the ability callback for a callback string.
  98. *
  99. * @param string $callback
  100. * @return \Closure
  101. */
  102. protected function buildAbilityCallback($callback)
  103. {
  104. return function () use ($callback) {
  105. list($class, $method) = explode('@', $callback);
  106. return call_user_func_array([$this->resolvePolicy($class), $method], func_get_args());
  107. };
  108. }
  109. /**
  110. * Define a policy class for a given class type.
  111. *
  112. * @param string $class
  113. * @param string $policy
  114. * @return $this
  115. */
  116. public function policy($class, $policy)
  117. {
  118. $this->policies[$class] = $policy;
  119. return $this;
  120. }
  121. /**
  122. * Register a callback to run before all Gate checks.
  123. *
  124. * @param callable $callback
  125. * @return $this
  126. */
  127. public function before(callable $callback)
  128. {
  129. $this->beforeCallbacks[] = $callback;
  130. return $this;
  131. }
  132. /**
  133. * Register a callback to run after all Gate checks.
  134. *
  135. * @param callable $callback
  136. * @return $this
  137. */
  138. public function after(callable $callback)
  139. {
  140. $this->afterCallbacks[] = $callback;
  141. return $this;
  142. }
  143. /**
  144. * Determine if the given ability should be granted for the current user.
  145. *
  146. * @param string $ability
  147. * @param array|mixed $arguments
  148. * @return bool
  149. */
  150. public function allows($ability, $arguments = [])
  151. {
  152. return $this->check($ability, $arguments);
  153. }
  154. /**
  155. * Determine if the given ability should be denied for the current user.
  156. *
  157. * @param string $ability
  158. * @param array|mixed $arguments
  159. * @return bool
  160. */
  161. public function denies($ability, $arguments = [])
  162. {
  163. return ! $this->allows($ability, $arguments);
  164. }
  165. /**
  166. * Determine if the given ability should be granted for the current user.
  167. *
  168. * @param string $ability
  169. * @param array|mixed $arguments
  170. * @return bool
  171. */
  172. public function check($ability, $arguments = [])
  173. {
  174. try {
  175. $result = $this->raw($ability, $arguments);
  176. } catch (AuthorizationException $e) {
  177. return false;
  178. }
  179. return (bool) $result;
  180. }
  181. /**
  182. * Determine if the given ability should be granted for the current user.
  183. *
  184. * @param string $ability
  185. * @param array|mixed $arguments
  186. * @return \Illuminate\Auth\Access\Response
  187. *
  188. * @throws \Illuminate\Auth\Access\AuthorizationException
  189. */
  190. public function authorize($ability, $arguments = [])
  191. {
  192. $result = $this->raw($ability, $arguments);
  193. if ($result instanceof Response) {
  194. return $result;
  195. }
  196. return $result ? $this->allow() : $this->deny();
  197. }
  198. /**
  199. * Get the raw result for the given ability for the current user.
  200. *
  201. * @param string $ability
  202. * @param array|mixed $arguments
  203. * @return mixed
  204. */
  205. protected function raw($ability, $arguments = [])
  206. {
  207. if (! $user = $this->resolveUser()) {
  208. return false;
  209. }
  210. $arguments = is_array($arguments) ? $arguments : [$arguments];
  211. if (is_null($result = $this->callBeforeCallbacks($user, $ability, $arguments))) {
  212. $result = $this->callAuthCallback($user, $ability, $arguments);
  213. }
  214. $this->callAfterCallbacks(
  215. $user, $ability, $arguments, $result
  216. );
  217. return $result;
  218. }
  219. /**
  220. * Resolve and call the appropriate authorization callback.
  221. *
  222. * @param \Illuminate\Contracts\Auth\Authenticatable $user
  223. * @param string $ability
  224. * @param array $arguments
  225. * @return bool
  226. */
  227. protected function callAuthCallback($user, $ability, array $arguments)
  228. {
  229. $callback = $this->resolveAuthCallback(
  230. $user, $ability, $arguments
  231. );
  232. return call_user_func_array(
  233. $callback, array_merge([$user], $arguments)
  234. );
  235. }
  236. /**
  237. * Call all of the before callbacks and return if a result is given.
  238. *
  239. * @param \Illuminate\Contracts\Auth\Authenticatable $user
  240. * @param string $ability
  241. * @param array $arguments
  242. * @return bool|null
  243. */
  244. protected function callBeforeCallbacks($user, $ability, array $arguments)
  245. {
  246. $arguments = array_merge([$user, $ability], [$arguments]);
  247. foreach ($this->beforeCallbacks as $before) {
  248. if (! is_null($result = call_user_func_array($before, $arguments))) {
  249. return $result;
  250. }
  251. }
  252. }
  253. /**
  254. * Call all of the after callbacks with check result.
  255. *
  256. * @param \Illuminate\Contracts\Auth\Authenticatable $user
  257. * @param string $ability
  258. * @param array $arguments
  259. * @param bool $result
  260. * @return void
  261. */
  262. protected function callAfterCallbacks($user, $ability, array $arguments, $result)
  263. {
  264. $arguments = array_merge([$user, $ability, $result], [$arguments]);
  265. foreach ($this->afterCallbacks as $after) {
  266. call_user_func_array($after, $arguments);
  267. }
  268. }
  269. /**
  270. * Resolve the callable for the given ability and arguments.
  271. *
  272. * @param \Illuminate\Contracts\Auth\Authenticatable $user
  273. * @param string $ability
  274. * @param array $arguments
  275. * @return callable
  276. */
  277. protected function resolveAuthCallback($user, $ability, array $arguments)
  278. {
  279. if ($this->firstArgumentCorrespondsToPolicy($arguments)) {
  280. return $this->resolvePolicyCallback($user, $ability, $arguments);
  281. } elseif (isset($this->abilities[$ability])) {
  282. return $this->abilities[$ability];
  283. } else {
  284. return function () {
  285. return false;
  286. };
  287. }
  288. }
  289. /**
  290. * Determine if the first argument in the array corresponds to a policy.
  291. *
  292. * @param array $arguments
  293. * @return bool
  294. */
  295. protected function firstArgumentCorrespondsToPolicy(array $arguments)
  296. {
  297. if (! isset($arguments[0])) {
  298. return false;
  299. }
  300. if (is_object($arguments[0])) {
  301. return isset($this->policies[get_class($arguments[0])]);
  302. }
  303. return is_string($arguments[0]) && isset($this->policies[$arguments[0]]);
  304. }
  305. /**
  306. * Resolve the callback for a policy check.
  307. *
  308. * @param \Illuminate\Contracts\Auth\Authenticatable $user
  309. * @param string $ability
  310. * @param array $arguments
  311. * @return callable
  312. */
  313. protected function resolvePolicyCallback($user, $ability, array $arguments)
  314. {
  315. return function () use ($user, $ability, $arguments) {
  316. $instance = $this->getPolicyFor($arguments[0]);
  317. if (method_exists($instance, 'before')) {
  318. // We will prepend the user and ability onto the arguments so that the before
  319. // callback can determine which ability is being called. Then we will call
  320. // into the policy before methods with the arguments and get the result.
  321. $beforeArguments = array_merge([$user, $ability], $arguments);
  322. $result = call_user_func_array(
  323. [$instance, 'before'], $beforeArguments
  324. );
  325. // If we received a non-null result from the before method, we will return it
  326. // as the result of a check. This allows developers to override the checks
  327. // in the policy and return a result for all rules defined in the class.
  328. if (! is_null($result)) {
  329. return $result;
  330. }
  331. }
  332. if (strpos($ability, '-') !== false) {
  333. $ability = Str::camel($ability);
  334. }
  335. if (! is_callable([$instance, $ability])) {
  336. return false;
  337. }
  338. return call_user_func_array(
  339. [$instance, $ability], array_merge([$user], $arguments)
  340. );
  341. };
  342. }
  343. /**
  344. * Get a policy instance for a given class.
  345. *
  346. * @param object|string $class
  347. * @return mixed
  348. *
  349. * @throws \InvalidArgumentException
  350. */
  351. public function getPolicyFor($class)
  352. {
  353. if (is_object($class)) {
  354. $class = get_class($class);
  355. }
  356. if (! isset($this->policies[$class])) {
  357. throw new InvalidArgumentException("Policy not defined for [{$class}].");
  358. }
  359. return $this->resolvePolicy($this->policies[$class]);
  360. }
  361. /**
  362. * Build a policy class instance of the given type.
  363. *
  364. * @param object|string $class
  365. * @return mixed
  366. */
  367. public function resolvePolicy($class)
  368. {
  369. return $this->container->make($class);
  370. }
  371. /**
  372. * Get a guard instance for the given user.
  373. *
  374. * @param \Illuminate\Contracts\Auth\Authenticatable|mixed $user
  375. * @return static
  376. */
  377. public function forUser($user)
  378. {
  379. $callback = function () use ($user) {
  380. return $user;
  381. };
  382. return new static(
  383. $this->container, $callback, $this->abilities,
  384. $this->policies, $this->beforeCallbacks, $this->afterCallbacks
  385. );
  386. }
  387. /**
  388. * Resolve the user from the user resolver.
  389. *
  390. * @return mixed
  391. */
  392. protected function resolveUser()
  393. {
  394. return call_user_func($this->userResolver);
  395. }
  396. }