rails_ops 1.5.7 → 1.6.0.rc0

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
  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