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