consent 0.6.0 → 1.0.0

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