i_am_i_can 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +17 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +116 -0
- data/LICENSE.txt +21 -0
- data/README.md +442 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/i_am_i_can.gemspec +36 -0
- data/lib/generators/i_am_i_can/setup_generator.rb +56 -0
- data/lib/generators/i_am_i_can/templates/migrations/add_to_subject.erb +5 -0
- data/lib/generators/i_am_i_can/templates/migrations/permission.erb +15 -0
- data/lib/generators/i_am_i_can/templates/migrations/role.erb +13 -0
- data/lib/generators/i_am_i_can/templates/migrations/role_group.erb +14 -0
- data/lib/generators/i_am_i_can/templates/models/permission.erb +11 -0
- data/lib/generators/i_am_i_can/templates/models/role.erb +10 -0
- data/lib/generators/i_am_i_can/templates/models/role_group.erb +11 -0
- data/lib/i_am_i_can/config.rb +14 -0
- data/lib/i_am_i_can/has_an_array_of.rb +75 -0
- data/lib/i_am_i_can/permission/assignment.rb +90 -0
- data/lib/i_am_i_can/permission/definition.rb +61 -0
- data/lib/i_am_i_can/permission/helpers.rb +46 -0
- data/lib/i_am_i_can/permission/p_array.rb +22 -0
- data/lib/i_am_i_can/permission.rb +68 -0
- data/lib/i_am_i_can/role/assignment.rb +76 -0
- data/lib/i_am_i_can/role/definition.rb +100 -0
- data/lib/i_am_i_can/role/helpers.rb +28 -0
- data/lib/i_am_i_can/subject/permission_querying.rb +69 -0
- data/lib/i_am_i_can/subject/role_querying.rb +66 -0
- data/lib/i_am_i_can/version.rb +3 -0
- data/lib/i_am_i_can.rb +61 -0
- metadata +220 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module HasAnArrayOf
|
3
|
+
def has_an_array_of obj, model: nil, field: nil,
|
4
|
+
prefix: nil, attrs: [ ], located_by: nil, cache_expires_in: nil, for_related_name: nil
|
5
|
+
obj_model = model.constantize || obj.to_s.singularize.camelize.constantize
|
6
|
+
field = field || :"#{obj.to_s.singularize}_ids"
|
7
|
+
prefix = "#{prefix}_" if prefix
|
8
|
+
|
9
|
+
# User.where(..).stored_roles
|
10
|
+
define_singleton_method "#{prefix}#{obj}" do
|
11
|
+
obj_ids = self.all.map(&field).flatten.uniq
|
12
|
+
obj_model.where(id: obj_ids)
|
13
|
+
end
|
14
|
+
|
15
|
+
# user.stored_roles
|
16
|
+
define_method "#{prefix}#{obj}" do
|
17
|
+
obj_model.where(id: send(field))
|
18
|
+
end
|
19
|
+
|
20
|
+
# cached_stored_roles
|
21
|
+
define_method "cached_#{prefix}#{obj}" do |**options|
|
22
|
+
Rails.cache.fetch("#{self.class.name}/#{id}/#{obj}", expires_in: cache_expires_in, **options) do
|
23
|
+
obj_model.where(id: send(field))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# stored_roles_add
|
28
|
+
define_method "#{prefix}#{obj}_add" do |locate_vals = nil, check_size: nil, **condition|
|
29
|
+
condition = { located_by => locate_vals } if locate_vals
|
30
|
+
obj_ids = obj_model.where(condition)&.pluck(:id)
|
31
|
+
# will return false if it does nothing
|
32
|
+
return false if obj_ids.blank? || (check_size && obj_ids != check_size)
|
33
|
+
(send(field).concat(obj_ids)).uniq!
|
34
|
+
save!
|
35
|
+
end
|
36
|
+
|
37
|
+
# stored_roles_rmv
|
38
|
+
define_method "#{prefix}#{obj}_rmv" do |locate_vals = nil, check_size: nil, **condition|
|
39
|
+
condition = { located_by => locate_vals } if locate_vals
|
40
|
+
obj_ids = obj_model.where(condition)&.pluck(:id)
|
41
|
+
# will return false if it does nothing
|
42
|
+
return false if obj_ids.blank? || (check_size && obj_ids != check_size)
|
43
|
+
send("#{field}=", send(field) - obj_ids)
|
44
|
+
save!
|
45
|
+
end
|
46
|
+
|
47
|
+
attrs.each do |(attr_name, attr_type)|
|
48
|
+
# User.where(..).stored_role_names
|
49
|
+
define_singleton_method "#{prefix}#{obj.to_s.singularize}_#{attr_name.to_s.pluralize}" do
|
50
|
+
res = send("#{prefix}#{obj}").pluck(attr_name)
|
51
|
+
attr_type ? res.map(&attr_type) : res
|
52
|
+
end
|
53
|
+
|
54
|
+
# user.stored_role_names
|
55
|
+
define_method "#{prefix}#{obj.to_s.singularize}_#{attr_name.to_s.pluralize}" do
|
56
|
+
res = send("#{prefix}#{obj}").pluck(attr_name)
|
57
|
+
attr_type ? res.map(&attr_type) : res
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# === actions for object model ===
|
62
|
+
obj_model.class_exec(for_related_name, self, field) do |subject_name, subject_model, related_field|
|
63
|
+
# roles.related_users
|
64
|
+
define_singleton_method "related_#{(subject_name || subject_model.name).underscore.pluralize}" do
|
65
|
+
subject_model.where("#{related_field} @> ARRAY[?]::integer[]", ids)
|
66
|
+
end
|
67
|
+
|
68
|
+
# role.related_users
|
69
|
+
define_method "related_#{(subject_name || subject_model.name).underscore.pluralize}" do
|
70
|
+
subject_model.where("? = ANY (#{related_field})", self.id)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'i_am_i_can/permission/helpers'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Permission
|
5
|
+
module Assignment
|
6
|
+
include Helpers::Ins
|
7
|
+
|
8
|
+
# permission assignment for stored role
|
9
|
+
def can *preds, obj: nil, strict_mode: false, auto_define_before: config.auto_define_before
|
10
|
+
self.class.have_permissions *preds, obj: obj if auto_define_before
|
11
|
+
not_defined_items, covered_items = [ ], [ ]
|
12
|
+
|
13
|
+
preds.each do |pred|
|
14
|
+
pms_name = pms_naming(pred, obj)
|
15
|
+
covered_items << pms_name if pms_matched?(pms_name, in: stored_permission_names)
|
16
|
+
not_defined_items << pms_name unless stored_permissions_add(pred: pred, **deconstruct_obj(obj))
|
17
|
+
end
|
18
|
+
|
19
|
+
_pms_assignment_result(preds, obj, not_defined_items, covered_items, strict_mode)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias has_permission can
|
23
|
+
|
24
|
+
def temporarily_can *preds, obj: nil, strict_mode: false, auto_define_before: config.auto_define_before
|
25
|
+
raise Error, "Permission Assignment: local role `#{name}` was not defined" unless config.subject_model.defined_local_roles.key?(self.name.to_sym)
|
26
|
+
self.class.have_permissions *preds, obj: obj, save: false if auto_define_before
|
27
|
+
not_defined_items, covered_items = [ ], [ ]
|
28
|
+
|
29
|
+
preds.each do |pred|
|
30
|
+
pms_name = pms_naming(pred, obj)
|
31
|
+
next not_defined_items << pms_name unless pms_name.in?(defined_permissions.keys)
|
32
|
+
covered_items << pms_name if pms_matched?(pms_name, in: pms_of_defined_local_role(self.name))
|
33
|
+
pms_of_defined_local_role(self.name) << pms_name
|
34
|
+
end
|
35
|
+
|
36
|
+
_pms_assignment_result(preds, obj, not_defined_items, covered_items, strict_mode)
|
37
|
+
end
|
38
|
+
|
39
|
+
alias locally_can temporarily_can
|
40
|
+
|
41
|
+
def cannot *preds, obj: nil, saved: true
|
42
|
+
not_defined_items = [ ]
|
43
|
+
|
44
|
+
preds.each do |pred|
|
45
|
+
pms_name = pms_naming(pred, obj)
|
46
|
+
if saved
|
47
|
+
next if stored_permissions_rmv(pred: pred, **deconstruct_obj(obj))
|
48
|
+
not_defined_items << pms_name
|
49
|
+
else
|
50
|
+
next not_defined_items << pms_name unless pms_name.in?(defined_permissions.keys)
|
51
|
+
pms_of_defined_local_role(self.name).delete(pms_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
_pms_assignment_result(preds, obj, not_defined_items)
|
56
|
+
end
|
57
|
+
|
58
|
+
alias is_not_allowed_to cannot
|
59
|
+
|
60
|
+
# `can? :manage, User` / `can? :manage, obj: User`
|
61
|
+
def can? pred, obj0 = nil, obj: nil
|
62
|
+
obj = obj0 || obj
|
63
|
+
pms_name = pms_naming(pred, obj)
|
64
|
+
temporarily_can?(pred, obj) || pms_matched?(pms_name, in: stored_permission_names)
|
65
|
+
end
|
66
|
+
|
67
|
+
def temporarily_can? pred, obj
|
68
|
+
pms_name = pms_naming(pred, obj)
|
69
|
+
pms_matched?(pms_name, in: pms_of_defined_local_role(self.name))
|
70
|
+
end
|
71
|
+
|
72
|
+
alias locally_can? temporarily_can?
|
73
|
+
|
74
|
+
def local_permissions
|
75
|
+
@local_permissions ||= [ ]
|
76
|
+
end
|
77
|
+
|
78
|
+
alias local_permission_names local_permissions
|
79
|
+
|
80
|
+
def stored_permission_names
|
81
|
+
stored_permissions.map(&:name)
|
82
|
+
end
|
83
|
+
|
84
|
+
# TODO: show by hash
|
85
|
+
def permissions
|
86
|
+
local_permission_names + stored_permission_names
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'i_am_i_can/permission/helpers'
|
2
|
+
require 'i_am_i_can/permission/p_array'
|
3
|
+
|
4
|
+
module IAmICan
|
5
|
+
module Permission
|
6
|
+
module Definition
|
7
|
+
include Helpers::Cls
|
8
|
+
|
9
|
+
def which(name:)
|
10
|
+
find_by!(name: name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def have_permission *preds, obj: nil, desc: nil, save: config.default_save
|
14
|
+
failed_items = [ ]
|
15
|
+
|
16
|
+
preds.each do |pred|
|
17
|
+
pms_name = pms_naming(pred, obj)
|
18
|
+
description = desc || pms_name.to_s.tr('_', ' ')
|
19
|
+
if save
|
20
|
+
failed_items << pms_name unless _to_store_permission(pred, obj, desc: description)
|
21
|
+
else
|
22
|
+
failed_items << pms_name if pms_name.in?(defined_local_permissions.keys)
|
23
|
+
defined_local_permissions[pms_name] ||= { desc: description }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
_pms_definition_result(preds, obj, failed_items)
|
28
|
+
end
|
29
|
+
|
30
|
+
alias have_permissions have_permission
|
31
|
+
alias has_permission have_permission
|
32
|
+
alias has_permissions have_permission
|
33
|
+
|
34
|
+
def declare_permission *preds, **options
|
35
|
+
have_permission *preds, **options, save: false
|
36
|
+
end
|
37
|
+
|
38
|
+
alias declare_permissions declare_permission
|
39
|
+
|
40
|
+
def defined_local_permissions
|
41
|
+
@defined_local_permissions ||= { }
|
42
|
+
end
|
43
|
+
|
44
|
+
def defined_stored_pms_names
|
45
|
+
config.permission_model.all.map(&:name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def defined_stored_permissions
|
49
|
+
config.permission_model.all.map { |pms| [ pms.name, pms.desc ] }.to_h
|
50
|
+
end
|
51
|
+
|
52
|
+
def defined_permissions
|
53
|
+
defined_local_permissions.deep_merge(defined_stored_permissions)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.extended(kls)
|
57
|
+
kls.delegate :defined_permissions, :pms_naming, :deconstruct_obj, :pms_of_defined_local_role, to: kls
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module Permission
|
3
|
+
module Helpers
|
4
|
+
module Cls
|
5
|
+
def _pms_definition_result(preds, obj, failed_items)
|
6
|
+
prefix = 'Permission Definition Done'
|
7
|
+
fail_msg = prefix + ", but #{failed_items} have been defined" if failed_items.present?
|
8
|
+
raise Error, fail_msg if config.strict_mode && fail_msg
|
9
|
+
fail_msg ? fail_msg : prefix
|
10
|
+
end
|
11
|
+
|
12
|
+
def _to_store_permission(pred, obj, **options)
|
13
|
+
return false if config.permission_model.exists?(pred, obj)
|
14
|
+
config.permission_model.create!(pred: pred, **deconstruct_obj(obj), **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def pms_naming(pred, obj)
|
18
|
+
config.permission_model.naming(pred, obj)
|
19
|
+
end
|
20
|
+
|
21
|
+
def deconstruct_obj(obj)
|
22
|
+
config.permission_model.deconstruct_obj(obj)
|
23
|
+
end
|
24
|
+
|
25
|
+
def pms_of_defined_local_role(role_name)
|
26
|
+
config.subject_model.defined_local_roles[role_name.to_sym]&.[](:permissions) || []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Ins
|
31
|
+
def _pms_assignment_result(preds, obj, not_defined_items, covered_items = nil, strict_mode = false)
|
32
|
+
prefix = 'Permission Assignment Done'
|
33
|
+
msg1 = "#{not_defined_items} have not been defined" if not_defined_items.present?
|
34
|
+
msg2 = "#{covered_items} have been covered" if covered_items.present?
|
35
|
+
fail_msg = prefix + ', but ' + [msg1, msg2].compact.join(', ') if msg1 || msg2
|
36
|
+
raise Error, fail_msg if (strict_mode || config.strict_mode) && fail_msg
|
37
|
+
fail_msg ? fail_msg : prefix
|
38
|
+
end
|
39
|
+
|
40
|
+
def pms_matched?(pms_name, plist)
|
41
|
+
config.permission_model.matched?(pms_name, in: plist[:in])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module Permission
|
3
|
+
class PArray < ::Array
|
4
|
+
attr_accessor :pms
|
5
|
+
|
6
|
+
def matched?(pms_name)
|
7
|
+
return false if self.blank?
|
8
|
+
self.pms = pms_name.to_sym
|
9
|
+
found? || covered?
|
10
|
+
end
|
11
|
+
|
12
|
+
def found?
|
13
|
+
pms.in? self
|
14
|
+
end
|
15
|
+
|
16
|
+
def covered?
|
17
|
+
pred, obj_type, obj_id = pms.to_s.split('_')
|
18
|
+
pred.to_sym.in?(self) || :"#{pred}_#{obj_type}".in?(self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'i_am_i_can/permission/p_array'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Permission
|
5
|
+
def matched?(pms_name = nil, pred: nil, obj: nil, **options)
|
6
|
+
PArray.new(options[:in]).matched?(pms_name || naming(pred, obj))
|
7
|
+
end
|
8
|
+
|
9
|
+
def which(pred:, obj: nil)
|
10
|
+
find_by!(pred: pred, **deconstruct_obj(obj))
|
11
|
+
end
|
12
|
+
|
13
|
+
def naming(pred, obj)
|
14
|
+
obj_type, obj_id = deconstruct_obj(obj).values
|
15
|
+
otp = "_#{obj_type}" if obj_type.present?
|
16
|
+
oid = "_#{obj_id}" if obj_id.present?
|
17
|
+
[pred, otp, oid].join.to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
def deconstruct_obj(obj)
|
21
|
+
return { } unless obj
|
22
|
+
|
23
|
+
if obj.is_a?(String) || obj.is_a?(Symbol)
|
24
|
+
{ obj_type: obj }
|
25
|
+
elsif obj.respond_to?(:attributes)
|
26
|
+
{ obj_type: obj.class.name, obj_id: obj.id }
|
27
|
+
else
|
28
|
+
{ obj_type: obj.to_s }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def exists?(pred, obj)
|
33
|
+
super(pred: pred, **deconstruct_obj(obj))
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.extended(kls)
|
37
|
+
kls.include InstanceMethods
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# === End of ClassMethods ===
|
42
|
+
|
43
|
+
module Permission::InstanceMethods
|
44
|
+
# like: manage_User_1
|
45
|
+
def name
|
46
|
+
otp = "_#{obj_type}" if obj_type.present?
|
47
|
+
oid = "_#{obj_id}" if obj_id.present?
|
48
|
+
[pred, otp, oid].join.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
def assign_to role: nil, group: nil
|
52
|
+
obj = if role
|
53
|
+
role.is_a?(Symbol) ? ii_config.role_model.find(name: role) : role
|
54
|
+
else
|
55
|
+
group.is_a?(Symbol) ? ii_config.role_group_model.find(name: role) : group
|
56
|
+
end
|
57
|
+
obj.have_permission self.pred, obj: self.obj
|
58
|
+
end
|
59
|
+
|
60
|
+
alias is_assigned_to assign_to
|
61
|
+
|
62
|
+
# :user, User, user
|
63
|
+
def obj
|
64
|
+
return obj_type.constantize.find(obj_id) if obj_id.present?
|
65
|
+
obj_type[/[A-Z]/] ? obj_type.constantize : obj_type.to_sym
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'i_am_i_can/role/helpers'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Role
|
5
|
+
module Assignment
|
6
|
+
include Helpers::Ins
|
7
|
+
|
8
|
+
def becomes_a *roles, which_can: [ ], obj: nil, auto_define_before: ii_config.auto_define_before, save: ii_config.default_save
|
9
|
+
should_define_role = which_can.present? || auto_define_before
|
10
|
+
self.class.have_roles *roles, which_can: which_can, obj: obj, save: save if should_define_role
|
11
|
+
failed_items = [ ]
|
12
|
+
|
13
|
+
roles.each do |role|
|
14
|
+
if save
|
15
|
+
failed_items << role unless stored_roles_add(role)
|
16
|
+
else
|
17
|
+
next failed_items << role unless role.in?(defined_roles.keys)
|
18
|
+
local_role_names << role unless role.in?(local_role_names)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
_role_assignment_result(roles, failed_items)
|
23
|
+
end
|
24
|
+
|
25
|
+
alias is becomes_a
|
26
|
+
alias is_a_role becomes_a
|
27
|
+
alias is_roles becomes_a
|
28
|
+
alias has_role becomes_a
|
29
|
+
alias has_roles becomes_a
|
30
|
+
alias role_is becomes_a
|
31
|
+
alias roles_are becomes_a
|
32
|
+
|
33
|
+
def temporarily_is *roles, **options
|
34
|
+
becomes_a *roles, save: false, **options
|
35
|
+
end
|
36
|
+
|
37
|
+
alias locally_is temporarily_is
|
38
|
+
|
39
|
+
def falls_from *roles, saved: ii_config.default_save
|
40
|
+
failed_items = [ ]
|
41
|
+
|
42
|
+
roles.each do |role|
|
43
|
+
if saved
|
44
|
+
failed_items << role unless stored_roles_rmv(role)
|
45
|
+
else
|
46
|
+
next failed_items << role unless role.in?(defined_roles.keys)
|
47
|
+
local_role_names.delete(role)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
_role_assignment_result(roles, failed_items)
|
52
|
+
end
|
53
|
+
|
54
|
+
alias is_not_a falls_from
|
55
|
+
alias will_not_be falls_from
|
56
|
+
alias removes_role falls_from
|
57
|
+
alias leaves falls_from
|
58
|
+
alias has_not_role falls_from
|
59
|
+
alias has_not_roles falls_from
|
60
|
+
|
61
|
+
def local_role_names
|
62
|
+
@local_role_names ||= [ ]
|
63
|
+
end
|
64
|
+
|
65
|
+
def local_roles
|
66
|
+
defined_local_roles.slice(*local_role_names)
|
67
|
+
end
|
68
|
+
|
69
|
+
def roles
|
70
|
+
local_role_names + stored_role_names
|
71
|
+
end
|
72
|
+
|
73
|
+
alias role_names roles
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'i_am_i_can/role/helpers'
|
2
|
+
|
3
|
+
module IAmICan
|
4
|
+
module Role
|
5
|
+
module Definition
|
6
|
+
include Helpers::Cls
|
7
|
+
|
8
|
+
def have_role *names, desc: nil, save: ii_config.default_save, which_can: [ ], obj: nil
|
9
|
+
failed_items, preds = [ ], which_can
|
10
|
+
|
11
|
+
names.each do |name|
|
12
|
+
description = desc || name.to_s.humanize
|
13
|
+
if save
|
14
|
+
next failed_items << name unless _to_store_role(name, desc: description)
|
15
|
+
ii_config.role_model.which(name: name).can *preds, obj: obj, auto_define_before: true, strict_mode: true if which_can.present?
|
16
|
+
else
|
17
|
+
next failed_items << name if defined_local_roles.key?(name)
|
18
|
+
defined_local_roles[name] ||= { desc: description, permissions: [ ] }
|
19
|
+
local_role_which(name: name, can: preds, obj: obj, auto_define_before: true, strict_mode: true) if which_can.present?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
_role_definition_result(names, failed_items)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias have_roles have_role
|
27
|
+
alias has_role have_role
|
28
|
+
alias has_roles have_role
|
29
|
+
|
30
|
+
def declare_role *names, **options
|
31
|
+
have_role *names, save: false, **options
|
32
|
+
end
|
33
|
+
|
34
|
+
alias declare_roles has_role
|
35
|
+
|
36
|
+
def group_roles *members, by_name:, which_can: [ ], obj: nil
|
37
|
+
raise Error, 'Some of members have not been defined' unless (members - defined_stored_role_names).empty?
|
38
|
+
raise Error, "Given name #{by_name} has been used by a role" if ii_config.role_model.exists?(name: by_name)
|
39
|
+
ii_config.role_group_model.find_or_create_by!(name: by_name).members_add(members)
|
40
|
+
end
|
41
|
+
|
42
|
+
alias group_role group_roles
|
43
|
+
alias groups_role group_roles
|
44
|
+
alias groups_roles group_roles
|
45
|
+
|
46
|
+
def have_and_group_roles *members, by_name:
|
47
|
+
has_roles *members
|
48
|
+
group_roles *members, by_name: by_name
|
49
|
+
end
|
50
|
+
|
51
|
+
alias has_and_groups_roles have_and_group_roles
|
52
|
+
|
53
|
+
# permission assignment locally for local role
|
54
|
+
# User.local_role_which(name: :admin, can: :fly)
|
55
|
+
# same effect to: UserRole.new(name: :admin).temporarily_can :fly
|
56
|
+
def local_role_which(name:, can:, obj: nil, **options)
|
57
|
+
ii_config.role_model.new(name: name).temporarily_can *Array(can), obj: obj, **options
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.extended(kls)
|
61
|
+
kls.delegate :defined_local_roles, :defined_stored_roles, :defined_roles, to: kls
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# === End of MainMethods ===
|
66
|
+
|
67
|
+
module Definition::SecondaryMethods
|
68
|
+
def defined_local_roles
|
69
|
+
@local_roles ||= { }
|
70
|
+
end
|
71
|
+
|
72
|
+
def defined_stored_role_names
|
73
|
+
ii_config.role_model.pluck(:name).map(&:to_sym)
|
74
|
+
end
|
75
|
+
|
76
|
+
def defined_stored_roles
|
77
|
+
ii_config.role_model.all.map { |role| [ role.name.to_sym, role.desc ] }.to_h
|
78
|
+
end
|
79
|
+
|
80
|
+
def defined_roles
|
81
|
+
defined_local_roles.deep_merge(defined_stored_roles)
|
82
|
+
end
|
83
|
+
|
84
|
+
def defined_role_group_names
|
85
|
+
ii_config.role_group_model.pluck(:name).map(&:to_sym)
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
def defined_role_groups
|
90
|
+
ii_config.role_group_model.all.map { |group| [ group.name.to_sym, group.member_names.map(&:to_sym) ] }.to_h
|
91
|
+
end
|
92
|
+
|
93
|
+
def members_of_role_group name
|
94
|
+
ii_config.role_group_model.find_by!(name: name).member_names
|
95
|
+
end
|
96
|
+
|
97
|
+
Definition.include self
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module Role
|
3
|
+
module Helpers
|
4
|
+
module Cls
|
5
|
+
def _to_store_role name, **options
|
6
|
+
return false if ii_config.role_model.exists?(name: name) || ii_config.role_group_model.exists?(name: name)
|
7
|
+
ii_config.role_model.create!(name: name, **options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def _role_definition_result(names, failed_items)
|
11
|
+
prefix = 'Role Definition Done'
|
12
|
+
fail_msg = prefix + ", but name #{failed_items} have been used by other role or group" if failed_items.present?
|
13
|
+
raise Error, fail_msg if ii_config.strict_mode && fail_msg
|
14
|
+
fail_msg ? fail_msg : prefix
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Ins
|
19
|
+
def _role_assignment_result(names, failed_items)
|
20
|
+
prefix = 'Role Assignment Done'
|
21
|
+
fail_msg = prefix + ", but #{failed_items} have not been defined" if failed_items.present?
|
22
|
+
raise Error, fail_msg if ii_config.strict_mode && fail_msg
|
23
|
+
fail_msg ? fail_msg : prefix
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module IAmICan
|
2
|
+
module Subject
|
3
|
+
module PermissionQuerying
|
4
|
+
def can? pred, obj0 = nil, obj: nil, without_group: false
|
5
|
+
obj = obj0 || obj
|
6
|
+
return true if temporarily_can?(pred, obj)
|
7
|
+
return true if stored_can?(pred, obj)
|
8
|
+
group_can?(pred, obj, without_group)
|
9
|
+
end
|
10
|
+
|
11
|
+
def cannot? pred, obj0 = nil, obj: nil
|
12
|
+
!can? pred, obj0, obj: obj
|
13
|
+
end
|
14
|
+
|
15
|
+
def can! pred, obj0 = nil, obj: nil
|
16
|
+
raise InsufficientPermission if cannot? pred, obj0, obj: obj
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def can_each? preds, obj0 = nil, obj: nil
|
21
|
+
preds.each { |pred| return false if cannot? pred, obj0, obj: obj } && true
|
22
|
+
end
|
23
|
+
|
24
|
+
alias can_every? can_each?
|
25
|
+
|
26
|
+
def can_each! preds, obj0 = nil, obj: nil
|
27
|
+
preds.each { |pred| can! pred, obj0, obj: obj } && true
|
28
|
+
end
|
29
|
+
|
30
|
+
alias can_every! can_each!
|
31
|
+
|
32
|
+
def can_one_of? preds, obj0 = nil, obj: nil
|
33
|
+
preds.each { |pred| return true if can? pred, obj0, obj: obj } && false
|
34
|
+
end
|
35
|
+
|
36
|
+
def can_one_of! preds, obj0 = nil, obj: nil
|
37
|
+
raise InsufficientPermission unless can_one_of? preds, obj0, obj: obj
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def temporarily_can? pred, obj
|
42
|
+
ii_config.permission_model.matched?(pred: pred, obj: obj, in: permissions_of_local_roles)
|
43
|
+
end
|
44
|
+
|
45
|
+
alias locally_can? temporarily_can?
|
46
|
+
|
47
|
+
def stored_can? pred, obj
|
48
|
+
ii_config.permission_model.matched?(pred: pred, obj: obj, in: permissions_of_stored_roles)
|
49
|
+
end
|
50
|
+
|
51
|
+
def group_can? pred, obj, without_group = false
|
52
|
+
return false if without_group || ii_config.without_group
|
53
|
+
ii_config.permission_model.matched?(pred: pred, obj: obj, in: permissions_of_role_groups)
|
54
|
+
end
|
55
|
+
|
56
|
+
def permissions_of_stored_roles
|
57
|
+
stored_roles.stored_permissions.map(&:name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def permissions_of_role_groups
|
61
|
+
stored_roles.related_role_groups.stored_permissions.map(&:name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def permissions_of_local_roles
|
65
|
+
local_roles.map { |(name, info)| info[:permissions] }.flatten.uniq
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|