card-mod-permissions 0.11.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/set/abstract/permission.rb +122 -0
- data/set/all/permissions.rb +227 -0
- data/set/all/update_read_rules.rb +12 -0
- data/set/right/create.rb +1 -0
- data/set/right/delete.rb +1 -0
- data/set/right/read.rb +79 -0
- data/set/right/update.rb +1 -0
- data/set/self/create.rb +3 -0
- data/set/self/delete.rb +3 -0
- data/set/self/read.rb +3 -0
- data/set/self/update.rb +3 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 979a6aa27a0485313d0b7c8716ce592eb4a006d8de7b0d72b93818fcaba9e168
|
4
|
+
data.tar.gz: 4ec1cebed12cc29ae51379cdeb255c43483533f39f3bfc5737f97e603757a55c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2561983f02c9b533f50f8e9c3a13f510b83d159d16d75dd5f20173cad22db7451e183cde9ec69f5e83a799c4017e017c8a1c473ff662fbe1ee2e51893e5e4e51
|
7
|
+
data.tar.gz: e536cbe5b71e9f486c224e918b46889a9b23666d7536ad67cdf87237a93830923e193319bac81739e463b9706f0a7a5e403d3f2760b33df9fb54845078175da5
|
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
def standardize_items
|
3
|
+
super unless content == "_left"
|
4
|
+
end
|
5
|
+
|
6
|
+
def options_rule_card
|
7
|
+
Card[:cards_with_account]
|
8
|
+
end
|
9
|
+
|
10
|
+
format :html do
|
11
|
+
view :pointer_core do
|
12
|
+
wrap_with :div, pointer_items, class: "pointer-list"
|
13
|
+
end
|
14
|
+
|
15
|
+
view :core, cache: :never do
|
16
|
+
if card.content == "_left"
|
17
|
+
core_inherit_content
|
18
|
+
else
|
19
|
+
render! :pointer_core
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
view :one_line_content, cache: :never do
|
24
|
+
render_core items: { view: :link }
|
25
|
+
end
|
26
|
+
|
27
|
+
view :input do
|
28
|
+
item_names = inheriting? ? [] : card.item_names
|
29
|
+
%(
|
30
|
+
#{_render_hidden_content_field}
|
31
|
+
<div class="perm-editor">
|
32
|
+
#{inheritance_checkbox}
|
33
|
+
<div class="perm-group perm-vals perm-section">
|
34
|
+
<h5 class="text-muted">Groups</h5>
|
35
|
+
#{groups item_names}
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<div class="perm-indiv perm-vals perm-section">
|
39
|
+
<h5 class="text-muted">Individuals</h5>
|
40
|
+
#{list_input item_list: item_names, extra_css_class: 'perm-indiv-ul'}
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def groups item_names
|
49
|
+
group_options.map do |option|
|
50
|
+
checked = !item_names.delete(option.name).nil?
|
51
|
+
icon = icon_tag "open_in_new", "link-muted"
|
52
|
+
option_link = link_to_card option.name, icon, target: "decko_role"
|
53
|
+
box = check_box_tag "#{option.key}-perm-checkbox",
|
54
|
+
option.name, checked, class: "perm-checkbox-button"
|
55
|
+
<<-HTML
|
56
|
+
<div class="form-check checkbox">
|
57
|
+
<label class="form-check-label">
|
58
|
+
#{box} #{option.name} #{option_link}
|
59
|
+
</label>
|
60
|
+
</div>
|
61
|
+
HTML
|
62
|
+
end * "\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
def group_options
|
66
|
+
Auth.as_bot do
|
67
|
+
Card.search({ type_id: RoleID, sort: "name" }, "roles by name")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def inheritable?
|
72
|
+
@inheritable ||=
|
73
|
+
begin
|
74
|
+
set_name = card.name.trunk_name
|
75
|
+
set_card = Card.fetch(set_name)
|
76
|
+
not_set = set_card && set_card.type_id != SetID
|
77
|
+
not_set ? false : set_card.inheritable?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def inheriting?
|
82
|
+
@inheriting ||= inheritable? && card.content == "_left"
|
83
|
+
end
|
84
|
+
|
85
|
+
def inheritance_checkbox
|
86
|
+
return unless inheritable?
|
87
|
+
<<-HTML
|
88
|
+
<div class="perm-inheritance perm-section">
|
89
|
+
#{check_box_tag 'inherit', 'inherit', inheriting?}
|
90
|
+
<label>
|
91
|
+
#{core_inherit_content}
|
92
|
+
#{wrap_with(:a, title: "use left's #{card.name.tag} rule") { '?' }}
|
93
|
+
</label>
|
94
|
+
</div>
|
95
|
+
HTML
|
96
|
+
end
|
97
|
+
|
98
|
+
def core_inherit_content
|
99
|
+
text = if in_context_of_self_set?
|
100
|
+
core_inherit_for_content_for_self_set
|
101
|
+
else
|
102
|
+
"Inherit from left card"
|
103
|
+
end
|
104
|
+
%(<span class="inherit-perm">#{text}</span>)
|
105
|
+
end
|
106
|
+
|
107
|
+
def in_context_of_self_set?
|
108
|
+
return false unless @set_context
|
109
|
+
@set_context.to_name.tag_name.key == Card[:self].key
|
110
|
+
end
|
111
|
+
|
112
|
+
def core_inherit_for_content_for_self_set
|
113
|
+
task = card.tag.codename
|
114
|
+
ancestor = Card[@set_context.trunk_name.trunk_name]
|
115
|
+
links = ancestor.who_can(task).map do |card_id|
|
116
|
+
link_to_card card_id, nil, target: args[:target]
|
117
|
+
end * ", "
|
118
|
+
"Inherit ( #{links} )"
|
119
|
+
rescue
|
120
|
+
"Inherit"
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module ClassMethods
|
2
|
+
def repair_all_permissions
|
3
|
+
Card.where("(read_rule_class is null or read_rule_id is null) and trash is false")
|
4
|
+
.each do |broken_card|
|
5
|
+
broken_card.include_set_modules
|
6
|
+
broken_card.repair_permissions!
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def repair_permissions!
|
12
|
+
rule_id, rule_class = permission_rule_id_and_class :read
|
13
|
+
update_columns read_rule_id: rule_id, read_rule_class: rule_class
|
14
|
+
end
|
15
|
+
|
16
|
+
# ok? and ok! are public facing methods to approve one action at a time
|
17
|
+
#
|
18
|
+
# fetching: if the optional :trait parameter is supplied, it is passed
|
19
|
+
# to fetch and the test is perfomed on the fetched card, therefore:
|
20
|
+
#
|
21
|
+
# trait: :account would fetch this card plus a tag codenamed :account
|
22
|
+
# trait: :roles, new: {} would initialize a new card with default ({})
|
23
|
+
# options.
|
24
|
+
|
25
|
+
def ok? action
|
26
|
+
@ok ||= {}
|
27
|
+
aok = @ok[Auth.as_id] ||= {}
|
28
|
+
if (cached = aok[action])
|
29
|
+
cached
|
30
|
+
else
|
31
|
+
aok[action] = send "ok_to_#{action}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def ok! action
|
36
|
+
raise Card::Error::PermissionDenied, self unless ok? action
|
37
|
+
end
|
38
|
+
|
39
|
+
def who_can action
|
40
|
+
permission_rule_card(action).item_cards.map(&:id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def anyone_can? action
|
44
|
+
who_can(action).include? AnyoneID
|
45
|
+
end
|
46
|
+
|
47
|
+
def direct_rule_card action
|
48
|
+
direct_rule_id = rule_card_id action
|
49
|
+
require_permission_rule! direct_rule_id, action
|
50
|
+
Card.quick_fetch direct_rule_id
|
51
|
+
end
|
52
|
+
|
53
|
+
def permission_rule_id action
|
54
|
+
if junction? && rule(action).match?(/^\[?\[?_left\]?\]?$/)
|
55
|
+
left_permission_rule_id action
|
56
|
+
else
|
57
|
+
rule_card_id(action)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def permission_rule_id_and_class action
|
62
|
+
[permission_rule_id(action), direct_rule_card(action).rule_class_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def left_permission_rule_id action
|
66
|
+
lcard = left_or_new(skip_virtual: true, skip_modules: true)
|
67
|
+
if action == :create && lcard.real? && lcard.action != :create
|
68
|
+
action = :update
|
69
|
+
end
|
70
|
+
lcard.permission_rule_id action
|
71
|
+
end
|
72
|
+
|
73
|
+
def permission_rule_card action
|
74
|
+
Card.fetch permission_rule_id(action)
|
75
|
+
end
|
76
|
+
|
77
|
+
def require_permission_rule! rule_id, action
|
78
|
+
return if rule_id
|
79
|
+
# RULE missing. should not be possible.
|
80
|
+
# generalize this to handling of all required rules
|
81
|
+
errors.add :permission_denied, tr(:error_no_action_rule, action: action, name: name)
|
82
|
+
raise Card::Error::PermissionDenied, self
|
83
|
+
end
|
84
|
+
|
85
|
+
def rule_class_name
|
86
|
+
trunk.type_id == SetID ? name.trunk_name.tag : nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def you_cant what
|
90
|
+
"You don't have permission to #{what}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def deny_because why
|
94
|
+
@permission_errors << why if @permission_errors
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def permitted? action
|
99
|
+
return false if Card.config.read_only # :read does not call #permit
|
100
|
+
return true if Auth.always_ok?
|
101
|
+
|
102
|
+
Auth.as_card.among? who_can(action)
|
103
|
+
end
|
104
|
+
|
105
|
+
def permit action, verb=nil
|
106
|
+
# not called by ok_to_read
|
107
|
+
if Card.config.read_only
|
108
|
+
deny_because "Currently in read-only mode"
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
|
112
|
+
return true if permitted? action
|
113
|
+
verb ||= action.to_s
|
114
|
+
deny_because you_cant("#{verb} #{name.present? ? name : 'this'}")
|
115
|
+
end
|
116
|
+
|
117
|
+
def ok_to_create
|
118
|
+
return false unless permit :create
|
119
|
+
return true if simple?
|
120
|
+
|
121
|
+
%i[left right].find { |side| !ok_to_create_side side } ? false : true
|
122
|
+
end
|
123
|
+
|
124
|
+
def ok_to_create_side side
|
125
|
+
# left is supercard; create permissions will get checked there.
|
126
|
+
return true if side == :left && superleft
|
127
|
+
part_card = send side, new: {}
|
128
|
+
# if no card, there must be other errors
|
129
|
+
return true unless part_card&.new_card? && !part_card.ok?(:create)
|
130
|
+
|
131
|
+
deny_because you_cant("create #{part_card.name}")
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
def ok_to_read
|
136
|
+
return true if Auth.always_ok?
|
137
|
+
|
138
|
+
self.read_rule_id ||= permission_rule_id :read
|
139
|
+
return true if Auth.as_card.read_rules_hash[read_rule_id]
|
140
|
+
|
141
|
+
deny_because you_cant "read this"
|
142
|
+
end
|
143
|
+
|
144
|
+
def ok_to_update
|
145
|
+
return false unless permit(:update)
|
146
|
+
return true unless type_id_changed? && !permitted?(:create)
|
147
|
+
deny_because you_cant("change to this type (need create permission)")
|
148
|
+
end
|
149
|
+
|
150
|
+
def ok_to_delete
|
151
|
+
permit :delete
|
152
|
+
end
|
153
|
+
|
154
|
+
# don't know why we introduced this
|
155
|
+
# but we have to preserve read rules to make
|
156
|
+
# delete acts visible in recent changes -pk
|
157
|
+
# event :clear_read_rule, :store, on: :delete do
|
158
|
+
# self.read_rule_id = self.read_rule_class = nil
|
159
|
+
# end
|
160
|
+
|
161
|
+
event :set_read_rule, :store, on: :save, changed: %i[type_id name] do
|
162
|
+
read_rule_id, read_rule_class = permission_rule_id_and_class(:read)
|
163
|
+
self.read_rule_id = read_rule_id
|
164
|
+
self.read_rule_class = read_rule_class
|
165
|
+
end
|
166
|
+
|
167
|
+
event :set_field_read_rules, after: :set_read_rule, on: :update, changed: :type_id do
|
168
|
+
each_field_as_bot(&:update_read_rule)
|
169
|
+
end
|
170
|
+
|
171
|
+
def update_field_read_rules
|
172
|
+
return unless type_id_changed? || read_rule_id_changed?
|
173
|
+
each_field_as_bot do |field|
|
174
|
+
field.update_read_rule if field.rule(:read) == "_left"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def each_field_as_bot
|
179
|
+
# find all cards with me as trunk and update their read_rule
|
180
|
+
# (because of *type plus right)
|
181
|
+
# skip if name is updated because will already be resaved
|
182
|
+
Auth.as_bot do
|
183
|
+
fields.each { |field| yield field }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def without_timestamps
|
188
|
+
Card.record_timestamps = false
|
189
|
+
yield
|
190
|
+
ensure
|
191
|
+
Card.record_timestamps = true
|
192
|
+
end
|
193
|
+
|
194
|
+
event :update_read_rule do
|
195
|
+
without_timestamps do
|
196
|
+
reset_patterns # why is this needed?
|
197
|
+
rcard_id, rclass = permission_rule_id_and_class :read
|
198
|
+
# these two are just to make sure vals are correct on current object
|
199
|
+
self.read_rule_id = rcard_id
|
200
|
+
self.read_rule_class = rclass
|
201
|
+
Card.where(id: id).update_all read_rule_id: rcard_id, read_rule_class: rclass
|
202
|
+
expire :hard
|
203
|
+
update_field_read_rules
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def add_to_read_rule_update_queue updates
|
208
|
+
@read_rule_update_queue = Array.wrap(@read_rule_update_queue).concat updates
|
209
|
+
end
|
210
|
+
|
211
|
+
event :check_permissions, :validate do
|
212
|
+
track_permission_errors do
|
213
|
+
ok? action_for_permission_check
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def action_for_permission_check
|
218
|
+
commenting? ? :update : action
|
219
|
+
end
|
220
|
+
|
221
|
+
def track_permission_errors
|
222
|
+
@permission_errors = []
|
223
|
+
result = yield
|
224
|
+
@permission_errors.each { |msg| errors.add :permission_denied, msg }
|
225
|
+
@permission_errors = nil
|
226
|
+
result
|
227
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
# FIXME: the following don't really belong here, but they have to come after
|
3
|
+
# the reference stuff. we need to organize a bit!
|
4
|
+
|
5
|
+
event :update_rule_cache, :finalize, when: :is_rule? do
|
6
|
+
Card::Rule.clear_rule_cache
|
7
|
+
end
|
8
|
+
|
9
|
+
event :expire_related, :finalize do
|
10
|
+
reset_patterns
|
11
|
+
structuree_names.each { |name| Director.expirees << name } if is_structure?
|
12
|
+
end
|
data/set/right/create.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
include_set Abstract::Permission
|
data/set/right/delete.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
include_set Abstract::Permission
|
data/set/right/read.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
include Abstract::Permission
|
2
|
+
|
3
|
+
format :html do include Abstract::Permission::HtmlFormat end
|
4
|
+
|
5
|
+
event :cascade_read_rule, :finalize, after: :update_rule_cache, when: :is_rule? do
|
6
|
+
return unless name_is_changing? || trash_is_changing?
|
7
|
+
|
8
|
+
update_read_ruled_cards
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_read_ruled_cards
|
12
|
+
Card::Rule.clear_read_rule_cache
|
13
|
+
Card.cache.reset # maybe be more surgical, just Auth.user related
|
14
|
+
expire # probably shouldn't be necessary,
|
15
|
+
# but was sometimes getting cached version when card should be in the
|
16
|
+
# trash. could be related to other bugs?
|
17
|
+
|
18
|
+
processed = update_read_rules_of_set_members
|
19
|
+
update_cards_with_read_rule_id processed unless new?
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_read_rules_of_set_members
|
23
|
+
return unless rule_pattern_index
|
24
|
+
|
25
|
+
each_member do |member, processed|
|
26
|
+
processed << member.key
|
27
|
+
member.update_read_rule unless member_has_overriding_rule?(member)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def member_has_overriding_rule? member
|
32
|
+
pattern_index(Card.fetch_id(member.read_rule_class)) < rule_pattern_index
|
33
|
+
end
|
34
|
+
|
35
|
+
# cards with this card as a read_rule_id
|
36
|
+
# These may include cards that are no longer set members if the card was renamed
|
37
|
+
# (edge case)
|
38
|
+
def update_cards_with_read_rule_id processed
|
39
|
+
processed ||= ::Set.new
|
40
|
+
Card::Auth.as_bot do
|
41
|
+
Card.search(read_rule_id: id) do |card|
|
42
|
+
card.update_read_rule unless processed.include?(card.key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_member
|
48
|
+
Auth.as_bot do
|
49
|
+
all_members.each_with_object(::Set.new) do |member, processed|
|
50
|
+
yield member, processed
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def all_members
|
56
|
+
rule_set.item_cards limit: 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def rule_pattern_index
|
60
|
+
return if trash
|
61
|
+
|
62
|
+
@rule_pattern_index ||= pattern_index rule_set&.tag&.id
|
63
|
+
end
|
64
|
+
|
65
|
+
def pattern_index pattern_id
|
66
|
+
pattern_ids.index(pattern_id) || invalid_pattern_id(pattern_id)
|
67
|
+
end
|
68
|
+
|
69
|
+
def pattern_ids
|
70
|
+
@pattern_ids ||= set_patterns.map(&:pattern_id)
|
71
|
+
end
|
72
|
+
|
73
|
+
def invalid_pattern_id pattern_id
|
74
|
+
Rails.logger.info "invalid pattern id for read rule: #{pattern_id}"
|
75
|
+
end
|
76
|
+
|
77
|
+
event :process_read_rule_update_queue, :finalize do
|
78
|
+
left&.update_read_rule
|
79
|
+
end
|
data/set/right/update.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
include_set Abstract::Permission
|
data/set/self/create.rb
ADDED
data/set/self/delete.rb
ADDED
data/set/self/read.rb
ADDED
data/set/self/update.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: card-mod-permissions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ethan McCutchen
|
8
|
+
- Philipp Kühl
|
9
|
+
- Gerry Gleason
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2020-12-24 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: card
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.101.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - '='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 1.101.0
|
29
|
+
description: ''
|
30
|
+
email:
|
31
|
+
- info@decko.org
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- set/abstract/permission.rb
|
37
|
+
- set/all/permissions.rb
|
38
|
+
- set/all/update_read_rules.rb
|
39
|
+
- set/right/create.rb
|
40
|
+
- set/right/delete.rb
|
41
|
+
- set/right/read.rb
|
42
|
+
- set/right/update.rb
|
43
|
+
- set/self/create.rb
|
44
|
+
- set/self/delete.rb
|
45
|
+
- set/self/read.rb
|
46
|
+
- set/self/update.rb
|
47
|
+
homepage: http://decko.org
|
48
|
+
licenses:
|
49
|
+
- GPL-3.0
|
50
|
+
metadata:
|
51
|
+
card-mod: permissions
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.5'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubygems_version: 3.0.3
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: decko permissions
|
71
|
+
test_files: []
|