{"action":"created","issue":{"url":"https://api.github.com/repos/laravel/framework/issues/16239","repository_url":"https://api.github.com/repos/laravel/framework","labels_url":"https://api.github.com/repos/laravel/framework/issues/16239/labels{/name}","comments_url":"https://api.github.com/repos/laravel/framework/issues/16239/comments","events_url":"https://api.github.com/repos/laravel/framework/issues/16239/events","html_url":"https://github.com/laravel/framework/pull/16239","id":186916970,"number":16239,"title":"[5.3] Allow Gate to explicitly check abilities for guests","user":{"login":"mmghv","id":5418859,"avatar_url":"https://avatars0.githubusercontent.com/u/5418859?v=4","gravatar_id":"","url":"https://api.github.com/users/mmghv","html_url":"https://github.com/mmghv","followers_url":"https://api.github.com/users/mmghv/followers","following_url":"https://api.github.com/users/mmghv/following{/other_user}","gists_url":"https://api.github.com/users/mmghv/gists{/gist_id}","starred_url":"https://api.github.com/users/mmghv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mmghv/subscriptions","organizations_url":"https://api.github.com/users/mmghv/orgs","repos_url":"https://api.github.com/users/mmghv/repos","events_url":"https://api.github.com/users/mmghv/events{/privacy}","received_events_url":"https://api.github.com/users/mmghv/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":23,"created_at":"2016-11-02T20:52:44Z","updated_at":"2017-08-19T03:19:06Z","closed_at":"2016-11-08T19:20:25Z","pull_request":{"url":"https://api.github.com/repos/laravel/framework/pulls/16239","html_url":"https://github.com/laravel/framework/pull/16239","diff_url":"https://github.com/laravel/framework/pull/16239.diff","patch_url":"https://github.com/laravel/framework/pull/16239.patch"},"body":"See #10568\r\n\r\nCurrently, the Gate immediately denies abilities if there is no authenticated user. This PR makes it possible to allow abilities to be checked for guests (closure abilities or abilities in policies).\r\n\r\nSay for example, We want to allow guests to comment on posts but we may change our mind in the future, So if we don't use an ability for commenting at all, Then in the future when we change our mind we have to update the views and/or controllers.\r\nBut now we can use an ability for commenting and we can mark it to be checked for guests, Then in the future when we change our mind we only have to change the ability's logic.\r\n\r\n### Closure abilities\r\n\r\nBy default, when we define an ability as normal, If no user is logged-in, it will immediately deny the ability:\r\n\r\n```PHP\r\n$gate->define('update-article', function ($user, Article $article) {\r\n    // If no user is logged-in, the guest will be denied before reaching here\r\n    return $user->id === $article->user_id;\r\n});\r\n```\r\n\r\nbut if we want to mark the ability to be checked for guests, We can pass `true` to the third parameter (`includeGuest`) :\r\n\r\n```PHP\r\n$gate->define('show-article', function ($user, Article $article) {\r\n    // If user is logged-in or not, this ability will be check because we told it so\r\n    return $user || $article->visibility == 'all';\r\n}, true);\r\n```\r\n\r\n**Note** The same applies for the `before` and `after` methods, If we want them to be called for guests we have to explicitly tell it so, Meaning that if we have an ability that is marked to be checked for guests, Then when this ability is called, It will be interrupted **only** by the `before` methods which are marked to be called with guests.\r\n\r\n**Note** In case of guest, The `$user` parameter will be `null`.\r\n\r\n### Policies\r\n\r\nFor abilities in policies, To mark any of them to be checked for guests, We can add an attribute named `$includeGuest` and include the names for the abilities that we want them to be checked for guests :\r\n\r\n```PHP\r\n// app/policies/ArticlePolicy.php\r\n\r\nnamespace App\\Policies;\r\n\r\nuse App\\Models\\Article;\r\n\r\nclass ArticlePolicy\r\n{\r\n    /**\r\n     * Array of abilities allowed to be checked for guests\r\n     *\r\n     * @var array\r\n     */\r\n    public $includeGuest = ['index', 'view'];\r\n\r\n    /**\r\n     * Ability to show index page\r\n     *\r\n     * @return bool\r\n     */\r\n    public function index()\r\n    {\r\n        return true;\r\n    }\r\n\r\n    /**\r\n     * Ability to view specific article\r\n     *\r\n     * @param  User|Null  $user\r\n     * @param  Article $article\r\n     * @return bool\r\n     */\r\n    public function view($user, Article $article)\r\n    {\r\n        // View all articles for the users, but only articles with 'all' visibility level for guests\r\n        return $user || $article->visibility == 'all';\r\n    }\r\n\r\n    /**\r\n     * Ability to create new article\r\n     *\r\n     * @param  User  $user\r\n     * @param  Article $article\r\n     * @return bool\r\n     */\r\n    public function create($user)\r\n    {\r\n        return $user->isAdmin;\r\n    }\r\n\r\n    /**\r\n     * Ability to edit specific article\r\n     *\r\n     * @param  User  $user\r\n     * @param  Article $article\r\n     * @return bool\r\n     */\r\n    public function edit($user, Article $article)\r\n    {\r\n        return $user->id === $article->user_id;\r\n    }\r\n\r\n    /**\r\n     * Ability to delete specific article\r\n     *\r\n     * @param  User  $user\r\n     * @param  Article $article\r\n     * @return bool\r\n     */\r\n    public function delete($user, Article $article)\r\n    {\r\n        return $user->id === $article->user_id;\r\n    }\r\n}\r\n```\r\n\r\n**Note** Like closure abilities, If the `before` method is not included in the `$includeGuest` attribute, It will not be called with guests.\r\n\r\n## The benefit of allowing abilities to be checked for guests\r\n\r\n* This way we can always have abilities like `index` and `view` in our policies and making them default to allow all users and guests, And change them when we want, For example if we decide to restrict some page to be only accessible for users (or admins), Then we can remove the `index` item from the `includeGuest` attribute and add some logic in the ability.\r\n\r\n* Also it will make it easy to give partial abilities for guests\r\n\r\n* And leads for good practice when letting the `Gate` responsible for all the permissions, Even if one of them is currently allowed for all users and guests and could be removed.\r\n\r\n* Also will allow (in the future) to use the abilities instead of some middlewares (like the `Auth` middleware), So we won't have to add the `Auth` middleware on the routes we want to allow only for users , This should be the responsibility of the `Gate` to determine.  "},"comment":{"url":"https://api.github.com/repos/laravel/framework/issues/comments/323496520","html_url":"https://github.com/laravel/framework/pull/16239#issuecomment-323496520","issue_url":"https://api.github.com/repos/laravel/framework/issues/16239","id":323496520,"user":{"login":"jfsullivan","id":5075028,"avatar_url":"https://avatars2.githubusercontent.com/u/5075028?v=4","gravatar_id":"","url":"https://api.github.com/users/jfsullivan","html_url":"https://github.com/jfsullivan","followers_url":"https://api.github.com/users/jfsullivan/followers","following_url":"https://api.github.com/users/jfsullivan/following{/other_user}","gists_url":"https://api.github.com/users/jfsullivan/gists{/gist_id}","starred_url":"https://api.github.com/users/jfsullivan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jfsullivan/subscriptions","organizations_url":"https://api.github.com/users/jfsullivan/orgs","repos_url":"https://api.github.com/users/jfsullivan/repos","events_url":"https://api.github.com/users/jfsullivan/events{/privacy}","received_events_url":"https://api.github.com/users/jfsullivan/received_events","type":"User","site_admin":false},"created_at":"2017-08-19T03:19:06Z","updated_at":"2017-08-19T03:19:06Z","body":"@taylorotwell I have a use case that this enhancement would help to meet.  \r\n\r\nI have multiple levels of security for a multi-tenant site where each tenant can set documents to be available to the public or to members-only using a site setting in the database.   In addition, each individual document could be set to be accessible only to certain groups of logged in users.  So in essence, the policy would need to check if the document itself is secured to a group and if not it would check the site setting.  If the site setting says \"public\" then a guest would have access but if it is \"members\" it would require a logged in user.  This is not currently possible with policies since they don't get called for guests."}}
{"actor":{"display_login":"jfsullivan"}}