jsonapi-authorization 1.0.0.alpha6 → 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.all-contributorsrc +13 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +7 -5
- data/Gemfile +0 -2
- data/README.md +18 -11
- data/docs/relationship-authorization.md +601 -0
- data/jsonapi-authorization.gemspec +7 -6
- data/lib/jsonapi/authorization/authorizing_processor.rb +42 -26
- data/lib/jsonapi/authorization/default_pundit_authorizer.rb +66 -29
- data/lib/jsonapi/authorization/version.rb +2 -1
- metadata +38 -24
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcd1ac1cfc1372b4ffeeabb8e1ae5bab6cf48da3
|
4
|
+
data.tar.gz: 13de8f3337c711b0454b5f1ca953cf99bd77be8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba7c459632d6809cc751358a1e2943e3ef7b03c646305133eb2e7129ae59b529edfc7ced44d3d6249c34f85f38f74e70ddb6ccfe96403d709cbf22f5ed8d5e55
|
7
|
+
data.tar.gz: 76aa072e610ad4949ede3da5b9264bbc53a3e5c4527bf2078cf6cdd428d24dc1fb9229ed548fba7221d5cfd1b16a2b90c2cc38cbe2b21eb3a4f4c7d3e828be58
|
data/.all-contributorsrc
CHANGED
@@ -115,6 +115,18 @@
|
|
115
115
|
"code",
|
116
116
|
"test"
|
117
117
|
]
|
118
|
+
},
|
119
|
+
{
|
120
|
+
"login": "nruth",
|
121
|
+
"name": "Nicholas Rutherford",
|
122
|
+
"avatar_url": "https://avatars1.githubusercontent.com/u/26158?v=4",
|
123
|
+
"profile": "http://www.google.co.uk/profiles/nick.rutherford",
|
124
|
+
"contributions": [
|
125
|
+
"code",
|
126
|
+
"test",
|
127
|
+
"infra"
|
128
|
+
]
|
118
129
|
}
|
119
|
-
]
|
130
|
+
],
|
131
|
+
"repoType": "github"
|
120
132
|
}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
sudo: false
|
4
2
|
env:
|
5
|
-
- JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=4.1.0
|
6
3
|
- JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=4.2.0
|
4
|
+
- JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=5.0.0
|
5
|
+
- JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=5.1.0
|
6
|
+
- JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=5.2.0
|
7
|
+
install:
|
8
|
+
- bundle install
|
7
9
|
rvm:
|
8
|
-
- 2.
|
9
|
-
before_install: gem install bundler -v 1.
|
10
|
+
- 2.3
|
11
|
+
before_install: gem install bundler -v 1.16.2
|
10
12
|
notifications:
|
11
13
|
email: false
|
12
14
|
script:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -14,17 +14,26 @@ branch. This may contain information that is not relevant to the release you are
|
|
14
14
|
[jr]: https://github.com/cerebris/jsonapi-resources "A resource-focused Rails library for developing JSON API compliant servers."
|
15
15
|
[pundit]: https://github.com/elabs/pundit "Minimal authorization through OO design and pure Ruby classes"
|
16
16
|
|
17
|
+
The core design principle of `JSONAPI::Authorization` is:
|
18
|
+
|
19
|
+
**Prefer being overly restrictive rather than too permissive by accident.**
|
20
|
+
|
21
|
+
What follows is that we want to have:
|
22
|
+
|
23
|
+
1. Whitelist over blacklist -approach for authorization
|
24
|
+
2. Fall back on a more strict authorization
|
25
|
+
|
17
26
|
## Caveats
|
18
27
|
|
19
28
|
Make sure to test for authorization in your application, too. We should have coverage of all operations, though. If that isn't the case, please [open an issue][issues].
|
20
29
|
|
21
30
|
If you're using custom processors, make sure that they extend `JSONAPI::Authorization::AuthorizingProcessor`, or authorization will not be performed for that resource.
|
22
31
|
|
23
|
-
This gem should work out-of-the box for simple cases. The default authorizer might be overly restrictive for
|
32
|
+
This gem should work out-of-the box for simple cases. The default authorizer might be overly restrictive for cases where you are touching relationships.
|
24
33
|
|
25
|
-
|
34
|
+
**If you are modifying relationships**, you should read the [relationship authorization documentation](docs/relationship-authorization.md).
|
26
35
|
|
27
|
-
|
36
|
+
The API is subject to change between minor version bumps until we reach v1.0.0.
|
28
37
|
|
29
38
|
## Installation
|
30
39
|
|
@@ -97,8 +106,7 @@ To check whether an action is allowed JSONAPI::Authorization calls the respectiv
|
|
97
106
|
(`index?`, `show?`, `create?`, `update?`, `destroy?`).
|
98
107
|
|
99
108
|
For relationship operations by default `update?` is being called for all affected resources.
|
100
|
-
For a finer grained control you can define
|
101
|
-
as the following example shows.
|
109
|
+
For a finer grained control you can define methods to authorize relationship changes. For example:
|
102
110
|
|
103
111
|
```ruby
|
104
112
|
class ArticlePolicy
|
@@ -120,9 +128,7 @@ class ArticlePolicy
|
|
120
128
|
end
|
121
129
|
```
|
122
130
|
|
123
|
-
|
124
|
-
For example if you have a many-to-many relationship with users and projects make sure that
|
125
|
-
`ProjectPolicy#add_to_users?(users)` and `UserPolicy#add_to_projects?(projects)` match up.
|
131
|
+
For thorough documentation about custom policy methods, check out the [relationship authorization docs](docs/relationship-authorization.md).
|
126
132
|
|
127
133
|
## Configuration
|
128
134
|
|
@@ -177,9 +183,10 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/venuu/
|
|
177
183
|
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
|
178
184
|
|
179
185
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
180
|
-
|
186
|
+
<!-- prettier-ignore -->
|
187
|
+
| [<img src="https://avatars.githubusercontent.com/u/482561?v=3" width="100px;"/><br /><sub><b>Vesa Laakso</b></sub>](http://vesalaakso.com)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Code") [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Documentation") [🚇](#infra-valscion "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Tests") [🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Avalscion "Bug reports") [💬](#question-valscion "Answering Questions") [👀](#review-valscion "Reviewed Pull Requests") | [<img src="https://avatars.githubusercontent.com/u/562204?v=3" width="100px;"/><br /><sub><b>Emil Sågfors</b></sub>](https://github.com/lime)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Code") [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Documentation") [🚇](#infra-lime "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Tests") [🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Alime "Bug reports") [💬](#question-lime "Answering Questions") [👀](#review-lime "Reviewed Pull Requests") | [<img src="https://avatars.githubusercontent.com/u/1591161?v=3" width="100px;"/><br /><sub><b>Matthias Grundmann</b></sub>](https://github.com/matthias-g)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Code") [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Documentation") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Tests") [💬](#question-matthias-g "Answering Questions") | [<img src="https://avatars.githubusercontent.com/u/1322?v=3" width="100px;"/><br /><sub><b>Thibaud Guillaume-Gentil</b></sub>](http://thibaud.gg)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=thibaudgg "Code") | [<img src="https://avatars.githubusercontent.com/u/71660?v=3" width="100px;"/><br /><sub><b>Daniel Schweighöfer</b></sub>](http://netsteward.net)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=acid "Code") | [<img src="https://avatars.githubusercontent.com/u/5076967?v=3" width="100px;"/><br /><sub><b>Bruno Sofiato</b></sub>](https://github.com/bsofiato)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=bsofiato "Code") | [<img src="https://avatars.githubusercontent.com/u/1896026?v=3" width="100px;"/><br /><sub><b>Adam Robertson</b></sub>](https://github.com/arcreative)<br />[📖](https://github.com/Venuu/jsonapi-authorization/commits?author=arcreative "Documentation") |
|
181
188
|
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
182
|
-
| [<img src="https://avatars3.githubusercontent.com/u/4742306?v=3" width="100px;"/><br /><sub>Greg Fisher</sub>](https://github.com/gnfisher)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher) [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher) | [<img src="https://avatars3.githubusercontent.com/u/370182?v=3" width="100px;"/><br /><sub>Sam</sub>](http://samlh.com)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers) [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers) | [<img src="https://avatars0.githubusercontent.com/u/2738630?v=3" width="100px;"/><br /><sub>Justas Palumickas</sub>](https://jpalumickas.com)<br />[🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Ajpalumickas) [💻](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas) [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas) |
|
189
|
+
| [<img src="https://avatars3.githubusercontent.com/u/4742306?v=3" width="100px;"/><br /><sub><b>Greg Fisher</b></sub>](https://github.com/gnfisher)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher "Code") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher "Tests") | [<img src="https://avatars3.githubusercontent.com/u/370182?v=3" width="100px;"/><br /><sub><b>Sam</b></sub>](http://samlh.com)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers "Code") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers "Tests") | [<img src="https://avatars0.githubusercontent.com/u/2738630?v=3" width="100px;"/><br /><sub><b>Justas Palumickas</b></sub>](https://jpalumickas.com)<br />[🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Ajpalumickas "Bug reports") [💻](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas "Code") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas "Tests") | [<img src="https://avatars1.githubusercontent.com/u/26158?v=4" width="100px;"/><br /><sub><b>Nicholas Rutherford</b></sub>](http://www.google.co.uk/profiles/nick.rutherford)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=nruth "Code") [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=nruth "Tests") [🚇](#infra-nruth "Infrastructure (Hosting, Build-Tools, etc)") |
|
183
190
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
184
191
|
|
185
|
-
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
192
|
+
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
@@ -0,0 +1,601 @@
|
|
1
|
+
# <a name="doc-top"></a>Authorization of operations touching relationships
|
2
|
+
|
3
|
+
`JSONAPI::Authorization` (JA) is unique in the way it considers relationship changes to change the underlying models. Whenever an incoming request changes associated resources, JA will authorize those operations are OK.
|
4
|
+
|
5
|
+
As JA runs the authorization checks _before_ any changes are made (even to in-memory objects), Pundit policies don't have the information needed to authorize changes to relationships. This is why JA provides special hooks to authorize relationship changes and falls back to checking `#update?` on all the related records.
|
6
|
+
|
7
|
+
Caveat: In case a relationship is modifiable through multiple ways it is your responsibility to ensure consistency.
|
8
|
+
For example if you have a many-to-many relationship with users and projects make sure that
|
9
|
+
`ProjectPolicy#add_to_users?(users)` and `UserPolicy#add_to_projects?(projects)` match up.
|
10
|
+
|
11
|
+
**Table of contents**
|
12
|
+
|
13
|
+
* `has-one` relationships
|
14
|
+
- [Example setup for `has-one` examples](#example-setup-has-one)
|
15
|
+
- [`PATCH /articles/article-1/relationships/author`](#change-has-one-relationship-op)
|
16
|
+
* Changing a `has-one` relationship
|
17
|
+
- [`DELETE /articles/article-1/relationships/author`](#remove-has-one-relationship-op)
|
18
|
+
* Removing a `has-one` relationship
|
19
|
+
- [`PATCH /articles/article-1/` with different `author` relationship](#change-and-replace-has-one-resource-op)
|
20
|
+
* Changing resource and replacing a `has-one` relationship
|
21
|
+
- [`PATCH /articles/article-1/` with null `author` relationship](#change-and-remove-has-one-resource-op)
|
22
|
+
* Changing resource and removing a `has-one` relationship
|
23
|
+
- [`POST /articles` with an `author` relationship](#create-has-one-resource-op)
|
24
|
+
* Creating a resource with a `has-one` relationship
|
25
|
+
* `has-many` relationships
|
26
|
+
- [Example setup for `has-many` examples](#example-setup-has-many)
|
27
|
+
- [`POST /articles/article-1/relationships/comments`](#add-to-has-many-relationship-op)
|
28
|
+
* Adding to a `has-many` relationship
|
29
|
+
- [`DELETE /articles/article-1/relationships/comments`](#remove-from-has-many-relationship-op)
|
30
|
+
* Removing from a `has-many` relationship
|
31
|
+
- [`PATCH /articles/article-1/relationships/comments` with different `comments`](#replace-has-many-relationship-op)
|
32
|
+
* Replacing a `has-many` relationship
|
33
|
+
- [`PATCH /articles/article-1/relationships/comments` with empty `comments`](#remove-has-many-relationship-op)
|
34
|
+
* Removing a `has-many` relationship
|
35
|
+
- [`PATCH /articles/article-1` with different `comments` relationship](#change-and-replace-has-many-resource-op)
|
36
|
+
* Changing resource and replacing a `has-many` relationship
|
37
|
+
- [`PATCH /articles/article-1` with empty `comments` relationship](#change-and-remove-has-many-resource-op)
|
38
|
+
* Changing resource and removing a `has-many` relationship
|
39
|
+
- [`POST /articles` with a `comments` relationship](#create-has-many-resource-op)
|
40
|
+
* Creating a resource with a `has-many` relationship
|
41
|
+
|
42
|
+
|
43
|
+
<a name="example-setup-has-one"></a>
|
44
|
+
|
45
|
+
[back to top ↑](#doc-top)
|
46
|
+
|
47
|
+
## `has-one` relationships
|
48
|
+
|
49
|
+
### Example setup for `has-one` examples
|
50
|
+
|
51
|
+
The examples for `has-one` relationship authorization use these models and resources:
|
52
|
+
|
53
|
+
```rb
|
54
|
+
class Article < ActiveRecord::Base
|
55
|
+
belongs_to :author, class_name: 'User'
|
56
|
+
end
|
57
|
+
|
58
|
+
class ArticleResource < JSONAPI::Resource
|
59
|
+
include JSONAPI::Authorization::PunditScopedResource
|
60
|
+
has_one :author, class_name: 'User'
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
```rb
|
65
|
+
class User < ActiveRecord::Base
|
66
|
+
has_many :articles, foreign_key: :author_id
|
67
|
+
end
|
68
|
+
|
69
|
+
class UserResource < JSONAPI::Resource
|
70
|
+
include JSONAPI::Authorization::PunditScopedResource
|
71
|
+
has_many :articles
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
<a name="change-has-one-relationship-op"></a>
|
76
|
+
|
77
|
+
[back to top ↑](#doc-top)
|
78
|
+
|
79
|
+
### `PATCH /articles/article-1/relationships/author`
|
80
|
+
|
81
|
+
_Changing a `has-one` relationship with a relationship operation_
|
82
|
+
|
83
|
+
Setup:
|
84
|
+
|
85
|
+
```rb
|
86
|
+
user_1 = User.create(id: 'user-1')
|
87
|
+
article_1 = Article.create(id: 'article-1', author: user_1)
|
88
|
+
user_2 = User.create(id: 'user-2')
|
89
|
+
```
|
90
|
+
|
91
|
+
> `PATCH /articles/article-1/relationships/author`
|
92
|
+
>
|
93
|
+
> ```json
|
94
|
+
> {
|
95
|
+
> "type": "users",
|
96
|
+
> "id": "user-2"
|
97
|
+
> }
|
98
|
+
> ```
|
99
|
+
|
100
|
+
#### Custom relationship authorization method
|
101
|
+
|
102
|
+
* `ArticlePolicy.new(current_user, article_1).replace_author?(user_2)`
|
103
|
+
|
104
|
+
#### Fallback
|
105
|
+
|
106
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
107
|
+
* `UserPolicy.new(current_user, user_2).update?`
|
108
|
+
|
109
|
+
**Note:** Currently JA does not fallback to authorizing `UserPolicy#update?` on `user_1` that is about to be dissociated. This will likely be changed in the future.
|
110
|
+
|
111
|
+
<a name="remove-has-one-relationship-op"></a>
|
112
|
+
|
113
|
+
[back to top ↑](#doc-top)
|
114
|
+
|
115
|
+
### `DELETE /articles/article-1/relationships/author`
|
116
|
+
|
117
|
+
_Removing a `has-one` relationship with a relationship operation_
|
118
|
+
|
119
|
+
Setup:
|
120
|
+
|
121
|
+
```rb
|
122
|
+
user_1 = User.create(id: 'user-1')
|
123
|
+
article_1 = Article.create(id: 'article-1', author: user_1)
|
124
|
+
```
|
125
|
+
|
126
|
+
> `DELETE /articles/article-1/relationships/author`
|
127
|
+
>
|
128
|
+
> (empty body)
|
129
|
+
|
130
|
+
#### Custom relationship authorization method
|
131
|
+
|
132
|
+
* `ArticlePolicy.new(current_user, article_1).remove_author?`
|
133
|
+
|
134
|
+
#### Fallback
|
135
|
+
|
136
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
137
|
+
|
138
|
+
**Note:** Currently JA does not fallback to authorizing `UserPolicy#update?` on `user_1` that is about to be dissociated. This will likely be changed in the future.
|
139
|
+
|
140
|
+
<a name="change-and-replace-has-one-resource-op"></a>
|
141
|
+
|
142
|
+
[back to top ↑](#doc-top)
|
143
|
+
|
144
|
+
### `PATCH /articles/article-1/` with different `author` relationship
|
145
|
+
|
146
|
+
_Changing resource and replacing a `has-one` relationship_
|
147
|
+
|
148
|
+
Setup:
|
149
|
+
|
150
|
+
```rb
|
151
|
+
user_1 = User.create(id: 'user-1')
|
152
|
+
article_1 = Article.create(id: 'article-1', author: user_1)
|
153
|
+
user_2 = User.create(id: 'user-2')
|
154
|
+
```
|
155
|
+
|
156
|
+
> `PATCH /articles/article-1`
|
157
|
+
>
|
158
|
+
> ```json
|
159
|
+
> {
|
160
|
+
> "type": "articles",
|
161
|
+
> "id": "article-1",
|
162
|
+
> "relationships": {
|
163
|
+
> "author": {
|
164
|
+
> "data": {
|
165
|
+
> "type": "users",
|
166
|
+
> "id": "user-2"
|
167
|
+
> }
|
168
|
+
> }
|
169
|
+
> }
|
170
|
+
> }
|
171
|
+
> ```
|
172
|
+
|
173
|
+
#### Always calls
|
174
|
+
|
175
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
176
|
+
|
177
|
+
#### Custom relationship authorization method
|
178
|
+
|
179
|
+
* `ArticlePolicy.new(current_user, article_1).replace_author?(user_2)`
|
180
|
+
|
181
|
+
#### Fallback
|
182
|
+
|
183
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
184
|
+
* `UserPolicy.new(current_user, user_2).update?`
|
185
|
+
|
186
|
+
**Note:** Currently JA does not fallback to authorizing `UserPolicy#update?` on `user_1` that is about to be dissociated. This will likely be changed in the future.
|
187
|
+
|
188
|
+
<a name="change-and-remove-has-one-resource-op"></a>
|
189
|
+
|
190
|
+
[back to top ↑](#doc-top)
|
191
|
+
|
192
|
+
### `PATCH /articles/article-1/` with null `author` relationship
|
193
|
+
|
194
|
+
_Changing resource and removing a `has-one` relationship_
|
195
|
+
|
196
|
+
Setup:
|
197
|
+
|
198
|
+
```rb
|
199
|
+
user_1 = User.create(id: 'user-1')
|
200
|
+
article_1 = Article.create(id: 'article-1', author: user_1)
|
201
|
+
```
|
202
|
+
|
203
|
+
> `PATCH /articles/article-1`
|
204
|
+
>
|
205
|
+
> ```json
|
206
|
+
> {
|
207
|
+
> "type": "articles",
|
208
|
+
> "id": "article-1",
|
209
|
+
> "relationships": {
|
210
|
+
> "author": {
|
211
|
+
> "data": null
|
212
|
+
> }
|
213
|
+
> }
|
214
|
+
> }
|
215
|
+
> ```
|
216
|
+
|
217
|
+
#### Always calls
|
218
|
+
|
219
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
220
|
+
|
221
|
+
#### Custom relationship authorization method
|
222
|
+
|
223
|
+
* `ArticlePolicy.new(current_user, article_1).remove_author?`
|
224
|
+
|
225
|
+
#### Fallback
|
226
|
+
|
227
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
228
|
+
|
229
|
+
**Note:** Currently JA does not fallback to authorizing `UserPolicy#update?` on `user_1` that is about to be dissociated. This will likely be changed in the future.
|
230
|
+
|
231
|
+
<a name="create-has-one-resource-op"></a>
|
232
|
+
|
233
|
+
[back to top ↑](#doc-top)
|
234
|
+
|
235
|
+
### `POST /articles` with an `author` relationship
|
236
|
+
|
237
|
+
_Creating a resource with a `has-one` relationship_
|
238
|
+
|
239
|
+
Setup:
|
240
|
+
|
241
|
+
```rb
|
242
|
+
user_1 = User.create(id: 'user-1')
|
243
|
+
```
|
244
|
+
|
245
|
+
> `POST /articles`
|
246
|
+
>
|
247
|
+
> ```json
|
248
|
+
> {
|
249
|
+
> "type": "articles",
|
250
|
+
> "relationships": {
|
251
|
+
> "author": {
|
252
|
+
> "data": {
|
253
|
+
> "type": "users",
|
254
|
+
> "id": "user-1"
|
255
|
+
> }
|
256
|
+
> }
|
257
|
+
> }
|
258
|
+
> }
|
259
|
+
> ```
|
260
|
+
|
261
|
+
#### Always calls
|
262
|
+
|
263
|
+
* `ArticlePolicy.new(current_user, Article).create?`
|
264
|
+
|
265
|
+
**Note:** The second parameter for the policy is the `Article` _class_, not the new record. This is because JA runs the authorization checks _before_ any changes are made, even changes to in-memory objects.
|
266
|
+
|
267
|
+
#### Custom relationship authorization method
|
268
|
+
|
269
|
+
* `ArticlePolicy.new(current_user, Article).create_with_author?(user_1)`
|
270
|
+
|
271
|
+
#### Fallback
|
272
|
+
|
273
|
+
* `UserPolicy.new(current_user, user_1).update?`
|
274
|
+
|
275
|
+
|
276
|
+
<a name="example-setup-has-many"></a>
|
277
|
+
|
278
|
+
[back to top ↑](#doc-top)
|
279
|
+
|
280
|
+
## `has-many` relationships
|
281
|
+
|
282
|
+
### Example setup for `has-many` examples
|
283
|
+
|
284
|
+
The examples for `has-many` relationship authorization use these models and resources:
|
285
|
+
|
286
|
+
```rb
|
287
|
+
class Article < ActiveRecord::Base
|
288
|
+
has_many :comments
|
289
|
+
end
|
290
|
+
|
291
|
+
class ArticleResource < JSONAPI::Resource
|
292
|
+
include JSONAPI::Authorization::PunditScopedResource
|
293
|
+
# `acts_as_set` allows replacing all comments at once
|
294
|
+
has_many :comments, acts_as_set: true
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
```rb
|
299
|
+
class Comment < ActiveRecord::Base
|
300
|
+
belongs_to :article
|
301
|
+
end
|
302
|
+
|
303
|
+
class CommentResource < JSONAPI::Resource
|
304
|
+
include JSONAPI::Authorization::PunditScopedResource
|
305
|
+
has_one :article
|
306
|
+
end
|
307
|
+
```
|
308
|
+
|
309
|
+
<a name="add-to-has-many-relationship-op"></a>
|
310
|
+
|
311
|
+
[back to top ↑](#doc-top)
|
312
|
+
|
313
|
+
### `POST /articles/article-1/relationships/comments`
|
314
|
+
|
315
|
+
_Adding to a `has-many` relationship_
|
316
|
+
|
317
|
+
Setup:
|
318
|
+
|
319
|
+
```rb
|
320
|
+
comment_1 = Comment.create(id: 'comment-1')
|
321
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1])
|
322
|
+
comment_2 = Comment.create(id: 'comment-2')
|
323
|
+
comment_3 = Comment.create(id: 'comment-3')
|
324
|
+
```
|
325
|
+
|
326
|
+
> `POST /articles/article-1/relationships/comments`
|
327
|
+
>
|
328
|
+
> ```json
|
329
|
+
> {
|
330
|
+
> "data": [
|
331
|
+
> { "type": "comments", "id": "comment-2" },
|
332
|
+
> { "type": "comments", "id": "comment-3" }
|
333
|
+
> ]
|
334
|
+
> }
|
335
|
+
> ```
|
336
|
+
|
337
|
+
#### Custom relationship authorization method
|
338
|
+
|
339
|
+
* `ArticlePolicy.new(current_user, article_1).add_to_comments?([comment_2, comment_3])`
|
340
|
+
|
341
|
+
#### Fallback
|
342
|
+
|
343
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
344
|
+
* `CommentPolicy.new(current_user, comment_2).update?`
|
345
|
+
* `CommentPolicy.new(current_user, comment_3).update?`
|
346
|
+
|
347
|
+
<a name="remove-from-has-many-relationship-op"></a>
|
348
|
+
|
349
|
+
[back to top ↑](#doc-top)
|
350
|
+
|
351
|
+
### `DELETE /articles/article-1/relationships/comments`
|
352
|
+
|
353
|
+
_Removing from a `has-many` relationship_
|
354
|
+
|
355
|
+
Setup:
|
356
|
+
|
357
|
+
```rb
|
358
|
+
comment_1 = Comment.create(id: 'comment-1')
|
359
|
+
comment_2 = Comment.create(id: 'comment-2')
|
360
|
+
comment_3 = Comment.create(id: 'comment-3')
|
361
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1, comment_2, comment_3])
|
362
|
+
```
|
363
|
+
|
364
|
+
> `DELETE /articles/article-1/relationships/comments`
|
365
|
+
>
|
366
|
+
> ```json
|
367
|
+
> {
|
368
|
+
> "data": [
|
369
|
+
> { "type": "comments", "id": "comment-1" },
|
370
|
+
> { "type": "comments", "id": "comment-2" }
|
371
|
+
> ]
|
372
|
+
> }
|
373
|
+
> ```
|
374
|
+
|
375
|
+
#### Custom relationship authorization method
|
376
|
+
|
377
|
+
* `ArticlePolicy.new(current_user, article_1).remove_from_comments?([comment_1, comment_2])`
|
378
|
+
|
379
|
+
#### Fallback
|
380
|
+
|
381
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
382
|
+
* `CommentPolicy.new(current_user, comment_1).update?`
|
383
|
+
* `CommentPolicy.new(current_user, comment_2).update?`
|
384
|
+
|
385
|
+
<a name="replace-has-many-relationship-op"></a>
|
386
|
+
|
387
|
+
[back to top ↑](#doc-top)
|
388
|
+
|
389
|
+
### `PATCH /articles/article-1/relationships/comments` with different `comments`
|
390
|
+
|
391
|
+
_Replacing a `has-many` relationship_
|
392
|
+
|
393
|
+
Setup:
|
394
|
+
|
395
|
+
```rb
|
396
|
+
comment_1 = Comment.create(id: 'comment-1')
|
397
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1])
|
398
|
+
comment_2 = Comment.create(id: 'comment-2')
|
399
|
+
comment_3 = Comment.create(id: 'comment-3')
|
400
|
+
```
|
401
|
+
|
402
|
+
> `PATCH /articles/article-1/relationships/comments`
|
403
|
+
>
|
404
|
+
> ```json
|
405
|
+
> {
|
406
|
+
> "data": [
|
407
|
+
> { "type": "comments", "id": "comment-2" },
|
408
|
+
> { "type": "comments", "id": "comment-3" }
|
409
|
+
> ]
|
410
|
+
> }
|
411
|
+
> ```
|
412
|
+
|
413
|
+
#### Custom relationship authorization method
|
414
|
+
|
415
|
+
* `ArticlePolicy.new(current_user, article_1).replace_comments?([comment_2, comment_3])`
|
416
|
+
|
417
|
+
#### Fallback
|
418
|
+
|
419
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
420
|
+
* `CommentPolicy.new(current_user, comment_2).update?`
|
421
|
+
* `CommentPolicy.new(current_user, comment_3).update?`
|
422
|
+
|
423
|
+
**Note:** Currently JA does not fallback to authorizing `CommentPolicy#update?` on `comment_1` that is about to be dissociated. This will likely be changed in the future.
|
424
|
+
|
425
|
+
<a name="remove-has-many-relationship-op"></a>
|
426
|
+
|
427
|
+
[back to top ↑](#doc-top)
|
428
|
+
|
429
|
+
### `PATCH /articles/article-1/relationships/comments` with empty `comments`
|
430
|
+
|
431
|
+
_Removing a `has-many` relationship_
|
432
|
+
|
433
|
+
Setup:
|
434
|
+
|
435
|
+
```rb
|
436
|
+
comment_1 = Comment.create(id: 'comment-1')
|
437
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1])
|
438
|
+
```
|
439
|
+
|
440
|
+
> `PATCH /articles/article-1/relationships/comments`
|
441
|
+
>
|
442
|
+
> ```json
|
443
|
+
> {
|
444
|
+
> "data": []
|
445
|
+
> }
|
446
|
+
> ```
|
447
|
+
|
448
|
+
#### Custom relationship authorization method
|
449
|
+
|
450
|
+
* `ArticlePolicy.new(current_user, article_1).replace_comments?([])`
|
451
|
+
|
452
|
+
**TODO:** We should probably call `remove_comments?` (with no arguments) instead. See https://github.com/venuu/jsonapi-authorization/issues/73 for more details and implementation progress.
|
453
|
+
|
454
|
+
#### Fallback
|
455
|
+
|
456
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
457
|
+
|
458
|
+
**Note:** Currently JA does not fallback to authorizing `CommentPolicy#update?` on `comment_1` that is about to be dissociated. This will likely be changed in the future.
|
459
|
+
|
460
|
+
<a name="change-and-replace-has-many-resource-op"></a>
|
461
|
+
|
462
|
+
[back to top ↑](#doc-top)
|
463
|
+
|
464
|
+
### `PATCH /articles/article-1` with different `comments` relationship
|
465
|
+
|
466
|
+
_Changing resource and replacing a `has-many` relationship_
|
467
|
+
|
468
|
+
Setup:
|
469
|
+
|
470
|
+
```rb
|
471
|
+
comment_1 = Comment.create(id: 'comment-1')
|
472
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1])
|
473
|
+
comment_2 = Comment.create(id: 'comment-2')
|
474
|
+
comment_3 = Comment.create(id: 'comment-3')
|
475
|
+
```
|
476
|
+
|
477
|
+
> `PATCH /articles/article-1`
|
478
|
+
>
|
479
|
+
> ```json
|
480
|
+
> {
|
481
|
+
> "type": "articles",
|
482
|
+
> "id": "article-1",
|
483
|
+
> "relationships": {
|
484
|
+
> "comments": {
|
485
|
+
> "data": [
|
486
|
+
> { "type": "comments", "id": "comment-2" },
|
487
|
+
> { "type": "comments", "id": "comment-3" }
|
488
|
+
> ]
|
489
|
+
> }
|
490
|
+
> }
|
491
|
+
> }
|
492
|
+
> ```
|
493
|
+
|
494
|
+
#### Always calls
|
495
|
+
|
496
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
497
|
+
|
498
|
+
#### Custom relationship authorization method
|
499
|
+
|
500
|
+
* `ArticlePolicy.new(current_user, article_1).replace_comments?([comment_2, comment_3])`
|
501
|
+
|
502
|
+
#### Fallback
|
503
|
+
|
504
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
505
|
+
* `CommentPolicy.new(current_user, comment_2).update?`
|
506
|
+
* `CommentPolicy.new(current_user, comment_3).update?`
|
507
|
+
|
508
|
+
**Note:** Currently JA does not fallback to authorizing `CommentPolicy#update?` on `comment_1` that is about to be dissociated. This will likely be changed in the future.
|
509
|
+
|
510
|
+
<a name="change-and-remove-has-many-resource-op"></a>
|
511
|
+
|
512
|
+
[back to top ↑](#doc-top)
|
513
|
+
|
514
|
+
### `PATCH /articles/article-1` with empty `comments` relationship
|
515
|
+
|
516
|
+
_Changing resource and removing a `has-many` relationship_
|
517
|
+
|
518
|
+
Setup:
|
519
|
+
|
520
|
+
```rb
|
521
|
+
comment_1 = Comment.create(id: 'comment-1')
|
522
|
+
article_1 = Article.create(id: 'article-1', comments: [comment_1])
|
523
|
+
```
|
524
|
+
|
525
|
+
> `PATCH /articles/article-1`
|
526
|
+
>
|
527
|
+
> ```json
|
528
|
+
> {
|
529
|
+
> "type": "articles",
|
530
|
+
> "id": "article-1",
|
531
|
+
> "relationships": {
|
532
|
+
> "comments": {
|
533
|
+
> "data": []
|
534
|
+
> }
|
535
|
+
> }
|
536
|
+
> }
|
537
|
+
> ```
|
538
|
+
|
539
|
+
#### Always calls
|
540
|
+
|
541
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
542
|
+
|
543
|
+
#### Custom relationship authorization method
|
544
|
+
|
545
|
+
* `ArticlePolicy.new(current_user, article_1).replace_comments?([])`
|
546
|
+
|
547
|
+
**TODO:** We should probably call `remove_comments?` (with no arguments) instead. See https://github.com/venuu/jsonapi-authorization/issues/73 for more details and implementation progress.
|
548
|
+
|
549
|
+
#### Fallback
|
550
|
+
|
551
|
+
* `ArticlePolicy.new(current_user, article_1).update?`
|
552
|
+
|
553
|
+
**Note:** Currently JA does not fallback to authorizing `CommentPolicy#update?` on `comment_1` that is about to be dissociated. This will likely be changed in the future.
|
554
|
+
|
555
|
+
<a name="create-has-many-resource-op"></a>
|
556
|
+
|
557
|
+
[back to top ↑](#doc-top)
|
558
|
+
|
559
|
+
### `POST /articles` with a `comments` relationship
|
560
|
+
|
561
|
+
_Creating a resource with a `has-many` relationship_
|
562
|
+
|
563
|
+
Setup:
|
564
|
+
|
565
|
+
```rb
|
566
|
+
comment_1 = Comment.create(id: 'comment-1')
|
567
|
+
comment_2 = Comment.create(id: 'comment-2')
|
568
|
+
```
|
569
|
+
|
570
|
+
> `POST /articles`
|
571
|
+
>
|
572
|
+
> ```json
|
573
|
+
> {
|
574
|
+
> "type": "articles",
|
575
|
+
> "relationships": {
|
576
|
+
> "comments": {
|
577
|
+
> "data": [
|
578
|
+
> { "type": "comments", "id": "comment-1" },
|
579
|
+
> { "type": "comments", "id": "comment-2" }
|
580
|
+
> ]
|
581
|
+
> }
|
582
|
+
> }
|
583
|
+
> }
|
584
|
+
> ```
|
585
|
+
|
586
|
+
#### Always calls
|
587
|
+
|
588
|
+
* `ArticlePolicy.new(current_user, Article).create?`
|
589
|
+
|
590
|
+
**Note:** The second parameter for the policy is the `Article` _class_, not the new record. This is because JA runs the authorization checks _before_ any changes are made, even changes to in-memory objects.
|
591
|
+
|
592
|
+
#### Custom relationship authorization method
|
593
|
+
|
594
|
+
* `ArticlePolicy.new(current_user, Article).create_with_comments?([comment_1, comment_2])`
|
595
|
+
|
596
|
+
#### Fallback
|
597
|
+
|
598
|
+
* `CommentPolicy.new(current_user, comment_1).update?`
|
599
|
+
* `CommentPolicy.new(current_user, comment_2).update?`
|
600
|
+
|
601
|
+
[back to top ↑](#doc-top)
|