consent 0.6.0 → 1.0.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: 5f54a2338cba28ba0e887ec44e7e43f9aae2209d4b1017638eaf4334564eae2c
4
- data.tar.gz: 2dc433ca07b4615fc56a344d32c3b6adfc5267420419d6d1fd201040ee9bdb31
3
+ metadata.gz: 66e7e1705d61760713253718eabda896ffa49f25ea6e1a9a6c4fc1a188730cfb
4
+ data.tar.gz: 4dc120c6f1e89a3cb612a62cf9fdbac565e51b9fabe9d04a68c64c3d9a8fd193
5
5
  SHA512:
6
- metadata.gz: e1ddfc63c31df91ccac74ada8806408353e81f2626d0592dcb954f34c3ffd03aacde585c0f625d9534cd749b2999deebb05860cd2cc3be34ba9b40c2a8645ac4
7
- data.tar.gz: 5389891aeef0e65d3752ada1c647c9e0dc1c60debb67fc22019f067dbeb4a4b0c5ac07ab14d0fb760680818c38beab6e4fb4c225f5d07639945b9377cf2dd0bd
6
+ metadata.gz: 9ca74d2788b689711966b38e11ce4b857009064d1fce59c2b5335adc437c1b20a9298acb61a0e71cf3aac2eef1f13f6b8b06570014938ef757374b6a4f98b9c4
7
+ data.tar.gz: 9d4e6cf5d18d0671c9aade10a33933cc93b0cb704c623d6705332de55a1a02b3952b01fdee9d357a1c0ba9f37d570910ac1fe54614087cd88c94d3d890b5761d
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /consent-*.gem
data/.travis.yml CHANGED
@@ -1,8 +1,10 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.2.2
5
- - 2.5.0
4
+ - 2.5.8
5
+ - 2.6.6
6
+ - 2.7.2
7
+ - 3.0.0
6
8
  before_install: gem install bundler -v 1.17.3
7
9
  script:
8
10
  - bundle exec rubocop
@@ -15,4 +17,4 @@ deploy:
15
17
  on:
16
18
  tags: true
17
19
  repo: powerhome/consent
18
- ruby: 2.5.0
20
+ ruby: 2.6.6
data/README.md CHANGED
@@ -18,21 +18,21 @@ Or install it yourself as:
18
18
 
19
19
  ## What is Consent
20
20
 
21
- Consent makes defining permissions easier by providing a clean, concise DSL for authorization so that all abilities do not have to be in your `Ability`
21
+ Consent makes defining permissions easier by providing a clean, concise DSL for authorization
22
+ so that all abilities do not have to be in your `Ability`
22
23
  class.
23
24
 
24
- Consent takes application permissions and models them so that permissions are organized and can be defined granularly. It does so using the
25
- following models:
25
+ Consent takes application permissions and models them so that permissions are organized and can
26
+ be defined granularly. It does so using the following models:
26
27
 
27
28
  * View: A collection of objects limited by a given condition.
28
29
  * Action: An action performed on top of the objects limited by the view. For example, one user could only `:view` something, while another could `:manage` it.
29
30
  * Subject: Holds the scope of the actions.
30
- * Permission: What is given to the user. Combines a subject, an action and
31
- a view.
31
+ * Permission: The combination of a subject, an action, and a view (or full-access).
32
32
 
33
33
  ## What Consent Is Not
34
34
 
35
- Consent isn't a tool to enforce permissions -- it is intended to be used with CanCanCan and is only to make permissions more easily readable and definable.
35
+ Consent isn't a tool to enforce permissions -- it supports CanCan(Can) for that goal.
36
36
 
37
37
  ## Subject
38
38
 
@@ -50,7 +50,8 @@ Consent.define Project, 'Our Projects' do
50
50
  end
51
51
  ```
52
52
 
53
- The scope is the action that's being performed on the subject. It can be anything, but will typically be an ActiveRecord class, a `:symbol`, or a PORO.
53
+ The scope is the action that's being performed on the subject. It can be anything, but will
54
+ typically be an ActiveRecord class, a `:symbol`, or a PORO.
54
55
 
55
56
  For instance:
56
57
 
@@ -62,16 +63,15 @@ end
62
63
 
63
64
  ## Views
64
65
 
65
- Views are the rules that limit the access to actions. For instance,
66
- a user may see a `Project` from his department, but not from others. That rule
67
- could be enforced with a `:department` view, defined like this:
66
+ Views are the rules that limit access to actions. For instance, a user may see a `Project`
67
+ from his department, but not from others. You can enforce it with a `:department` view,
68
+ as in the examples below:
68
69
 
69
70
  ### Hash Conditions
70
71
 
71
- This is probably the most commonly used and is useful, for example,
72
- when the view can be defined using a where condition in an ActiveRecord context.
73
- It follows a match condition and will return all objects that meet the criteria
74
- and is based off a boolean:
72
+ Probably the most commonly used. When the view can be defined using a `where` scope in
73
+ an ActiveRecord context. It follows a match condition and will return all objects that meet
74
+ the criteria:
75
75
 
76
76
  ```ruby
77
77
  Consent.define Project, 'Projects' do
@@ -81,22 +81,21 @@ Consent.define Project, 'Projects' do
81
81
  end
82
82
  ```
83
83
 
84
- Although hash conditions (matching object's attributes) are recommended,
85
- the constraints can be anything you want. Since Consent does not enforce the
86
- rules, those rules are directly given to CanCan. Following [CanCan rules](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practice)
84
+ Although hash conditions (matching object's attributes) are recommended, the constraints can
85
+ be anything you want. Since Consent does not enforce the rules, those rules are directly given
86
+ to CanCan. Following [CanCan rules](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practice)
87
87
  for defining abilities is recommended.
88
88
 
89
89
  ### Object Conditions
90
90
 
91
- If you're not matching for equal values, then you would need to use an object
92
- condition, which matches data based off a range.
91
+ If you're not matching for equal values, then you would need to use an object condition.
93
92
 
94
- If you already have an object and want to check to see whether the user has
95
- permission to view that specific object, you would use object conditions.
93
+ If you already have an object and want to check to see whether the user has permission to view
94
+ that specific object, you would use object conditions.
96
95
 
97
- If your needs can't be satisfied by hash conditions, it is recommended that a
98
- second condition is given for constraining object instances. For example, if you
99
- want to restrict a view for smaller volume projects:
96
+ If your needs can't be satisfied by hash conditions, it is recommended that a second condition
97
+ is given for constraining object instances. For example, if you want to restrict a view for smaller
98
+ volume projects:
100
99
 
101
100
  ```ruby
102
101
  Consent.define Project, 'Projects' do
@@ -111,7 +110,7 @@ end
111
110
  ```
112
111
 
113
112
  For object conditions, the latter argument will be the referred object, while the
114
- former will be the context given to the [Permission](#permission) (also check
113
+ first will be the context given to the [Permission](#permission) (also check
115
114
  [CanCan integration](#cancan-integration)).
116
115
 
117
116
  ## Action
@@ -161,73 +160,81 @@ end
161
160
 
162
161
  ## Permission
163
162
 
164
- A permission is what is consented to the user. It is the *permission* to perform
163
+ A permission is what is consented to the user. It consentment to perform
165
164
  an *action* on a limited *view* of the *subject*. It marries the three concepts
166
165
  to consent an access to the user.
167
166
 
168
- A permission is not specified by the user, it is calculated from a permissions
169
- hash owned by a `User`, or a `Role` on an application.
167
+ ## CanCan Integration
170
168
 
171
- The permissions hash looks like the following:
169
+ Consent provides a CanCan ability (Consent::Ability) to integrate your
170
+ permissions with frameworks like Rails. To use it with Rails check out the
171
+ example at [Ability for Other Users](https://github.com/CanCanCommunity/cancancan/wiki/Ability-for-Other-Users)
172
+ on CanCanCan's wiki.
173
+
174
+ In the ability you define the scope of the permissions. This is typically a
175
+ user:
172
176
 
173
177
  ```ruby
174
- {
175
- project: {
176
- read: 'department',
177
- approve: 'small_volumes'
178
- }
179
- }
178
+ Consent::Ability.new(user)
180
179
  ```
181
180
 
182
- In other words:
181
+ You'd more commonly define a subclass of `Consent::Ability`, and consent access
182
+ to the user by calling `consent`:
183
183
 
184
184
  ```ruby
185
- {
186
- <subject>: {
187
- <action>: <view>
188
- }
189
- }
185
+ class MyAbility < Consent::Ability
186
+ def initialize(user)
187
+ super user
188
+
189
+ consent :read, Project, :department
190
+ end
191
+ end
190
192
  ```
191
193
 
192
- ### Full Access
194
+ You can also consent full access by not specifying the view:
193
195
 
194
- Full (unrestricted by views) access is granted when view is `'1'`, `true` or
195
- `'true'`. For instance:
196
+ ```ruby
197
+ consent :read, Project
198
+ ```
196
199
 
197
- In other words:
200
+ If you have a somehow manageable permission, you can consent them in batch in your ability:
198
201
 
199
202
  ```ruby
200
- {
201
- projects: {
202
- approve: true
203
- }
204
- }
203
+ class MyAbility < Consent::Ability
204
+ def initialize(user)
205
+ super user
206
+
207
+ user.permissions.each do |permission|
208
+ consent permission.action, permission.subject, permission.view
209
+ end
210
+ end
211
+ end
205
212
  ```
206
213
 
207
- ## CanCan Integration
214
+ Consenting the same permission multiple times is handled as a Union by CanCanCan:
208
215
 
209
- Consent provides a CanCan ability (Consent::Ability) to integrate your
210
- permissions with frameworks like Rails. To use it with Rails check out the
211
- example at [Ability for Other Users](https://github.com/CanCanCommunity/cancancan/wiki/Ability-for-Other-Users)
212
- on CanCanCan's wiki.
216
+ ```ruby
217
+ class MyAbility < Consent::Ability
218
+ def initialize(user)
219
+ super user
213
220
 
214
- In the ability you define the scope of the permissions. This is typically an
215
- user:
221
+ consent :read, Project, :department
222
+ consent :read, Project, :future_projects
223
+ end
224
+ end
216
225
 
217
- ```ruby
218
- Consent::Ability.new(user.permissions, user)
219
- ```
226
+ user = User.new(department_id: 13)
227
+ ability = MyAbility.new(user)
220
228
 
221
- The first parameter given to the ability is the permissions hash, seen at
222
- [Permission](#permission). The following parameters are the permission context.
223
- These parameters are given directly to the condition blocks defined by the views
224
- in the exact same order, so it's up to you to define what your context is.
229
+ Project.accessible_by(ability, :read).to_sql
230
+ => SELECT * FROM projects WHERE ((department_id = 13) OR (starts_at > '2021-04-06'))
231
+ ```
225
232
 
226
233
  ## Rails Integration
227
234
 
228
235
  Consent is integrated into Rails with `Consent::Railtie`. To define where
229
236
  your permission files will be, use `config.consent.path`. This defaults to
230
- `app/permissions/` to conform to Rails' standards.
237
+ `#{Rails.root}/app/permissions/` to conform to Rails' standards.
231
238
 
232
239
  ## Development
233
240
 
data/consent.gemspec CHANGED
@@ -20,10 +20,9 @@ Gem::Specification.new do |spec|
20
20
  end
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_development_dependency 'activesupport', '>= 4.1.11'
24
23
  spec.add_development_dependency 'bundler', '>= 1.17.3'
25
24
  spec.add_development_dependency 'cancancan', '~> 1.15.0'
26
- spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rake', '>= 12.3.3'
27
26
  spec.add_development_dependency 'rspec', '~> 3.0'
28
27
  spec.add_development_dependency 'rubocop', '~> 0.65.0'
29
28
  end
data/lib/consent.rb CHANGED
@@ -6,7 +6,6 @@ require 'consent/view'
6
6
  require 'consent/action'
7
7
  require 'consent/dsl'
8
8
  require 'consent/permission'
9
- require 'consent/permissions'
10
9
  require 'consent/ability' if defined?(CanCan)
11
10
  require 'consent/railtie' if defined?(Rails)
12
11
 
@@ -14,8 +13,6 @@ require 'consent/railtie' if defined?(Rails)
14
13
  # concise DSL for authorization so that all abilities do not have
15
14
  # to be in your `Ability` class.
16
15
  module Consent
17
- FULL_ACCESS = %w[1 true].freeze
18
-
19
16
  # Default views available to every permission
20
17
  #
21
18
  # i.e.:
@@ -5,11 +5,35 @@ module Consent
5
5
  class Ability
6
6
  include CanCan::Ability
7
7
 
8
- def initialize(permissions, *args)
9
- Consent.permissions(permissions).each do |permission|
10
- conditions = permission.conditions(*args)
11
- ocond = permission.object_conditions(*args)
12
- can permission.action_key, permission.subject_key, conditions, &ocond
8
+ def initialize(*args, apply_defaults: true)
9
+ @context = *args
10
+ apply_defaults! if apply_defaults
11
+ end
12
+
13
+ def consent(permission: nil, subject: nil, action: nil, view: nil)
14
+ permission ||= Permission.new(subject, action, view)
15
+ return unless permission.valid?
16
+
17
+ can(
18
+ permission.action_key, permission.subject_key,
19
+ permission.conditions(*@context),
20
+ &permission.object_conditions(*@context)
21
+ )
22
+ end
23
+
24
+ private
25
+
26
+ def apply_defaults!
27
+ Consent.subjects.each do |subject|
28
+ subject.actions.each do |action|
29
+ next unless action.default_view
30
+
31
+ consent(
32
+ subject: subject.key,
33
+ action: action.key,
34
+ view: action.default_view
35
+ )
36
+ end
13
37
  end
14
38
  end
15
39
  end
@@ -2,34 +2,29 @@
2
2
 
3
3
  module Consent
4
4
  class Permission # :nodoc:
5
- def initialize(subject, action, view = nil)
6
- @subject = subject
7
- @action = action
8
- @view = view
9
- end
5
+ attr_reader :subject_key, :action_key, :view_key, :view
10
6
 
11
- def subject_key
12
- @subject.key
7
+ def initialize(subject_key, action_key, view_key = nil)
8
+ @subject_key = subject_key
9
+ @action_key = action_key
10
+ @view_key = view_key
11
+ @view = Consent.find_view(subject_key, view_key) if view_key
13
12
  end
14
13
 
15
- def action_key
16
- @action.key
14
+ def action
15
+ @action ||= Consent.find_action(subject_key, action_key)
17
16
  end
18
17
 
19
- # Disables Sytle/SafeNavigation to keep this code
20
- # compatible with ruby < 2.3
21
- # rubocop:disable Style/SafeNavigation
22
- def view_key
23
- @view && @view.key
18
+ def valid?
19
+ action && (@view_key.nil? == @view.nil?)
24
20
  end
25
21
 
26
22
  def conditions(*args)
27
- @view && @view.conditions(*args)
23
+ @view.nil? ? nil : @view.conditions(*args)
28
24
  end
29
25
 
30
26
  def object_conditions(*args)
31
- @view && @view.object_conditions(*args)
27
+ @view.nil? ? nil : @view.object_conditions(*args)
32
28
  end
33
- # rubocop:enable Style/SafeNavigation
34
29
  end
35
30
  end
@@ -10,14 +10,5 @@ module Consent
10
10
  @actions = []
11
11
  @views = Consent.default_views.clone
12
12
  end
13
-
14
- def permission_key
15
- ActiveSupport::Inflector.underscore(@key.to_s).to_sym
16
- end
17
-
18
- def view_for(action, key)
19
- view = @views.keys & action.view_keys & [key]
20
- @views[view.first] || @views[action.default_view]
21
- end
22
13
  end
23
14
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consent
4
- VERSION = '0.6.0'
4
+ VERSION = '1.0.0'
5
5
  end
data/renovate.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ]
5
+ }
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Palhares
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-22 00:00:00.000000000 Z
11
+ date: 2021-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: activesupport
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 4.1.11
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 4.1.11
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +42,16 @@ dependencies:
56
42
  name: rake
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - "~>"
45
+ - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: '10.0'
47
+ version: 12.3.3
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - "~>"
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '10.0'
54
+ version: 12.3.3
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rspec
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -120,7 +106,6 @@ files:
120
106
  - lib/consent/action.rb
121
107
  - lib/consent/dsl.rb
122
108
  - lib/consent/permission.rb
123
- - lib/consent/permissions.rb
124
109
  - lib/consent/railtie.rb
125
110
  - lib/consent/reloader.rb
126
111
  - lib/consent/rspec.rb
@@ -130,6 +115,7 @@ files:
130
115
  - lib/generators/consent/permissions_generator.rb
131
116
  - lib/generators/consent/templates/permissions.rb.erb
132
117
  - lib/generators/consent/templates/permissions_spec.rb.erb
118
+ - renovate.json
133
119
  homepage:
134
120
  licenses:
135
121
  - MIT
@@ -149,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
135
  - !ruby/object:Gem::Version
150
136
  version: '0'
151
137
  requirements: []
152
- rubyforge_project:
153
- rubygems_version: 2.7.3
138
+ rubygems_version: 3.0.8
154
139
  signing_key:
155
140
  specification_version: 4
156
141
  summary: Consent
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Consent
4
- class Permissions # :nodoc:
5
- include Enumerable
6
-
7
- def initialize(permissions)
8
- @permissions = permissions
9
- end
10
-
11
- def each(&block)
12
- Consent.subjects.each do |subject|
13
- subject.actions.map do |action|
14
- map_permission subject, action
15
- end.compact.each(&block)
16
- end
17
- end
18
-
19
- private
20
-
21
- def map_permission(subject, action)
22
- subject_key = subject.permission_key
23
- actions = @permissions[subject_key] || @permissions[subject_key.to_s]
24
- view = actions && (actions[action.key] || actions[action.key.to_s])
25
- full(subject, action, view) || partial(subject, action, view)
26
- end
27
-
28
- def full(subject, action, view_key)
29
- return unless Consent::FULL_ACCESS.include?(view_key.to_s.strip)
30
-
31
- Permission.new(subject, action)
32
- end
33
-
34
- def partial(subject, action, view_key)
35
- view = subject.view_for(action, view_key.to_s.to_sym)
36
- return if view.nil?
37
-
38
- Permission.new(subject, action, view)
39
- end
40
- end
41
- end