rails_ops 1.5.7 → 1.6.0.rc0

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
  SHA256:
3
- metadata.gz: 2bbc09db31ff37b510e90a5e4b1de5ba64f50a5d53461c6526eb55011cede2c1
4
- data.tar.gz: 5803d14113c57b999f7a119fa9ccf9f7e6582e0750943476b5b10ffb664b0d9b
3
+ metadata.gz: cc1e32fdbfeebc90a622cb5394dcf1d4d7b41e2d8286a45eadfec4db6f0894e8
4
+ data.tar.gz: 5fcf1149cb2b0a348a6ecdb81e7c874056aa26bac979b436c17144ee48d7094b
5
5
  SHA512:
6
- metadata.gz: ed731e2912fd87ebbef299555febefa0dbe4f1a20201f429a92ca97e2f6e17c9e6106e4d701039c1832a8cb037d1feca6c92095da05eeea8ddb783e86644058e
7
- data.tar.gz: 80c2d4ec48d3fa45ab385694acfecfccc94213e12e99107d456fbf8d0325853a19d6f6148b6da001a115a9120ff0b4cd394fd8f883985ee84693a7c1b3398095
6
+ metadata.gz: c0f97ccdac3919747d2c952805fc0e605e357e27ad8c3500f3e0e384e8063683a8031f9f8271ac3b492e1f9dca2633b57cdfad2cf81ec231b70cbeae8e54c797
7
+ data.tar.gz: f6a1e8a2eafc54e21a89f1b30b6c5d931ee90c4f352d8eed1df9f17e5f921b4da7115b2cc1d3bac35beaf0ebddfdf1cadffaaa7cd7ac7e935efb7ffcb742582d
@@ -11,7 +11,7 @@ jobs:
11
11
  - name: Set up Ruby
12
12
  uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: 3.0.1
14
+ ruby-version: 3.1.0
15
15
  bundler-cache: true
16
16
  - name: Run rubocop
17
17
  run: bundle exec rubocop
@@ -14,13 +14,13 @@ jobs:
14
14
  matrix:
15
15
  include:
16
16
  - rails-version: '6.0'
17
- ruby-version: '2.7.1'
17
+ ruby-version: '2.7.8'
18
18
  - rails-version: '6.1'
19
- ruby-version: '2.7.1'
19
+ ruby-version: '2.7.8'
20
20
  - rails-version: '6.1'
21
21
  ruby-version: '3.0.1'
22
22
  - rails-version: '7.0'
23
- ruby-version: '2.7.1'
23
+ ruby-version: '2.7.8'
24
24
  - rails-version: '7.0'
25
25
  ruby-version: '3.0.1'
26
26
  - rails-version: '7.0'
@@ -28,7 +28,7 @@ jobs:
28
28
  - rails-version: '7.0'
29
29
  ruby-version: '3.2.0'
30
30
  - rails-version: '7.1'
31
- ruby-version: '2.7.1'
31
+ ruby-version: '2.7.8'
32
32
  - rails-version: '7.1'
33
33
  ruby-version: '3.0.1'
34
34
  - rails-version: '7.1'
@@ -37,6 +37,12 @@ jobs:
37
37
  ruby-version: '3.2.0'
38
38
  - rails-version: '7.1'
39
39
  ruby-version: '3.3.0'
40
+ - rails-version: '8.0'
41
+ ruby-version: '3.2.0'
42
+ - rails-version: '8.0'
43
+ ruby-version: '3.3.0'
44
+ - rails-version: '8.0'
45
+ ruby-version: '3.4.0'
40
46
  name: Test against Ruby ${{ matrix.ruby-version }} / Rails ${{ matrix.rails-version }}
41
47
  env:
42
48
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails-version }}.gemfile
data/Appraisals CHANGED
@@ -1,3 +1,16 @@
1
+ appraise 'rails-8.0' do
2
+ gem 'rails', '~> 8.0.1'
3
+ gem 'sqlite3', '~> 2.1'
4
+ end
5
+
6
+ appraise 'rails-7.2' do
7
+ gem 'rails', '~> 7.2.1'
8
+ end
9
+
10
+ appraise 'rails-7.1' do
11
+ gem 'rails', '~> 7.1.0'
12
+ end
13
+
1
14
  appraise 'rails-7.0' do
2
15
  gem 'rails', '~> 7.0.1'
3
16
  end
@@ -9,11 +22,3 @@ end
9
22
  appraise 'rails-6.0' do
10
23
  gem 'rails', '~> 6.0.4'
11
24
  end
12
-
13
- appraise 'rails-5.2' do
14
- gem 'rails', '~> 5.2.6'
15
- end
16
-
17
- appraise 'rails-5.1' do
18
- gem 'rails', '~> 5.1.7'
19
- end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,85 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.0.rc0 (2025-01-22)
4
+
5
+ * Adapt the way model authorization works for an additional layer of security:
6
+
7
+ * Update-Operations (operations inheriting from `RailsOps::Operation::Model::Update`)
8
+ now perform their model authorization immediately after the model is loaded (in `build_model`).
9
+
10
+ Previously, the authorization was only performed after the attributes have
11
+ already been assigned, never checking authorization against the "pristine"
12
+ model.
13
+
14
+ * Load-Operations (operations inheriting from
15
+ `RailsOps::Operation::Model::Load`) now always load their associated model
16
+ **directly on OP instantiation**. Previously, this was only the case if load
17
+ model authorization was enabled.
18
+
19
+ In addition, the method `model_authorization` has been renamed to
20
+ `load_model_authorization` in order to separate it from the method
21
+ `model_authorization` in `RailsOps::Operation::Model::Update`.
22
+
23
+ Internal reference: `#133622`.
24
+
25
+ ### Migrating from earlier versions
26
+
27
+ Check that operations using model authorization still work as expected, especially
28
+ operations inheriting from `RailsOps::Operation::Model::Update` or from
29
+ `RailsOps::Operation::Model::Load`:
30
+
31
+ * For operations inheriting from `RailsOps::Operation::Model::Load`: Rename all
32
+ uses of `model_authorization` to `load_model_authorization`.
33
+
34
+ * For operations inheriting from `RailsOps::Operation::Model::Update`, you need
35
+ to make sure that running the model authorization on the "pristine" model (before
36
+ assigning the new attributes) is still applying your authorization logic in
37
+ the correct way (i.e. authorizing on the model *before* assigning the attributes
38
+ applies authorization correctly).
39
+ If you need to authorize the state *after* assigning the params to the model,
40
+ you'll need add that check manually in your operation.
41
+
42
+
43
+ One example of how this behaviour was changed: A user may only update
44
+ a `Group` object with a `color` of `'red'`:
45
+
46
+ ```ruby
47
+ class Ability
48
+ can :update, Group, color: 'red'
49
+ end
50
+ ```
51
+
52
+ Before, this was the way RailsOps handled it:
53
+
54
+ ```ruby
55
+ model = find_record(params[:id]) # => model = Group(color: 'blue')
56
+ model.assign_attributes(params) # params = { color: 'red' }
57
+ authorize! :update, model
58
+ ```
59
+
60
+ This works, because RailsOps already assigned `'red'` to the `color` attribute of the model. This means
61
+ that the `authorize!` call will succeed, even though the original model in the database is not permissible
62
+ to be updated by the user.
63
+
64
+ Afterwards, the behaviour is as follows:
65
+
66
+ ```ruby
67
+ model = find_record(params[:id]) # => model = Group(color: 'blue')
68
+ authorize! :update, model # => Fails with CanCan::AccessDenied, as the user may not update the group
69
+ # [...]
70
+ ```
71
+
72
+ After applying these changes, carefully test your application, run unit tests etc.
73
+ to ensure all operations still behave as expected in regards to authorization.
74
+
75
+ ## 1.5.8 (2024-09-11)
76
+
77
+ * Also allow single path segments as symbols instead of array for
78
+ `authorize_param`'s `path` argument. Before, paths that were not arrays would
79
+ lead to the param authorization being ignored silently.
80
+
81
+ Internal reference: `#128987`.
82
+
3
83
  ## 1.5.7 (2024-08-22)
4
84
 
5
85
  * Fix compatibility issue with older versions of Rails introduced in version
data/Gemfile CHANGED
@@ -2,3 +2,14 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rails_ops.gemspec
4
4
  gemspec
5
+
6
+ # Development dependencies should be specified in here
7
+ gem 'appraisal'
8
+ gem 'bundler'
9
+ gem 'cancancan'
10
+ gem 'pry'
11
+ gem 'rake'
12
+ gem 'rubocop', '1.70.0'
13
+ gem 'simplecov'
14
+ gem 'sprockets-rails'
15
+ gem 'sqlite3', '<2.0.0'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_ops (1.5.7)
4
+ rails_ops (1.6.0.rc0)
5
5
  active_type (>= 1.3.0)
6
6
  minitest
7
7
  rails
@@ -97,7 +97,6 @@ GEM
97
97
  builder (3.2.4)
98
98
  cancancan (3.5.0)
99
99
  coderay (1.1.3)
100
- colorize (0.8.1)
101
100
  concurrent-ruby (1.3.1)
102
101
  connection_pool (2.4.1)
103
102
  crass (1.0.6)
@@ -113,7 +112,8 @@ GEM
113
112
  irb (1.11.2)
114
113
  rdoc
115
114
  reline (>= 0.4.2)
116
- json (2.6.3)
115
+ json (2.9.1)
116
+ language_server-protocol (3.17.0.3)
117
117
  loofah (2.22.0)
118
118
  crass (~> 1.0.2)
119
119
  nokogiri (>= 1.12.0)
@@ -141,9 +141,10 @@ GEM
141
141
  racc (~> 1.4)
142
142
  nokogiri (1.16.5-x86_64-linux)
143
143
  racc (~> 1.4)
144
- parallel (1.22.1)
145
- parser (3.2.1.1)
144
+ parallel (1.26.3)
145
+ parser (3.3.7.0)
146
146
  ast (~> 2.4.1)
147
+ racc
147
148
  pry (0.14.2)
148
149
  coderay (~> 1.1)
149
150
  method_source (~> 1.0)
@@ -191,25 +192,23 @@ GEM
191
192
  rake (13.1.0)
192
193
  rdoc (6.6.3.1)
193
194
  psych (>= 4.0.0)
194
- regexp_parser (2.7.0)
195
+ regexp_parser (2.10.0)
195
196
  reline (0.4.3)
196
197
  io-console (~> 0.5)
197
198
  request_store (1.5.1)
198
199
  rack (>= 1.4)
199
- rexml (3.3.3)
200
- strscan
201
- rubocop (1.45.1)
200
+ rubocop (1.70.0)
202
201
  json (~> 2.3)
202
+ language_server-protocol (>= 3.17.0)
203
203
  parallel (~> 1.10)
204
- parser (>= 3.2.0.0)
204
+ parser (>= 3.3.0.2)
205
205
  rainbow (>= 2.2.2, < 4.0)
206
- regexp_parser (>= 1.8, < 3.0)
207
- rexml (>= 3.2.5, < 4.0)
208
- rubocop-ast (>= 1.24.1, < 2.0)
206
+ regexp_parser (>= 2.9.3, < 3.0)
207
+ rubocop-ast (>= 1.36.2, < 2.0)
209
208
  ruby-progressbar (~> 1.7)
210
- unicode-display_width (>= 2.4.0, < 3.0)
211
- rubocop-ast (1.28.0)
212
- parser (>= 3.2.1.0)
209
+ unicode-display_width (>= 2.4.0, < 4.0)
210
+ rubocop-ast (1.37.0)
211
+ parser (>= 3.3.1.0)
213
212
  ruby-progressbar (1.13.0)
214
213
  ruby2_keywords (0.0.4)
215
214
  schemacop (3.0.22)
@@ -231,12 +230,13 @@ GEM
231
230
  sqlite3 (1.6.2-x86_64-darwin)
232
231
  sqlite3 (1.6.2-x86_64-linux)
233
232
  stringio (3.1.0)
234
- strscan (3.1.0)
235
233
  thor (1.3.1)
236
234
  timeout (0.4.1)
237
235
  tzinfo (2.0.6)
238
236
  concurrent-ruby (~> 1.0)
239
- unicode-display_width (2.4.2)
237
+ unicode-display_width (3.1.4)
238
+ unicode-emoji (~> 4.0, >= 4.0.4)
239
+ unicode-emoji (4.0.4)
240
240
  webrick (1.8.1)
241
241
  websocket-driver (0.7.6)
242
242
  websocket-extensions (>= 0.1.0)
@@ -254,11 +254,10 @@ DEPENDENCIES
254
254
  appraisal
255
255
  bundler
256
256
  cancancan
257
- colorize
258
257
  pry
259
258
  rails_ops!
260
259
  rake
261
- rubocop (= 1.45.1)
260
+ rubocop (= 1.70.0)
262
261
  simplecov
263
262
  sprockets-rails
264
263
  sqlite3 (< 2.0.0)
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright © 2017-2024 Sitrox
3
+ Copyright © 2017-2025 Sitrox
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -28,12 +28,15 @@ Requirements & Installation
28
28
  * Rails 6.1.x
29
29
  * Rails 7.0.x
30
30
  * Rails 7.1.x
31
+ * Rails 7.2.x
32
+ * Rails 8.0.x
31
33
  - Additionally, the following Ruby versions are covered by our unit tests:
32
- * 2.7.1
34
+ * 2.7.8
33
35
  * 3.0.1
34
36
  * 3.1.0
35
37
  * 3.2.0
36
38
  * 3.3.0
39
+ * 3.4.0
37
40
  - Please see the [unit test workflow](https://github.com/sitrox/rails_ops/actions/workflows/ruby.yml) for the combinations of the Rails & Ruby versions, as only compatible versions are tested with each other.
38
41
  - Prior Rails and Ruby versions may be supported but they are not tested in the CI.
39
42
  - Rails Ops' model operations require ActiveRecord but are database / adapter
@@ -1352,8 +1355,9 @@ authorization check based on this model.
1352
1355
 
1353
1356
  While you can override this method to perform custom authorization, RailsOps
1354
1357
  provides a base implementation. Using the class method
1355
- `model_authorization_action`, you can specify an action verb that is used for
1356
- authorizing your model.
1358
+ `model_authorization_action` (or `load_model_authorization` for operations
1359
+ inheriting from `RailsOps::Operation::Model::Load`), you can specify an action
1360
+ verb that is used for authorizing your model.
1357
1361
 
1358
1362
  ```ruby
1359
1363
  class Operations::User::Load < RailsOps::Operation::Model::Load
@@ -1361,7 +1365,23 @@ class Operations::User::Load < RailsOps::Operation::Model::Load
1361
1365
 
1362
1366
  # This automatically calls `authorize_model! :read` after operation
1363
1367
  # instantiation.
1364
- model_authorization_action :read
1368
+ load_model_authorization :read
1369
+ end
1370
+ ```
1371
+
1372
+ Another example for an update operation:
1373
+
1374
+ ```ruby
1375
+ class Operations::User::Update < RailsOps::Operation::Model::Update
1376
+ model User
1377
+
1378
+ # This automatically calls `authorize_model! :read` after operation
1379
+ # instantiation.
1380
+ load_model_authorization :read
1381
+
1382
+ # This automatically calls `authorize_model! :update` after operation
1383
+ # instantiation.
1384
+ model_authorization :update
1365
1385
  end
1366
1386
  ```
1367
1387
 
@@ -1792,4 +1812,4 @@ Rails architecture.
1792
1812
 
1793
1813
  ## Copyright
1794
1814
 
1795
- Copyright © 2017 - 2024 Sitrox. See `LICENSE` for further details.
1815
+ Copyright © 2017 - 2025 Sitrox. See `LICENSE` for further details.
data/Rakefile CHANGED
@@ -16,16 +16,6 @@ task :gemspec do
16
16
  spec.require_paths = ['lib']
17
17
  spec.licenses = ['MIT']
18
18
 
19
- spec.add_development_dependency 'appraisal'
20
- spec.add_development_dependency 'bundler'
21
- spec.add_development_dependency 'rake'
22
- spec.add_development_dependency 'sqlite3', '<2.0.0'
23
- spec.add_development_dependency 'cancancan'
24
- spec.add_development_dependency 'pry'
25
- spec.add_development_dependency 'colorize'
26
- spec.add_development_dependency 'rubocop', '1.45.1'
27
- spec.add_development_dependency 'sprockets-rails'
28
- spec.add_development_dependency 'simplecov'
29
19
  spec.add_dependency 'active_type', '>= 1.3.0'
30
20
  spec.add_dependency 'minitest'
31
21
  spec.add_dependency 'rails'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.7
1
+ 1.6.0.rc0
@@ -2,6 +2,15 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
5
9
  gem 'rails', '~> 6.0.4'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '<2.0.0'
6
15
 
7
16
  gemspec path: '../'
@@ -2,6 +2,15 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
5
9
  gem 'rails', '~> 6.1.4'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '<2.0.0'
6
15
 
7
16
  gemspec path: '../'
@@ -2,6 +2,15 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
5
9
  gem 'rails', '~> 7.0.1'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '<2.0.0'
6
15
 
7
16
  gemspec path: '../'
@@ -2,6 +2,15 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
5
9
  gem 'rails', '~> 7.1.0'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '<2.0.0'
6
15
 
7
16
  gemspec path: '../'
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
9
+ gem 'rails', '~> 7.2.1'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '<2.0.0'
15
+
16
+ gemspec path: '../'
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'appraisal'
6
+ gem 'bundler'
7
+ gem 'cancancan'
8
+ gem 'pry'
9
+ gem 'rails', '~> 8.0.1'
10
+ gem 'rake'
11
+ gem 'rubocop', '1.70.0'
12
+ gem 'simplecov'
13
+ gem 'sprockets-rails'
14
+ gem 'sqlite3', '~> 2.1'
15
+
16
+ gemspec path: '../'
@@ -13,7 +13,7 @@ module RailsOps::Exceptions
13
13
 
14
14
  def initialize(original_exception)
15
15
  @original_exception = original_exception
16
- super original_exception.message
16
+ super(original_exception.message)
17
17
  end
18
18
  end
19
19
 
@@ -33,7 +33,7 @@ module RailsOps
33
33
  if Rails.gem_version >= Gem::Version.new('7.1')
34
34
  super(message, color, bold: bold)
35
35
  else
36
- super(message, color, bold)
36
+ super
37
37
  end
38
38
  end
39
39
  end
@@ -29,6 +29,8 @@ module RailsOps::Mixins::ParamAuthorization
29
29
  # authorization backend. The block receives no arguments and is executed
30
30
  # in context of the operation instance.
31
31
  def authorize_param(path, action = nil, *args, &block)
32
+ path = Array(path)
33
+
32
34
  # Validate parameters
33
35
  if block_given? && (action || args.any?)
34
36
  fail ArgumentError,
@@ -20,6 +20,11 @@ class RailsOps::Operation::Model::Destroy < RailsOps::Operation::Model::Load
20
20
  end
21
21
  end
22
22
 
23
+ def build_model
24
+ super
25
+ model_authorization
26
+ end
27
+
23
28
  def perform
24
29
  trigger :before_destroy, model: model
25
30
  model.destroy!
@@ -4,7 +4,7 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
4
4
  class_attribute :_lock_mode
5
5
 
6
6
  policy :on_init do
7
- model_authorization
7
+ model
8
8
  end
9
9
 
10
10
  # Gets or sets the action verb used for authorizing models on load.
@@ -18,10 +18,8 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
18
18
  return _load_model_authorization_action
19
19
  end
20
20
 
21
- def model_authorization
22
- return unless authorization_enabled?
23
-
24
- unless load_model_authorization_action.nil?
21
+ def load_model_authorization
22
+ if authorization_enabled? && load_model_authorization_action.present?
25
23
  authorize_model! load_model_authorization_action, model
26
24
  end
27
25
  end
@@ -78,12 +76,18 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
78
76
  relation = lock_relation(relation)
79
77
 
80
78
  # Fetch (and possibly lock) model
81
- return relation.find_by!(model_id_field => params[model_id_field])
79
+ model = relation.find_by!(model_id_field => params[model_id_field])
80
+
81
+ # Return model
82
+ return model
82
83
  end
83
84
 
84
85
  def build_model
85
86
  @model = find_model
86
87
 
88
+ # Perform load model authorization
89
+ load_model_authorization
90
+
87
91
  if @model.respond_to?(:parent_op=)
88
92
  @model.parent_op = self
89
93
  end
@@ -8,36 +8,44 @@ class RailsOps::Operation::Model::Update < RailsOps::Operation::Model::Load
8
8
  true
9
9
  end
10
10
 
11
- policy :before_perform do
12
- if model_authorization_action && self.class._model_authorization_lazy
13
- authorize_model! model_authorization_action, model
11
+ policy :on_init do
12
+ if self.class._model_authorization_lazy && load_model_authorization_action.nil?
13
+ fail RailsOps::Exceptions::NoAuthorizationPerformed,
14
+ "Operation #{self.class.name} must specify a " \
15
+ 'load_model_authorization_action because model ' \
16
+ 'authorization is configured to be lazy.'
14
17
  end
15
18
  end
16
19
 
17
- def model_authorization
18
- return unless authorization_enabled?
19
-
20
- if self.class._model_authorization_lazy
21
- if load_model_authorization_action.nil?
22
- fail RailsOps::Exceptions::NoAuthorizationPerformed,
23
- "Operation #{self.class.name} must specify a " \
24
- 'load_model_authorization_action because model ' \
25
- 'authorization is configured to be lazy.'
26
- else
27
- authorize_model! load_model_authorization_action, model
28
- end
29
- elsif !load_model_authorization_action.nil?
30
- authorize_model_with_authorize_only! load_model_authorization_action, model
31
- end
20
+ policy :before_perform do
21
+ # If the authorization is configured to be lazy, we need to call the authorization
22
+ # on the copy of the model that we made before assigning the new attributes.
23
+ authorize_model! model_authorization_action, @model_before_assigning_attributes if self.class._model_authorization_lazy
24
+ end
32
25
 
33
- unless model_authorization_action.nil? || self.class._model_authorization_lazy
26
+ def model_authorization
27
+ if authorization_enabled? && model_authorization_action.present?
34
28
  authorize_model! model_authorization_action, model
35
29
  end
36
30
  end
37
31
 
38
32
  def build_model
33
+ # Load model via parent class
39
34
  super
35
+
36
+ # Build nested model operations
40
37
  build_nested_model_ops :update
38
+
39
+ # Perform update authorization BEFORE assigning attributes. If the authorization is lazy,
40
+ # we copy the model before assigning the attributes, such that we can call the authorization
41
+ # later on.
42
+ if self.class._model_authorization_lazy
43
+ @model_before_assigning_attributes = @model.dup
44
+ else
45
+ model_authorization
46
+ end
47
+
48
+ # Assign attributes
41
49
  assign_attributes
42
50
  end
43
51
 
@@ -169,9 +169,8 @@ class RailsOps::Operation::Model < RailsOps::Operation
169
169
 
170
170
  protected
171
171
 
172
- # Performs nested model operations and then saves the model. If you have
173
- # nested model operations, you should call this method instead of calling
174
- # `model.save!` directly.
172
+ # Performs nested model operations and then saves the model. You should always
173
+ # call this method instead of invoking "model.save!" directly.
175
174
  def save!
176
175
  run_policies :before_nested_model_ops
177
176
  perform_nested_model_ops!
@@ -19,7 +19,7 @@ module RailsOps
19
19
  descr += ' - ' if descr
20
20
  start = Time.now
21
21
  res = yield
22
- puts "#{descr}#{((Time.now - start).to_f * 1000).round(1)}ms elapsed.".magenta
22
+ puts "#{descr}#{((Time.now - start).to_f * 1000).round(1)}ms elapsed."
23
23
  res
24
24
  end
25
25
 
data/lib/rails_ops.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'active_type'
2
2
  require 'schemacop'
3
3
  require 'request_store'
4
+ require 'ostruct'
4
5
 
5
6
  module RailsOps
6
7
  AUTH_THREAD_STORAGE_KEY = :rails_ops_authorization_enabled