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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -3
- data/README.md +72 -65
- data/consent.gemspec +1 -2
- data/lib/consent.rb +0 -3
- data/lib/consent/ability.rb +29 -5
- data/lib/consent/permission.rb +12 -17
- data/lib/consent/subject.rb +0 -9
- data/lib/consent/version.rb +1 -1
- data/renovate.json +5 -0
- metadata +8 -23
- data/lib/consent/permissions.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66e7e1705d61760713253718eabda896ffa49f25ea6e1a9a6c4fc1a188730cfb
|
4
|
+
data.tar.gz: 4dc120c6f1e89a3cb612a62cf9fdbac565e51b9fabe9d04a68c64c3d9a8fd193
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ca74d2788b689711966b38e11ce4b857009064d1fce59c2b5335adc437c1b20a9298acb61a0e71cf3aac2eef1f13f6b8b06570014938ef757374b6a4f98b9c4
|
7
|
+
data.tar.gz: 9d4e6cf5d18d0671c9aade10a33933cc93b0cb704c623d6705332de55a1a02b3952b01fdee9d357a1c0ba9f37d570910ac1fe54614087cd88c94d3d890b5761d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.
|
5
|
-
- 2.
|
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.
|
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
|
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
|
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:
|
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
|
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
|
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
|
66
|
-
|
67
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
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
|
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
|
-
|
169
|
-
hash owned by a `User`, or a `Role` on an application.
|
167
|
+
## CanCan Integration
|
170
168
|
|
171
|
-
|
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
|
-
|
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
|
-
|
187
|
-
|
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
|
-
|
194
|
+
You can also consent full access by not specifying the view:
|
193
195
|
|
194
|
-
|
195
|
-
|
196
|
+
```ruby
|
197
|
+
consent :read, Project
|
198
|
+
```
|
196
199
|
|
197
|
-
|
200
|
+
If you have a somehow manageable permission, you can consent them in batch in your ability:
|
198
201
|
|
199
202
|
```ruby
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
214
|
+
Consenting the same permission multiple times is handled as a Union by CanCanCan:
|
208
215
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
216
|
+
```ruby
|
217
|
+
class MyAbility < Consent::Ability
|
218
|
+
def initialize(user)
|
219
|
+
super user
|
213
220
|
|
214
|
-
|
215
|
-
|
221
|
+
consent :read, Project, :department
|
222
|
+
consent :read, Project, :future_projects
|
223
|
+
end
|
224
|
+
end
|
216
225
|
|
217
|
-
|
218
|
-
|
219
|
-
```
|
226
|
+
user = User.new(department_id: 13)
|
227
|
+
ability = MyAbility.new(user)
|
220
228
|
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
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', '
|
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.:
|
data/lib/consent/ability.rb
CHANGED
@@ -5,11 +5,35 @@ module Consent
|
|
5
5
|
class Ability
|
6
6
|
include CanCan::Ability
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
data/lib/consent/permission.rb
CHANGED
@@ -2,34 +2,29 @@
|
|
2
2
|
|
3
3
|
module Consent
|
4
4
|
class Permission # :nodoc:
|
5
|
-
|
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
|
-
@
|
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
|
16
|
-
@action.
|
14
|
+
def action
|
15
|
+
@action ||= Consent.find_action(subject_key, action_key)
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
|
-
|
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
|
23
|
+
@view.nil? ? nil : @view.conditions(*args)
|
28
24
|
end
|
29
25
|
|
30
26
|
def object_conditions(*args)
|
31
|
-
@view
|
27
|
+
@view.nil? ? nil : @view.object_conditions(*args)
|
32
28
|
end
|
33
|
-
# rubocop:enable Style/SafeNavigation
|
34
29
|
end
|
35
30
|
end
|
data/lib/consent/subject.rb
CHANGED
@@ -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
|
data/lib/consent/version.rb
CHANGED
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.
|
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:
|
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:
|
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:
|
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
|
-
|
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
|
data/lib/consent/permissions.rb
DELETED
@@ -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
|