i_am_i_can 2.1.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 +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
|