├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── resources
└── js
│ └── Gate.js
├── src
├── Console
│ ├── Commands
│ │ └── JsPolicyMakeCommand.php
│ └── stubs
│ │ └── Policy.stub
├── PolicyServiceProvider.php
└── UsesModelName.php
└── tests
├── CreatesApplication.php
├── Feature
└── BladeTest.php
├── Models
├── Comment.php
└── User.php
├── TestCase.php
├── Unit
└── ModelNameTest.php
├── factories
├── CommentFactory.php
└── UserFactory.php
├── migrations
└── 2017_01_01_000000_create_comments_table.php
└── views
├── current-user.blade.php
└── custom-key.blade.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /vendor
3 | /.vscode
4 | composer.phar
5 | composer.lock
6 | Thumbs.db
7 | .phpunit.result.cache
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v0.4.1 (2021-03-02)
4 | ### Added
5 | - PHP 8 support
6 |
7 | ## v0.4.0 (2020-09-16)
8 | ### Added
9 | - Laravel 8 compatiblity
10 |
11 | ### Changed
12 | - Update test suite
13 |
14 | ## v0.3.0 (2020-03-09)
15 | ### Changed
16 | - Migrate tests, set minimum Laravel versions
17 |
18 | ## v0.2.0 (2020-01-15)
19 | ### Changed
20 | - Refactoring classes
21 | - Fix styling
22 | - Upgrade testbech version
23 | - Update license date
24 |
25 | ## v0.1.2 (2019-06-05)
26 | ### Added
27 | - publish and generate JS in the corrent folder if Laravel is lower than 5.7
28 |
29 | ## v0.1.1 (2019-06-05)
30 | ### Added
31 | - `viewAny()` policy
32 |
33 | ## v0.1.0 (2019-06-05)
34 | - Initial release
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Cone Development
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Policy
2 |
3 | Using Laravel's authorization on the front-end.
4 |
5 | A nice tool for SPAs and front-end heavy applications.
6 |
7 | If you want to see behind the package, we suggest to read this post:
8 | [Implementing Laravel’s Authorization on the Front-End](https://pineco.de/implementing-laravels-authorization-front-end/).
9 |
10 | ### Table of contents
11 |
12 | 1. [Getting Started](#getting-started)
13 | 2. [Publishing and setting up the JavaScript library](#publishing-and-setting-up-the-javascript-library)
14 | - [Setting up the Gate.js](#setting-up-the-gatejs)
15 | - [Initializing a gate instance](#initializing-a-gate-instance)
16 | - [Passing the user to the gate instance](#passing-the-user-to-the-gate-instance)
17 | - [Using it as a Vue service](#using-it-as-a-vue-service)
18 | - [The @currentUser blade directive](#the-currentuser-blade-directive)
19 | 3. [Using the policies and the Gate.js](#using-the-policies-and-the-gatejs)
20 | - [The available methods](#the-available-methods)
21 | - [Adding the UsesModelName trait to the models](#adding-the-usesmodelname-trait-to-the-models)
22 | - [Generating policies with artisan](#generating-policies-with-artisan)
23 | - [Writing the policy rules](#writing-the-policy-rules)
24 | 4. [Example](#example)
25 | 5. [Contribute](#contribute)
26 |
27 | ## Getting started
28 |
29 | You can install the package with composer, running the `composer require thepinecode/policy` command.
30 |
31 | Since the package supports auto-discovery, Laravel will register the service provider automatically behind the scenes.
32 |
33 | In some cases you may disable auto-discovery for this package.
34 | You can add the provider class to the `dont-discover` array to disable it.
35 | Then you need to register it manually again.
36 |
37 | ## Publishing and setting up the JavaScript library
38 |
39 | By default the package provides a `Gate.js` file, that will handle the policies.
40 | Use the `php artisan vendor:publish` command and choose the `Pine\Policy\PolicyServiceProvider` provider.
41 | After publishing you can find your fresh copy in the `resources/js/policies` folder if you are using Laravel 5.7+.
42 | If your application is lower than 5.7, the JS will be published in the `resources/assets/js/policies`.
43 |
44 | ### Setting up the Gate.js
45 |
46 | Then you can import the `Gate` class and assign it to the `window` object.
47 |
48 | ```js
49 | import Gate from './policies/Gate';
50 | window.Gate = Gate;
51 | ```
52 |
53 | ### Initializing a gate instance
54 |
55 | From this point you can initialize the translation service anywhere from your application.
56 |
57 | ```js
58 | let gate = new Gate;
59 | ```
60 |
61 | ### Passing the user to the gate instance
62 |
63 | The `Gate` object requires a passed user to work properly. This can be a `string` or an `object`.
64 | By default, it looks for the `window['user']` object, however you may customize the key or the object itself.
65 |
66 | ```js
67 | let gate = new Gate; // window['user']
68 |
69 | let gate = new Gate('admin'); // window['admin']
70 |
71 | let gate = new Gate({ ... }); // uses the custom object
72 | ```
73 |
74 | > Note, you can pass any object as a *user*.
75 | > If you pass a team or a group object, it works as well. Since you define the logic behind the `Gate`,
76 | > you can pass anything you wish.
77 |
78 | ### Using it as a Vue service
79 |
80 | If you want to use it from Vue templates directly you can extend Vue with this easily.
81 |
82 | ```js
83 | Vue.prototype.$Gate = new Gate;
84 | ```
85 | ```html
86 |
87 |
...
88 |
89 | ```
90 |
91 | ```js
92 | computed: {
93 | hasPermission: {
94 | return this.$Gate.allow('view', this.model);
95 | }
96 | }
97 | ```
98 |
99 | ### The @currentUser blade directive
100 |
101 | To make it quicker, the package comes with a `@currentUser` blade directive.
102 | This does nothing more, but to print the currently authenticated user as `JSON` and assign it to the `window` object.
103 |
104 | ```html
105 | @currentUser
106 |
107 |
108 |
109 | ```
110 |
111 | You may override the default key for the user. You can do that by passing a string to the blade directive.
112 |
113 | ```html
114 | @currentUser ('admin')
115 |
116 |
117 |
118 | ```
119 |
120 | > If there is no authenticated user, the value will be `null`.
121 |
122 | ## Using the policies and the Gate.js
123 |
124 | ### The available methods
125 |
126 | #### allow()
127 |
128 | The `allow()` accepts two parameters. The first is the action to perform, the second is the **model object** or the **model name**, like in Laravel.
129 |
130 | > Note: **model name** should be a lower case version of the actual model name in Laravel: for example `Comment` becomes `comment`.
131 |
132 | ```js
133 | gate.allow('view', model);
134 |
135 | gate.allow('create', 'comment');
136 | ```
137 |
138 | #### deny()
139 |
140 | The `deny()` has the same signature like `allow()` but it will negate its return value.
141 |
142 | ```js
143 | gate.deny('view', model);
144 |
145 | gate.deny('create', 'comment');
146 | ```
147 |
148 | #### before()
149 |
150 | Like in Laravel, in the `before()` method you can provide a custom logic to check for special conditions.
151 | If the condition passes, the rest of the policy rules in the `allow()` or `deny()` won't run at all.
152 | However if the condition fails, the policy rules will get place.
153 | To use the `before()` method, you may extend the gate object and define your custom logic.
154 |
155 | ```js
156 | Gate.prototype.before = function () {
157 | return this.user.is_admin;
158 | }
159 | ```
160 |
161 | > Please note, to use the `this` object correctly,
162 | > **use the traditional function signature instead of the arrow (() => {}) functions**.
163 |
164 | ### Adding the `UsesModelName` trait to the models
165 |
166 | Since, the policies use real JSON shaped eloquent models, the models have to use the `Pine\Policy\UsesModelName`
167 | trait that generates the proper model name. This model name attribute is used for pairing the proper policy with
168 | the model by the `Gate.js`.
169 |
170 | ```php
171 | use Pine\Policy\UsesModelName;
172 | use Illuminate\Database\Eloquent\Model;
173 |
174 | class Comment extends Model
175 | {
176 | use UsesModelName;
177 |
178 | protected $appends = ['model_name'];
179 | }
180 | ```
181 |
182 | > Please note, to be able to use this attribute on the front-end, the attribute has to be appended to the JSON form.
183 | > You can read more about appending values to JSON in the
184 | > [docs](https://laravel.com/docs/master/eloquent-serialization#appending-values-to-json).
185 |
186 | ### Generating policies with artisan
187 |
188 | The package comes with an artisan command by default, that helps you to generate your JavaScript policies easily.
189 | To make a policy, run the `php artisan make:js-policy Model` command, where the `Model` is the model's name.
190 |
191 | ```sh
192 | php artisan make:js-policy Comment
193 | ```
194 |
195 | This command will create the `CommentPolicy.js` file next to the `Gate.js` in the `resources/js/policies` directory.
196 | If you are using lower than Laravel 5.7, the policies will be generated in the `resources/assets/js/policies` directory.
197 |
198 | > Note, the command will append the `Policy` automatically in the file name.
199 | > It means you may pass only the model name when running the command.
200 |
201 | After you generated the policy files, use `npm` to compile all the JavaScript, including policies.
202 |
203 | ***
204 |
205 | #### Important!
206 |
207 | **The policies are registered automatically**. It means, no need for importing them manually.
208 | The gate instance will **automatically** populate the policies.
209 | Every policy will be used where it matches with the model's `model_name` attribute.
210 |
211 | Based on
212 | [Laravel's default app.js](https://github.com/laravel/laravel/blob/master/resources/js/app.js#L19-L20)
213 | the Gate instance
214 | [registers the policies automatically](https://github.com/thepinecode/policy/blob/master/resources/js/Gate.js#L14-L18)
215 | when calling `npm run dev`, `npm run prod` and so on.
216 |
217 | ***
218 |
219 | ### Writing the policy rules
220 |
221 | Policies – like in Laravel – have the following methods by default:
222 | `viewAny`, `view`, `create`, `update`, `restore`, `delete` and `forceDelete`.
223 | Of course, you can use custom methods as well, policies are fully customizables.
224 |
225 | ```js
226 | ...
227 |
228 | view(user, model)
229 | {
230 | return user.id == model.user_id;
231 | }
232 |
233 | create(user)
234 | {
235 | return user.is_admin;
236 | }
237 |
238 | approve(user, model)
239 | {
240 | return user.is_editor && user.id == model.user_id;
241 | }
242 |
243 | ...
244 | ```
245 |
246 | ## Example
247 |
248 | ```js
249 | // app.js
250 | Vue.prototype.$Gate = new Gate;
251 |
252 | Vue.component('posts', {
253 | mounted() {
254 | axios.get('/api/posts')
255 | .then(response => this.posts = response.data);
256 | },
257 | data() {
258 | return {
259 | posts: [],
260 | };
261 | },
262 | template: `
263 |