jsonapi-authorization 0.8.2 → 1.0.0.alpha1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 829e3b94fc7a796418ffcb274fd822886bc8d7f3
4
- data.tar.gz: e0280fef8a27a4c12b8d8550deab9dae76fbceb2
3
+ metadata.gz: 50a06e5fdcd473f0bcd5ea8b1881d11933476d6c
4
+ data.tar.gz: 4bef789a4172b7c93a0ee20092e2da33fe2704dd
5
5
  SHA512:
6
- metadata.gz: 5ffaeb2569e05bb6e3eabb0aae2b28d26ccc12129c1015224cb4e0834fb8ff7ca9012106e0e38377e22a62ef38cbc7695dbc3cbe6543f206c9b58347d724e26e
7
- data.tar.gz: d515f2990a4529a755133d8c21d713bc0fa5e8542fb4de5ac1e9d1d251c9a593f34bbd8426b1bc4d675a94135da62d196c48d6c5dbb9673337f95131e490590d
6
+ metadata.gz: 36ae7c80e93eb5cc1f07be3f14869723982419681cd15f6d71b760d78a53305f3824b90f1edd71fc1dd4c8fd26f04ae51a0a26e9e92abb1396ff5944f06a7718
7
+ data.tar.gz: 0f91ba98fc0774febee9634c7e0ee28bbf0cd7f9e69703dd0b5b7f0f1e48132ffd446d0e9be4a6c1f8b7797e699b49bcb4b37b6bf989fef27c05dc523ff31bf2
@@ -0,0 +1,109 @@
1
+ {
2
+ "projectName": "jsonapi-authorization",
3
+ "projectOwner": "Venuu",
4
+ "files": [
5
+ "README.md"
6
+ ],
7
+ "imageSize": 100,
8
+ "commit": true,
9
+ "contributors": [
10
+ {
11
+ "login": "valscion",
12
+ "name": "Vesa Laakso",
13
+ "avatar_url": "https://avatars.githubusercontent.com/u/482561?v=3",
14
+ "profile": "http://vesalaakso.com",
15
+ "contributions": [
16
+ "code",
17
+ "doc",
18
+ "infra",
19
+ "test",
20
+ "bug",
21
+ "question",
22
+ "review"
23
+ ]
24
+ },
25
+ {
26
+ "login": "lime",
27
+ "name": "Emil Sågfors",
28
+ "avatar_url": "https://avatars.githubusercontent.com/u/562204?v=3",
29
+ "profile": "https://github.com/lime",
30
+ "contributions": [
31
+ "code",
32
+ "doc",
33
+ "infra",
34
+ "test",
35
+ "bug",
36
+ "question",
37
+ "review"
38
+ ]
39
+ },
40
+ {
41
+ "login": "matthias-g",
42
+ "name": "Matthias Grundmann",
43
+ "avatar_url": "https://avatars.githubusercontent.com/u/1591161?v=3",
44
+ "profile": "https://github.com/matthias-g",
45
+ "contributions": [
46
+ "code",
47
+ "doc",
48
+ "test",
49
+ "question"
50
+ ]
51
+ },
52
+ {
53
+ "login": "thibaudgg",
54
+ "name": "Thibaud Guillaume-Gentil",
55
+ "avatar_url": "https://avatars.githubusercontent.com/u/1322?v=3",
56
+ "profile": "http://thibaud.gg",
57
+ "contributions": [
58
+ "code"
59
+ ]
60
+ },
61
+ {
62
+ "login": "acid",
63
+ "name": "Daniel Schweighöfer",
64
+ "avatar_url": "https://avatars.githubusercontent.com/u/71660?v=3",
65
+ "profile": "http://netsteward.net",
66
+ "contributions": [
67
+ "code"
68
+ ]
69
+ },
70
+ {
71
+ "login": "bsofiato",
72
+ "name": "Bruno Sofiato",
73
+ "avatar_url": "https://avatars.githubusercontent.com/u/5076967?v=3",
74
+ "profile": "https://github.com/bsofiato",
75
+ "contributions": [
76
+ "code"
77
+ ]
78
+ },
79
+ {
80
+ "login": "arcreative",
81
+ "name": "Adam Robertson",
82
+ "avatar_url": "https://avatars.githubusercontent.com/u/1896026?v=3",
83
+ "profile": "https://github.com/arcreative",
84
+ "contributions": [
85
+ "doc"
86
+ ]
87
+ },
88
+ {
89
+ "login": "gnfisher",
90
+ "name": "Greg Fisher",
91
+ "avatar_url": "https://avatars3.githubusercontent.com/u/4742306?v=3",
92
+ "profile": "https://github.com/gnfisher",
93
+ "contributions": [
94
+ "code",
95
+ "test"
96
+ ]
97
+ },
98
+ {
99
+ "login": "handlers",
100
+ "name": "Sam",
101
+ "avatar_url": "https://avatars3.githubusercontent.com/u/370182?v=3",
102
+ "profile": "http://samlh.com",
103
+ "contributions": [
104
+ "code",
105
+ "test"
106
+ ]
107
+ }
108
+ ]
109
+ }
data/.travis.yml CHANGED
@@ -2,18 +2,13 @@ language: ruby
2
2
  cache: bundler
3
3
  sudo: false
4
4
  env:
5
- - JSONAPI_RESOURCES_VERSION=0.8.0.beta1 RAILS_VERSION=4.1.0
6
- - JSONAPI_RESOURCES_VERSION=0.8.0.beta1 RAILS_VERSION=4.2.0
7
- - JSONAPI_RESOURCES_VERSION=master RAILS_VERSION=4.2.0
8
- - JSONAPI_RESOURCES_VERSION=master RAILS_VERSION=4.1.0
5
+ - JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=4.1.0
6
+ - JSONAPI_RESOURCES_VERSION=0.9 RAILS_VERSION=4.2.0
9
7
  rvm:
10
8
  - 2.1.2
11
9
  before_install: gem install bundler -v 1.11.2
12
10
  notifications:
13
11
  email: false
14
- matrix:
15
- allow_failures:
16
- - env: JSONAPI_RESOURCES_VERSION=master RAILS_VERSION=4.2.0
17
- - env: JSONAPI_RESOURCES_VERSION=master RAILS_VERSION=4.1.0
18
12
  script:
19
13
  - ./bin/phare
14
+ - bundle exec rake
data/Gemfile CHANGED
@@ -18,10 +18,8 @@ else
18
18
  end
19
19
 
20
20
  case jsonapi_resources_version
21
- when 'master'
22
- gem 'jsonapi-resources', git: 'https://github.com/cerebris/jsonapi-resources.git'
23
21
  when 'default'
24
- gem 'jsonapi-resources', '0.8.0.beta1'
22
+ gem 'jsonapi-resources', '0.9'
25
23
  else
26
24
  gem 'jsonapi-resources', jsonapi_resources_version
27
25
  end
data/README.md CHANGED
@@ -91,6 +91,39 @@ class BaseResource < JSONAPI::Resource
91
91
  end
92
92
  ```
93
93
 
94
+ ### Policies
95
+
96
+ To check whether an action is allowed JSONAPI::Authorization calls the respective actions of your pundit policies
97
+ (`index?`, `show?`, `create?`, `update?`, `destroy?`).
98
+
99
+ For relationship operations by default `update?` is being called for all affected resources.
100
+ For a finer grained control you can define `add_to_<relation>?`, `replace_<relation>?`, and `remove_from_<relation>?`
101
+ as the following example shows.
102
+
103
+ ```ruby
104
+ class ArticlePolicy
105
+
106
+ # (...)
107
+
108
+ def add_to_comments?(new_comments)
109
+ record.published && new_comments.all? { |comment| comment.author == user }
110
+ end
111
+
112
+ def replace_comments?(new_comments)
113
+ allowed = record.comments.all? { |comment| new_comments.include?(comment) || add_to_comments?([comment])}
114
+ allowed && new_comments.all? { |comment| record.comments.include?(comment) || remove_from_comments?(comment) }
115
+ end
116
+
117
+ def remove_from_comments?(comment)
118
+ comment.author == user || user.admin?
119
+ end
120
+ end
121
+ ```
122
+
123
+ Caveat: In case a relationship is modifiable through multiple ways it is your responsibility to ensure consistency.
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.
126
+
94
127
  ## Configuration
95
128
 
96
129
  You can use a custom authorizer class by specifying a configure block in an initializer file. If using a custom authorizer class, be sure to require them at the top of the initializer before usage.
@@ -138,3 +171,15 @@ Originally based on discussion and code samples by [@barelyknown](https://github
138
171
  Bug reports and pull requests are welcome on GitHub at https://github.com/venuu/jsonapi-authorization.
139
172
 
140
173
  [issues]: https://github.com/venuu/jsonapi-authorization/issues
174
+
175
+ ## Contributors
176
+
177
+ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
178
+
179
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
180
+ | [<img src="https://avatars.githubusercontent.com/u/482561?v=3" width="100px;"/><br /><sub>Vesa Laakso</sub>](http://vesalaakso.com)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion) [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion) 🚇 [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion) [🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Avalscion) 💬 👀 | [<img src="https://avatars.githubusercontent.com/u/562204?v=3" width="100px;"/><br /><sub>Emil Sågfors</sub>](https://github.com/lime)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=lime) [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=lime) 🚇 [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=lime) [🐛](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Alime) 💬 👀 | [<img src="https://avatars.githubusercontent.com/u/1591161?v=3" width="100px;"/><br /><sub>Matthias Grundmann</sub>](https://github.com/matthias-g)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g) [📖](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g) [⚠️](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g) 💬 | [<img src="https://avatars.githubusercontent.com/u/1322?v=3" width="100px;"/><br /><sub>Thibaud Guillaume-Gentil</sub>](http://thibaud.gg)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=thibaudgg) | [<img src="https://avatars.githubusercontent.com/u/71660?v=3" width="100px;"/><br /><sub>Daniel Schweighöfer</sub>](http://netsteward.net)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=acid) | [<img src="https://avatars.githubusercontent.com/u/5076967?v=3" width="100px;"/><br /><sub>Bruno Sofiato</sub>](https://github.com/bsofiato)<br />[💻](https://github.com/Venuu/jsonapi-authorization/commits?author=bsofiato) | [<img src="https://avatars.githubusercontent.com/u/1896026?v=3" width="100px;"/><br /><sub>Adam Robertson</sub>](https://github.com/arcreative)<br />[📖](https://github.com/Venuu/jsonapi-authorization/commits?author=arcreative) |
181
+ | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
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) |
183
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
184
+
185
+ This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "jsonapi-resources", "~> 0.8.0.beta1"
20
+ spec.add_dependency "jsonapi-resources", "~> 0.9"
21
21
  spec.add_dependency "pundit", "~> 1.0"
22
22
 
23
23
  spec.add_development_dependency "bundler", "~> 1.11"
@@ -12,10 +12,10 @@ module JSONAPI
12
12
  set_callback :remove_resource, :before, :authorize_remove_resource
13
13
  set_callback :replace_fields, :before, :authorize_replace_fields
14
14
  set_callback :replace_to_one_relationship, :before, :authorize_replace_to_one_relationship
15
- set_callback :create_to_many_relationship, :before, :authorize_create_to_many_relationship
16
- set_callback :replace_to_many_relationship, :before, :authorize_replace_to_many_relationship
17
- set_callback :remove_to_many_relationship, :before, :authorize_remove_to_many_relationship
18
15
  set_callback :remove_to_one_relationship, :before, :authorize_remove_to_one_relationship
16
+ set_callback :create_to_many_relationships, :before, :authorize_create_to_many_relationships
17
+ set_callback :replace_to_many_relationships, :before, :authorize_replace_to_many_relationships
18
+ set_callback :remove_to_many_relationships, :before, :authorize_remove_to_many_relationships
19
19
 
20
20
  [
21
21
  :find,
@@ -107,13 +107,11 @@ module JSONAPI
107
107
  params[:resource_id],
108
108
  context: context
109
109
  )._model
110
-
111
- authorizer.replace_fields(source_record, related_models)
110
+ authorizer.replace_fields(source_record, related_models_with_context)
112
111
  end
113
112
 
114
113
  def authorize_create_resource
115
- source_class = @resource_klass._model_class
116
-
114
+ source_class = resource_klass._model_class
117
115
  authorizer.create_resource(source_class, related_models)
118
116
  end
119
117
 
@@ -127,91 +125,95 @@ module JSONAPI
127
125
  end
128
126
 
129
127
  def authorize_replace_to_one_relationship
128
+ return authorize_remove_to_one_relationship if params[:key_value].nil?
129
+
130
130
  source_resource = @resource_klass.find_by_key(
131
131
  params[:resource_id],
132
132
  context: context
133
133
  )
134
134
  source_record = source_resource._model
135
135
 
136
- old_related_record = source_resource.records_for(params[:relationship_type].to_sym)
137
- unless params[:key_value].nil?
138
- new_related_resource = @resource_klass
139
- ._relationship(params[:relationship_type].to_sym)
140
- .resource_klass
141
- .find_by_key(
142
- params[:key_value],
143
- context: context
144
- )
145
- new_related_record = new_related_resource._model unless new_related_resource.nil?
146
- end
136
+ relationship_type = params[:relationship_type].to_sym
137
+ new_related_resource = @resource_klass
138
+ ._relationship(relationship_type)
139
+ .resource_klass
140
+ .find_by_key(
141
+ params[:key_value],
142
+ context: context
143
+ )
144
+ new_related_record = new_related_resource._model unless new_related_resource.nil?
147
145
 
148
146
  authorizer.replace_to_one_relationship(
149
147
  source_record,
150
- old_related_record,
151
- new_related_record
148
+ new_related_record,
149
+ relationship_type
152
150
  )
153
151
  end
154
152
 
155
- def authorize_create_to_many_relationship
153
+ def authorize_create_to_many_relationships
156
154
  source_record = @resource_klass.find_by_key(
157
155
  params[:resource_id],
158
156
  context: context
159
157
  )._model
160
158
 
161
- related_models =
162
- model_class_for_relationship(params[:relationship_type].to_sym).find(params[:data])
159
+ relationship_type = params[:relationship_type].to_sym
160
+ related_models = model_class_for_relationship(relationship_type).find(params[:data])
163
161
 
164
- authorizer.create_to_many_relationship(source_record, related_models)
162
+ authorizer.create_to_many_relationship(source_record, related_models, relationship_type)
165
163
  end
166
164
 
167
- def authorize_replace_to_many_relationship
165
+ def authorize_replace_to_many_relationships
168
166
  source_resource = @resource_klass.find_by_key(
169
167
  params[:resource_id],
170
168
  context: context
171
169
  )
172
170
  source_record = source_resource._model
173
171
 
174
- related_records = source_resource.records_for(params[:relationship_type].to_sym)
172
+ relationship_type = params[:relationship_type].to_sym
173
+ new_related_records = model_class_for_relationship(relationship_type).find(params[:data])
175
174
 
176
175
  authorizer.replace_to_many_relationship(
177
176
  source_record,
178
- related_records
177
+ new_related_records,
178
+ relationship_type
179
179
  )
180
180
  end
181
181
 
182
- def authorize_remove_to_many_relationship
182
+ def authorize_remove_to_many_relationships
183
183
  source_resource = @resource_klass.find_by_key(
184
184
  params[:resource_id],
185
185
  context: context
186
186
  )
187
187
  source_record = source_resource._model
188
188
 
189
- related_resource = @resource_klass
190
- ._relationship(params[:relationship_type].to_sym)
189
+ relationship_type = params[:relationship_type].to_sym
190
+
191
+ related_resources = @resource_klass
192
+ ._relationship(relationship_type)
191
193
  .resource_klass
192
- .find_by_key(
193
- params[:associated_key],
194
+ .find_by_keys(
195
+ params[:associated_keys],
194
196
  context: context
195
197
  )
196
- related_record = related_resource._model unless related_resource.nil?
198
+
199
+ related_records = related_resources.map(&:_model)
197
200
 
198
201
  authorizer.remove_to_many_relationship(
199
202
  source_record,
200
- related_record
203
+ related_records,
204
+ relationship_type
201
205
  )
202
206
  end
203
207
 
204
208
  def authorize_remove_to_one_relationship
205
- source_resource = @resource_klass.find_by_key(
209
+ source_record = @resource_klass.find_by_key(
206
210
  params[:resource_id],
207
211
  context: context
208
- )
212
+ )._model
209
213
 
210
- related_resource = source_resource.public_send(params[:relationship_type].to_sym)
214
+ relationship_type = params[:relationship_type].to_sym
211
215
 
212
- source_record = source_resource._model
213
- related_record = related_resource._model unless related_resource.nil?
214
- authorizer.remove_to_one_relationship(source_record, related_record)
216
+ authorizer.remove_to_one_relationship(source_record, relationship_type)
215
217
  end
216
218
 
217
219
  private
@@ -259,6 +261,34 @@ module JSONAPI
259
261
  end
260
262
  end
261
263
 
264
+ def related_models_with_context
265
+ data = params[:data]
266
+ return { relationship: nil, relation_name: nil, records: nil } if data.nil?
267
+
268
+ [:to_one, :to_many].flat_map do |rel_type|
269
+ data[rel_type].flat_map do |assoc_name, assoc_value|
270
+ related_models =
271
+ case assoc_value
272
+ when Hash # polymorphic relationship
273
+ resource_class = @resource_klass.resource_for(assoc_value[:type].to_s)
274
+ resource_class.find_by_key(assoc_value[:id], context: context)._model
275
+ when Array
276
+ resource_class = resource_class_for_relationship(assoc_name)
277
+ resource_class.find_by_keys(assoc_value, context: context).map(&:_model)
278
+ else
279
+ resource_class = resource_class_for_relationship(assoc_name)
280
+ resource_class.find_by_key(assoc_value[:id], context: context)._model
281
+ end
282
+
283
+ {
284
+ relation_type: rel_type,
285
+ relation_name: assoc_name,
286
+ records: related_models
287
+ }
288
+ end
289
+ end
290
+ end
291
+
262
292
  def authorize_model_includes(source_record)
263
293
  if params[:include_directives]
264
294
  params[:include_directives].model_includes.each do |include_item|
@@ -86,17 +86,11 @@ module JSONAPI
86
86
  # ==== Parameters
87
87
  #
88
88
  # * +source_record+ - The record to be modified
89
- # * +new_related_records+ - An array of records to be associated to the
90
- # +source_record+. This will contain the records specified in the
91
- # "relationships" key in the request
92
- #--
93
- # TODO: Should probably take old records as well
94
- def replace_fields(source_record, new_related_records)
89
+ # * +related_records_with_context+ - A hash with the association type,
90
+ # the relationship name, an Array of new related records.
91
+ def replace_fields(source_record, related_records_with_context)
95
92
  ::Pundit.authorize(user, source_record, 'update?')
96
-
97
- new_related_records.each do |record|
98
- ::Pundit.authorize(user, record, 'update?')
99
- end
93
+ authorize_related_records(source_record, related_records_with_context)
100
94
  end
101
95
 
102
96
  # <tt>POST /resources</tt>
@@ -131,11 +125,11 @@ module JSONAPI
131
125
  # ==== Parameters
132
126
  #
133
127
  # * +source_record+ - The record whose relationship is modified
134
- # * +old_related_record+ - The current associated record
135
- # * +new_related_record+ - The new record replacing the +old_record+
136
- # association, or +nil+ if the association is to be cleared
137
- def replace_to_one_relationship(_source_record, _old_related_record, _new_related_record)
138
- raise NotImplementedError
128
+ # * +new_related_record+ - The new record replacing the old record
129
+ # * +relationship_type+ - The relationship type
130
+ def replace_to_one_relationship(source_record, new_related_record, relationship_type)
131
+ relationship_method = "replace_#{relationship_type}?"
132
+ authorize_relationship_operation(source_record, relationship_method, new_related_record)
139
133
  end
140
134
 
141
135
  # <tt>POST /resources/:id/relationships/other-resources</tt>
@@ -146,8 +140,10 @@ module JSONAPI
146
140
  #
147
141
  # * +source_record+ - The record whose relationship is modified
148
142
  # * +new_related_records+ - The new records to be added to the association
149
- def create_to_many_relationship(_source_record, _new_related_records)
150
- raise NotImplementedError
143
+ # * +relationship_type+ - The relationship type
144
+ def create_to_many_relationship(source_record, new_related_records, relationship_type)
145
+ relationship_method = "add_to_#{relationship_type}?"
146
+ authorize_relationship_operation(source_record, relationship_method, new_related_records)
151
147
  end
152
148
 
153
149
  # <tt>PATCH /resources/:id/relationships/other-resources</tt>
@@ -159,36 +155,37 @@ module JSONAPI
159
155
  # * +source_record+ - The record whose relationship is modified
160
156
  # * +new_related_records+ - The new records replacing the entire +has_many+
161
157
  # association
162
- #--
163
- # TODO: Should probably take old records as well
164
- def replace_to_many_relationship(_source_record, _new_related_records)
165
- raise NotImplementedError
158
+ # * +relationship_type+ - The relationship type
159
+ def replace_to_many_relationship(source_record, new_related_records, relationship_type)
160
+ relationship_method = "replace_#{relationship_type}?"
161
+ authorize_relationship_operation(source_record, relationship_method, new_related_records)
166
162
  end
167
163
 
168
164
  # <tt>DELETE /resources/:id/relationships/other-resources</tt>
169
165
  #
170
- # A request to deassociate elements of a +has_many+ association
171
- #
172
- # NOTE: this is called once per related record, not all at once
166
+ # A request to disassociate elements of a +has_many+ association
173
167
  #
174
168
  # ==== Parameters
175
169
  #
176
170
  # * +source_record+ - The record whose relationship is modified
177
- # * +related_record+ - The record which will be deassociatied from +source_record+
178
- def remove_to_many_relationship(_source_record, _related_record)
179
- raise NotImplementedError
171
+ # * +related_records+ - The records which will be disassociated from +source_record+
172
+ # * +relationship_type+ - The relationship type
173
+ def remove_to_many_relationship(source_record, related_records, relationship_type)
174
+ relationship_method = "remove_from_#{relationship_type}?"
175
+ authorize_relationship_operation(source_record, relationship_method, related_records)
180
176
  end
181
177
 
182
178
  # <tt>DELETE /resources/:id/relationships/another-resource</tt>
183
179
  #
184
- # A request to deassociate a +has_one+ association
180
+ # A request to disassociate a +has_one+ association
185
181
  #
186
182
  # ==== Parameters
187
183
  #
188
184
  # * +source_record+ - The record whose relationship is modified
189
- # * +related_record+ - The record which will be deassociatied from +source_record+
190
- def remove_to_one_relationship(_source_record, _related_record)
191
- raise NotImplementedError
185
+ # * +relationship_type+ - The relationship type
186
+ def remove_to_one_relationship(source_record, relationship_type)
187
+ relationship_method = "remove_#{relationship_type}?"
188
+ authorize_relationship_operation(source_record, relationship_method)
192
189
  end
193
190
 
194
191
  # Any request including <tt>?include=other-resources</tt>
@@ -224,6 +221,36 @@ module JSONAPI
224
221
  def include_has_one_resource(_source_record, related_record)
225
222
  ::Pundit.authorize(user, related_record, 'show?')
226
223
  end
224
+
225
+ private
226
+
227
+ def authorize_relationship_operation(source_record, relationship_method, *args)
228
+ policy = ::Pundit.policy(user, source_record)
229
+ if policy.respond_to?(relationship_method)
230
+ unless policy.public_send(relationship_method, *args)
231
+ raise ::Pundit::NotAuthorizedError,
232
+ query: relationship_method,
233
+ record: source_record,
234
+ policy: policy
235
+ end
236
+ else
237
+ ::Pundit.authorize(user, source_record, 'update?')
238
+ end
239
+ end
240
+
241
+ def authorize_related_records(source_record, related_records_with_context)
242
+ related_records_with_context.each do |data|
243
+ relation_type = data[:relation_type]
244
+ relation_name = data[:relation_name]
245
+ records = data[:records]
246
+ case relation_type
247
+ when :to_many
248
+ replace_to_many_relationship(source_record, records, relation_name)
249
+ when :to_one
250
+ replace_to_one_relationship(source_record, records, relation_name)
251
+ end
252
+ end
253
+ end
227
254
  end
228
255
  end
229
256
  end
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Authorization
3
- VERSION = "0.8.2".freeze
3
+ VERSION = "1.0.0.alpha1".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-authorization
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 1.0.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vesa Laakso
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-02-03 00:00:00.000000000 Z
12
+ date: 2017-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jsonapi-resources
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: 0.8.0.beta1
20
+ version: '0.9'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: 0.8.0.beta1
27
+ version: '0.9'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: pundit
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -187,6 +187,7 @@ executables: []
187
187
  extensions: []
188
188
  extra_rdoc_files: []
189
189
  files:
190
+ - ".all-contributorsrc"
190
191
  - ".editorconfig"
191
192
  - ".gitignore"
192
193
  - ".rspec"
@@ -224,9 +225,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
225
  version: '0'
225
226
  required_rubygems_version: !ruby/object:Gem::Requirement
226
227
  requirements:
227
- - - ">="
228
+ - - ">"
228
229
  - !ruby/object:Gem::Version
229
- version: '0'
230
+ version: 1.3.1
230
231
  requirements: []
231
232
  rubyforge_project:
232
233
  rubygems_version: 2.2.2