jsonapi-authorization 0.8.2 → 1.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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