declarative_policy 2.0.1 → 2.1.0

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: 704af6c0500c00a0e6c6797dd5b496c098db86f33ccc1ef2496be37e43b33e03
4
- data.tar.gz: 2236adb02dbee28b6565fc72541fe2fbee5a087ef15cc2fe1f0d060ce1eece13
3
+ metadata.gz: 05f875265a827bd025947cd0098d09804051cf0fa2c02857aa230e03d8554f02
4
+ data.tar.gz: 38452f2308ad61d9cf54bb604f68cbdd73cb75d342ea24a55c265ee708f588b4
5
5
  SHA512:
6
- metadata.gz: c3841495e1922ae72f704524e40ca080da4838cfe075d401350212f5b31f116f6003aa3e371d2a0084a11986b4745bb82946ad84b61247a19b59470a26e24755
7
- data.tar.gz: f3f0d97ba2b9e56079d5307c135ec2b15c73759661f32e6cda2db44ab0fa21002598725cf0c762ca85c0580ae1c5b7c397115f185ec94c9da600cfaf19f6e590
6
+ metadata.gz: 82ac859206da667fb45b59662aa9a35fb760f59179e0976f9e7ed9ffba92259ba271fa6732ec9b84e3e14e14961f1362fb13077deb7191c75436c83ef60ab342
7
+ data.tar.gz: c9e41debc20ab2ecc2dc8da44c2e782f50e58ec7d9cba23986c5494c7c00d334de82a3f6f2c9a5b35739a402ecc097f144cb05314ab8584f07aa46dab548b6c0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
+ Starting from version 2.0, changelog entries are tracked via https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/releases
2
+
1
3
  2.0.0:
2
4
 
3
5
  - Drop explicit support for Ruby 2.6 and 2.7 by removing those versions from
data/README.md CHANGED
@@ -202,6 +202,20 @@ https://gitlab.com/gitlab-org/ruby/gems/declarative-policy. This project is inte
202
202
  a safe, welcoming space for collaboration, and contributors are expected to
203
203
  adhere to the [GitLab code of conduct](https://about.gitlab.com/community/contribute/code-of-conduct/).
204
204
 
205
+ ## Release process
206
+
207
+ We release `declarative_policy` on an ad-hoc basis. There is no regularity to when
208
+ we release, we just release when we make a change - no matter the size of the
209
+ change.
210
+
211
+ To release a new version:
212
+
213
+ 1. Create a Merge Request.
214
+ 1. Use Merge Request template [Release.md](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/main/.gitlab/merge_request_templates/Release.md).
215
+ 1. Follow the instructions.
216
+ 1. After the Merge Request has been merged, a new gem version is [published automatically](https://gitlab.com/gitlab-org/components/gem-release).
217
+ 1. Once the new gem version is visible on [RubyGems.org](https://rubygems.org/gems/declarative_policy), it is recommended to update [GitLab's `Gemfile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/Gemfile) to bump the `declarative_policy` Ruby gem to the new version also.
218
+
205
219
  ## License
206
220
 
207
221
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -210,4 +224,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
210
224
 
211
225
  Everyone interacting in the `DeclarativePolicy` project's codebase, issue
212
226
  trackers, chat rooms and mailing lists is expected to follow
213
- the [code of conduct](https://github.com/[USERNAME]/declarative-policy/blob/master/CODE_OF_CONDUCT.md).
227
+ the [code of conduct](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/blob/main/CODE_OF_CONDUCT.md).
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.metadata['homepage_uri'] = spec.homepage
25
25
  spec.metadata['source_code_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy'
26
- spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/main/CHANGELOG.md'
26
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/releases'
27
27
 
28
28
  spec.metadata['rubygems_mfa_required'] = 'false'
29
29
 
@@ -124,6 +124,42 @@ rule { old_enough_to_drive }.policy do
124
124
  end
125
125
  ```
126
126
 
127
+ #### `prevent_all`
128
+
129
+ To prevent all abilities at once, use `prevent_all`:
130
+
131
+ ```ruby
132
+ rule { banned }.prevent_all
133
+ ```
134
+
135
+ This is equivalent to adding a `prevent` for every ability in the policy. It is
136
+ commonly used to deny all access when a precondition fails (e.g. a user is
137
+ suspended or a resource is locked).
138
+
139
+ #### `prevent_all` with exceptions
140
+
141
+ To prevent all abilities **except** specific ones, pass a block with `except`
142
+ declarations:
143
+
144
+ ```ruby
145
+ rule { suspended }.prevent_all do
146
+ except :read
147
+ except :appeal_suspension
148
+ end
149
+ ```
150
+
151
+ Multiple abilities can also be listed in a single `except` call:
152
+
153
+ ```ruby
154
+ rule { suspended }.prevent_all do
155
+ except :read, :list, :appeal_suspension
156
+ end
157
+ ```
158
+
159
+ Excepted abilities are excluded from the blanket prevent and follow normal
160
+ enable/prevent evaluation. Non-excepted abilities are prevented when the rule's
161
+ condition holds, regardless of any `enable` rules.
162
+
127
163
  Rule blocks do not have access to the internal state of the policy, and cannot
128
164
  access the `@user` or `@subject`, or any methods on the policy instance. You
129
165
  should not perform I/O in a rule. They exist solely to define the logical rules
@@ -113,9 +113,15 @@ module DeclarativePolicy
113
113
 
114
114
  # all the [rule, action] pairs that apply to a particular ability.
115
115
  # we combine the specific ones looked up in ability_map with the global
116
- # ones.
116
+ # ones, filtering out any global actions that have excepted this ability.
117
117
  def configuration_for(ability)
118
- ability_map.actions(ability) + global_actions
118
+ applicable_globals = global_actions.filter_map do |(action, rule, exceptions)|
119
+ next if exceptions&.include?(ability)
120
+
121
+ [action, rule]
122
+ end
123
+
124
+ ability_map.actions(ability) + applicable_globals
119
125
  end
120
126
 
121
127
  ### declaration methods ###
@@ -215,8 +221,10 @@ module DeclarativePolicy
215
221
 
216
222
  # we store global prevents (from `prevent_all`) separately,
217
223
  # so that they can be combined into every decision made.
218
- def prevent_all_when(rule)
219
- own_global_actions << [:prevent, rule]
224
+ # The optional `except` parameter is a Set of abilities
225
+ # that should be excluded from the blanket prevent.
226
+ def prevent_all_when(rule, except: nil)
227
+ own_global_actions << [:prevent, rule, except]
220
228
  end
221
229
 
222
230
  private
@@ -255,6 +263,8 @@ module DeclarativePolicy
255
263
  # This is the main entry point for permission checks. It constructs
256
264
  # or looks up a Runner for the given ability and asks it if it passes.
257
265
  def allowed?(*abilities)
266
+ return false if abilities.empty?
267
+
258
268
  abilities.all? { |a| runner(a).pass? }
259
269
  end
260
270
 
@@ -351,8 +361,17 @@ module DeclarativePolicy
351
361
 
352
362
  # used in specs - returns true if there is no possible way for any action
353
363
  # to be allowed, determined only by the global :prevent_all rules.
364
+ # Only considers global actions with no exceptions (a prevent_all with
365
+ # exceptions cannot fully ban, since the excepted abilities may still pass).
354
366
  def banned?
355
- global_steps = self.class.global_actions.map { |(action, rule)| Step.new(self, rule, action) }
367
+ global_steps = self.class.global_actions.filter_map do |(action, rule, exceptions)|
368
+ next if exceptions && !exceptions.empty?
369
+
370
+ Step.new(self, rule, action)
371
+ end
372
+
373
+ return false if global_steps.empty?
374
+
356
375
  !Runner.new(global_steps).pass?
357
376
  end
358
377
 
@@ -29,8 +29,14 @@ module DeclarativePolicy
29
29
  @context_class.prevent_when(abilities, @rule)
30
30
  end
31
31
 
32
- def prevent_all
33
- @context_class.prevent_all_when(@rule)
32
+ def prevent_all(&block)
33
+ if block
34
+ dsl = PreventAllDsl.new
35
+ dsl.instance_eval(&block)
36
+ @context_class.prevent_all_when(@rule, except: dsl.exceptions)
37
+ else
38
+ @context_class.prevent_all_when(@rule)
39
+ end
34
40
  end
35
41
 
36
42
  def method_missing(msg, ...)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module DeclarativePolicy
6
+ # A small DSL class used within a prevent_all { ... } block
7
+ # to capture exception abilities.
8
+ #
9
+ # Usage:
10
+ # rule { some_condition }.prevent_all do
11
+ # except :read
12
+ # except :list
13
+ # end
14
+ class PreventAllDsl
15
+ attr_reader :exceptions
16
+
17
+ def initialize
18
+ @exceptions = Set.new
19
+ end
20
+
21
+ def except(*abilities)
22
+ @exceptions.merge(abilities)
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclarativePolicy
4
- VERSION = '2.0.1'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -4,6 +4,7 @@ require 'set'
4
4
  require_relative 'declarative_policy/cache'
5
5
  require_relative 'declarative_policy/condition'
6
6
  require_relative 'declarative_policy/delegate_dsl'
7
+ require_relative 'declarative_policy/prevent_all_dsl'
7
8
  require_relative 'declarative_policy/policy_dsl'
8
9
  require_relative 'declarative_policy/rule_dsl'
9
10
  require_relative 'declarative_policy/preferred_scope'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: declarative_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - group::authorization
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-23 00:00:00.000000000 Z
11
+ date: 2026-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -140,6 +140,7 @@ files:
140
140
  - lib/declarative_policy/nil_policy.rb
141
141
  - lib/declarative_policy/policy_dsl.rb
142
142
  - lib/declarative_policy/preferred_scope.rb
143
+ - lib/declarative_policy/prevent_all_dsl.rb
143
144
  - lib/declarative_policy/rule.rb
144
145
  - lib/declarative_policy/rule_dsl.rb
145
146
  - lib/declarative_policy/runner.rb
@@ -151,7 +152,7 @@ licenses:
151
152
  metadata:
152
153
  homepage_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
153
154
  source_code_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
154
- changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/main/CHANGELOG.md
155
+ changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/releases
155
156
  rubygems_mfa_required: 'false'
156
157
  post_install_message:
157
158
  rdoc_options: []