mikras_utils 0.13.0 → 0.14.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/lib/mikras_utils/mkacl/analyzer.rb +17 -11
- data/lib/mikras_utils/mkacl/generators/seeds.rb +5 -6
- data/lib/mikras_utils/mkacl/parser.rb +43 -61
- data/lib/mikras_utils/mkacl/spec.rb +89 -74
- data/lib/mikras_utils/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09748841bbccd2fc59e03f01fd240ac333963e7f6e8f35ed68d3d8268fdb4a97'
|
4
|
+
data.tar.gz: b058155721495e702725d5cf1a68d37139e3c8abe82089939e3c018ac5b3e7b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b25c043d64872f36c9b8d92e0f86d314164ef19695273255030d685e42497d0f7497395cf66718a41bf65e2ee6d4a4c1fad7db1b9202358ca6180e30df88eb76
|
7
|
+
data.tar.gz: b66d7b04e85ff125b6f8b5dc143bdb7893db08686c036899ca37ac0aa6aa7dd58edda99b976962266ebae4e00343fd80d1e66aa9c719846bf327dedc42c89888
|
@@ -15,6 +15,23 @@ module MkAcl
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def analyze
|
18
|
+
# Check select-mutations are nil
|
19
|
+
spec.tables.each { |table|
|
20
|
+
table.actions.each { |name, action|
|
21
|
+
if name == "select"
|
22
|
+
action.rules.each { |rule|
|
23
|
+
rule.mutation.nil? or
|
24
|
+
raise ArgumentError, "Can't use mutations in select actions in table #{table.name}"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
# Assign default detach
|
31
|
+
spec.tables.each { |table|
|
32
|
+
table.attach&.copy(:detach) if table.detach.nil?
|
33
|
+
}
|
34
|
+
|
18
35
|
# Find child-parent relations between linked tables
|
19
36
|
links = conn.tuples %(
|
20
37
|
select
|
@@ -66,17 +83,6 @@ module MkAcl
|
|
66
83
|
end
|
67
84
|
}
|
68
85
|
|
69
|
-
# Set mutation to nil select/attach/detach actions
|
70
|
-
spec.tables.each { |table|
|
71
|
-
table.actions.each { |name, action|
|
72
|
-
if %w(select attach detach).include?(name)
|
73
|
-
action.rules.each { |rule|
|
74
|
-
rule.mutation = nil
|
75
|
-
}
|
76
|
-
end
|
77
|
-
}
|
78
|
-
}
|
79
|
-
|
80
86
|
# Resolve domains
|
81
87
|
spec.tables.select(&:acl).each { |t| resolve_domain(t) }
|
82
88
|
|
@@ -21,7 +21,7 @@ module MkAcl
|
|
21
21
|
private
|
22
22
|
def clean_tables
|
23
23
|
puts %(
|
24
|
-
delete from acl_portal.
|
24
|
+
delete from acl_portal.acl_stamps;
|
25
25
|
delete from acl_portal.acl_rules;
|
26
26
|
delete from acl_portal.acl_actions;
|
27
27
|
delete from acl_portal.acl_tables;
|
@@ -68,14 +68,13 @@ module MkAcl
|
|
68
68
|
puts
|
69
69
|
}
|
70
70
|
|
71
|
-
action.
|
71
|
+
action.stamps.each { |stamp|
|
72
72
|
puts %(
|
73
|
-
insert into acl_portal.
|
73
|
+
insert into acl_portal.acl_stamps (action_id, watch, stamp)
|
74
74
|
values (
|
75
75
|
:action_id,
|
76
|
-
#{conn.quote_value(
|
77
|
-
#{conn.quote_value(
|
78
|
-
#{conn.quote_value(timestamp.stamp)}
|
76
|
+
#{conn.quote_value(stamp.watch)},
|
77
|
+
#{conn.quote_value(stamp.stamp)}
|
79
78
|
);
|
80
79
|
).align
|
81
80
|
puts
|
@@ -56,65 +56,23 @@ module MkAcl
|
|
56
56
|
|
57
57
|
def parse_actions(table, actions)
|
58
58
|
for action_name, entries in actions
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
entries = [{ roles: entries }]
|
75
|
-
when Array
|
76
|
-
# Arrays can't specify mutations (for now)
|
77
|
-
entries.reject! { |element|
|
78
|
-
timestamps = element.delete(:timestamps) and parse_timestamps(action, timestamps)
|
79
|
-
}
|
80
|
-
end
|
81
|
-
parse_rules(action, entries)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def parse_timestamps(table, timestamps)
|
87
|
-
# Normalize timestamps
|
88
|
-
timestamps = [timestamps] if timestamps.is_a?(Hash)
|
89
|
-
|
90
|
-
case timestamps
|
91
|
-
when String
|
92
|
-
timestamps.split.each { |watch_field|
|
93
|
-
case watch_field
|
94
|
-
when /^(.*)(?:_by_id|_to_id)$/
|
95
|
-
Timestamp.new(table, watch_field, nil, "#{$1}_at")
|
96
|
-
when /^(created|updated|deleted)_at$/
|
97
|
-
Timestamp.new(table, nil, "#{$1}_by_id", watch_field)
|
98
|
-
else
|
99
|
-
raise ArgumentError, "Illegal timestamp specifier: #{watch_field.inspect}"
|
100
|
-
end
|
101
|
-
}
|
102
|
-
|
103
|
-
when Array
|
104
|
-
for entry in timestamps
|
105
|
-
timestamp = Timestamp.new(table)
|
106
|
-
for key, value in entry
|
107
|
-
case key
|
108
|
-
when :watch; timestamp.watch = value
|
109
|
-
when :assign; timestamp.assign = value
|
110
|
-
when :stamp; timestamp.stamp = value
|
111
|
-
else
|
112
|
-
error "Illegal field '#{key}' in #{table} timestamps"
|
113
|
-
end
|
59
|
+
ACTIONS.include?(action_name.to_s) or error "Illegal action '#{action_name}'"
|
60
|
+
constrain?(entries, String, Array, Hash) or
|
61
|
+
error "Illegal value for #{action_name} entry '#{entries}'"
|
62
|
+
|
63
|
+
action = Action.new(table, action_name)
|
64
|
+
|
65
|
+
# Normalize entries
|
66
|
+
entries =
|
67
|
+
case entries
|
68
|
+
when Hash
|
69
|
+
[entries]
|
70
|
+
when String
|
71
|
+
[{ roles: entries }]
|
72
|
+
when Array
|
73
|
+
entries
|
114
74
|
end
|
115
|
-
|
116
|
-
else
|
117
|
-
error "Illegal timestamp type: #{timestamps.class}"
|
75
|
+
parse_rules(action, entries)
|
118
76
|
end
|
119
77
|
end
|
120
78
|
|
@@ -122,8 +80,8 @@ module MkAcl
|
|
122
80
|
ordinal = 0
|
123
81
|
|
124
82
|
for entry in rules
|
125
|
-
timestamps = entry.delete(:timestamps) and parse_timestamps(action, timestamps)
|
126
83
|
rule = Rule.new(action, ordinal += 1)
|
84
|
+
stamps = entry.delete(:stamps) and parse_stamps(rule, stamps)
|
127
85
|
for key, value in entry
|
128
86
|
case key
|
129
87
|
when :roles; rule.roles = norm_array(value)
|
@@ -141,11 +99,35 @@ module MkAcl
|
|
141
99
|
error "At least one rule is required in #{action.table}.#{action}"
|
142
100
|
end
|
143
101
|
end
|
102
|
+
|
103
|
+
def parse_stamps(rule, stamps)
|
104
|
+
constrain rule, Rule
|
105
|
+
constrain stamps, String, Hash, Array
|
106
|
+
# constrain stamps, String, Hash[watch: NilClass | String | [String], stamp: String | [String]]
|
107
|
+
|
108
|
+
# Normalize stamps
|
109
|
+
stamps = Array(stamps)
|
110
|
+
|
111
|
+
stamps.map! { |stamp|
|
112
|
+
case stamp
|
113
|
+
when Hash; stamp
|
114
|
+
when String; { stamp: stamp }
|
115
|
+
else
|
116
|
+
raise ArgumentError, "Illegal element in #{rule.name} rule in table #{rule.table.name}"
|
117
|
+
end
|
118
|
+
}
|
119
|
+
|
120
|
+
# Check fields and create stamp
|
121
|
+
stamps.each { |stamp|
|
122
|
+
# Check fields
|
123
|
+
diff = stamp.keys - [:watch, :stamp]
|
124
|
+
diff.empty? or raise "Illegal field '#{diff.first}' in #{rule.name} rule in table #{rule.table.name}"
|
125
|
+
Stamp.new(rule, stamp[:watch]&.split, stamp[:stamp].split)
|
126
|
+
}
|
127
|
+
end
|
144
128
|
end
|
145
129
|
end
|
146
130
|
|
147
|
-
#def declare_record(name, array_arg = nil, &record_
|
148
|
-
|
149
131
|
|
150
132
|
|
151
133
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|
module MkAcl
|
3
3
|
DEFAULT_MUTATION = "app_portal.mutate"
|
4
|
+
ACTIONS = %w(insert select update delete attach detach)
|
4
5
|
|
5
6
|
class Spec
|
6
7
|
# Source SPEC file. Only for informational purposes
|
@@ -33,7 +34,6 @@ module MkAcl
|
|
33
34
|
indent {
|
34
35
|
puts "app_schema: #{app_schema}"
|
35
36
|
puts "acl_schema: #{acl_schema}"
|
36
|
-
puts
|
37
37
|
tables.map(&:dump)
|
38
38
|
}
|
39
39
|
end
|
@@ -74,20 +74,22 @@ module MkAcl
|
|
74
74
|
# True if the portal object triggers should be active on the table
|
75
75
|
attr_accessor :triggers
|
76
76
|
|
77
|
-
#
|
77
|
+
# True if the table is under RLS control
|
78
78
|
attr_accessor :acl
|
79
79
|
|
80
80
|
# Associated record name. Used in function names and in conversions
|
81
81
|
attr_reader :record_name
|
82
82
|
|
83
|
-
#
|
83
|
+
# Hash from action name to action object
|
84
|
+
attr_reader :actions
|
85
|
+
|
86
|
+
# Action objects
|
84
87
|
def insert = @actions["insert"]
|
85
88
|
def select = @actions["select"]
|
86
89
|
def update = @actions["update"]
|
87
90
|
def delete = @actions["delete"]
|
88
|
-
|
89
|
-
|
90
|
-
attr_reader :actions
|
91
|
+
def attach = @actions["attach"]
|
92
|
+
def detach = @actions["detach"]
|
91
93
|
|
92
94
|
def initialize(spec, name, domain, parent_name, triggers, acl)
|
93
95
|
@spec = spec
|
@@ -102,31 +104,37 @@ module MkAcl
|
|
102
104
|
@acl = acl
|
103
105
|
@actions = {}
|
104
106
|
@spec.send :attach_table, self
|
105
|
-
|
106
|
-
|
107
|
-
|
107
|
+
# for action_name in ACTIONS
|
108
|
+
# attach_action(Action.new(self, action_name))
|
109
|
+
# end
|
108
110
|
end
|
109
111
|
|
110
112
|
def to_s() = name
|
111
113
|
def inspect() "<#{self.class}#name #{name.inspect}>" end
|
112
114
|
|
113
115
|
def dump
|
116
|
+
puts
|
114
117
|
puts "#{name}:"
|
115
118
|
indent {
|
116
119
|
puts "domain: #{domain}" if domain
|
117
120
|
puts "parent: #{parent}" if parent
|
118
|
-
puts "references:
|
119
|
-
for action_name in
|
120
|
-
actions
|
121
|
+
puts "references: #{references.values.map { |k,v| "#{v}->#{k.name}" }.join(' ')}"
|
122
|
+
for action_name in ACTIONS
|
123
|
+
next if !actions.key? action_name
|
124
|
+
|
125
|
+
if !(action_name == "detach" && attach.rules == detach.rules)
|
126
|
+
if action_name == "attach" && attach.rules == detach.rules
|
127
|
+
header = "attach/detach"
|
128
|
+
else
|
129
|
+
header = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
action = actions[action_name]
|
133
|
+
actions[action_name]&.dump(header)
|
134
|
+
end
|
121
135
|
end
|
122
|
-
|
123
136
|
puts "triggers: #{triggers}"
|
124
|
-
|
125
|
-
when false; puts "acl: false"
|
126
|
-
when true;
|
127
|
-
puts "acl:"
|
128
|
-
indent { puts acl }
|
129
|
-
end
|
137
|
+
puts "acl: #{acl}"
|
130
138
|
}
|
131
139
|
end
|
132
140
|
|
@@ -140,38 +148,34 @@ module MkAcl
|
|
140
148
|
attr_reader :table
|
141
149
|
attr_reader :name
|
142
150
|
attr_reader :rules
|
143
|
-
attr_reader :timestamps
|
144
151
|
|
145
152
|
def initialize(table, name)
|
146
153
|
@table = table
|
147
154
|
@name = name.to_s
|
148
155
|
@rules = []
|
149
|
-
@timestamps = []
|
150
156
|
@table.send :attach_action, self
|
151
157
|
end
|
152
158
|
|
159
|
+
def copy(name)
|
160
|
+
cp = Action.new(self.table, name)
|
161
|
+
cp.rules.concat self.rules
|
162
|
+
cp
|
163
|
+
end
|
164
|
+
|
153
165
|
def to_s() name end
|
154
166
|
|
155
|
-
def dump
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
167
|
+
def dump(header)
|
168
|
+
header = "#{header || name}:"
|
169
|
+
if rules.size == 1
|
170
|
+
puts header
|
171
|
+
indent { rules.first.dump }
|
172
|
+
elsif rules.size > 1
|
173
|
+
puts header
|
160
174
|
indent {
|
161
175
|
for rule in rules.sort_by(&:ordinal)
|
162
176
|
print "- "
|
163
177
|
indent(bol: false) { rule.dump }
|
164
178
|
end
|
165
|
-
|
166
|
-
if !timestamps.empty?
|
167
|
-
puts "- timestamps:"
|
168
|
-
indent {
|
169
|
-
for timestamp in timestamps
|
170
|
-
print "- "
|
171
|
-
indent(bol: false) { timestamp.dump }
|
172
|
-
end
|
173
|
-
}
|
174
|
-
end
|
175
179
|
}
|
176
180
|
end
|
177
181
|
end
|
@@ -180,36 +184,6 @@ module MkAcl
|
|
180
184
|
def attach_rule(rule)
|
181
185
|
@rules << rule
|
182
186
|
end
|
183
|
-
|
184
|
-
def attach_timestamp(timestamp)
|
185
|
-
@timestamps << timestamp
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
class Timestamp
|
190
|
-
attr_reader :action
|
191
|
-
forward_to :action, :table, :name
|
192
|
-
attr_accessor :watch
|
193
|
-
attr_accessor :assign
|
194
|
-
attr_accessor :stamp
|
195
|
-
|
196
|
-
# def created_at?() = watch == "insert"
|
197
|
-
# def updated_at?() = watch == "update"
|
198
|
-
# def deleted_at?() = watch == "delete"
|
199
|
-
|
200
|
-
def initialize(action, watch = nil, assign = nil, stamp = nil)
|
201
|
-
@action = action
|
202
|
-
@watch, @assign, @stamp = watch, assign, stamp
|
203
|
-
action.send :attach_timestamp, self
|
204
|
-
end
|
205
|
-
|
206
|
-
def to_s() = watch
|
207
|
-
|
208
|
-
def dump
|
209
|
-
puts "watch: #{watch}" if watch
|
210
|
-
puts "assign: #{assign}" if assign
|
211
|
-
puts "stamp: #{stamp}" if stamp
|
212
|
-
end
|
213
187
|
end
|
214
188
|
|
215
189
|
class Rule
|
@@ -220,9 +194,10 @@ module MkAcl
|
|
220
194
|
attr_accessor :roles # Roles that this rule applies to
|
221
195
|
attr_accessor :filter # Goes into the postgres policy, may be nil
|
222
196
|
attr_accessor :assert # Goes into the postgres trigger, may be nil
|
223
|
-
attr_accessor :mutation # Mutation function to use, may be nil. Default app_portal.mutate()
|
197
|
+
attr_accessor :mutation # Mutation function to use, may be nil. Default app_portal.mutate() except attach/detach
|
224
198
|
attr_accessor :fields # Only used for insert and update, nil otherwise
|
225
199
|
attr_accessor :tables # Only used for attach and detach, nil otherwise
|
200
|
+
attr_accessor :stamps
|
226
201
|
attr_reader :ordinal
|
227
202
|
|
228
203
|
# admin, internal, etc.
|
@@ -237,23 +212,63 @@ module MkAcl
|
|
237
212
|
@roles = []
|
238
213
|
@filter = nil
|
239
214
|
@assert = nil
|
240
|
-
@mutation = DEFAULT_MUTATION
|
215
|
+
@mutation = (action.name == "select" ? nil : DEFAULT_MUTATION)
|
241
216
|
@fields = %w(insert update).include?(action.name) ? [] : nil
|
242
|
-
@tables = %w(attach).include?(action.name) ? [] : nil
|
217
|
+
@tables = %w(attach detach).include?(action.name) ? [] : nil
|
218
|
+
@stamps = []
|
243
219
|
|
244
220
|
action.send :attach_rule, self
|
245
221
|
end
|
246
222
|
|
247
223
|
def dump
|
248
|
-
puts "roles:
|
224
|
+
puts "roles: #{roles.join(' ')}"
|
249
225
|
puts "filter: #{filter}" if filter
|
250
226
|
puts "assert: #{assert}" if assert
|
251
|
-
puts "mutation: #{mutation || 'nil'}"
|
252
|
-
|
253
|
-
puts "
|
254
|
-
|
227
|
+
puts "mutation: #{mutation || 'nil'}" if !mutation.nil? && mutation != DEFAULT_MUTATION
|
228
|
+
puts "fields: #{fields.join(' ')}" if fields && !fields.empty?
|
229
|
+
puts "tables: #{tables.join(' ')}" if tables && !tables.empty?
|
230
|
+
|
231
|
+
if stamps.size == 1
|
232
|
+
stamps.first.dump
|
233
|
+
elsif stamps.size > 1
|
234
|
+
puts "stamps:"
|
235
|
+
indent {
|
236
|
+
for stamp in stamps
|
237
|
+
print "- "
|
238
|
+
indent(bol: false) { stamp.dump }
|
239
|
+
end
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
255
243
|
puts "ordinal: #{ordinal}"
|
256
244
|
end
|
245
|
+
private
|
246
|
+
def attach_stamp(stamp)
|
247
|
+
@stamps << stamp
|
248
|
+
end
|
257
249
|
end
|
250
|
+
|
251
|
+
class Stamp
|
252
|
+
attr_reader :rule
|
253
|
+
forward_to :rule, :table, :name
|
254
|
+
|
255
|
+
attr_accessor :watch
|
256
|
+
attr_accessor :stamp
|
257
|
+
|
258
|
+
def initialize(rule, watch = nil, stamp)
|
259
|
+
constrain rule, Rule
|
260
|
+
@rule = rule
|
261
|
+
@watch, @stamp = watch, stamp
|
262
|
+
rule.send :attach_stamp, self
|
263
|
+
end
|
264
|
+
|
265
|
+
def to_s() = watch
|
266
|
+
|
267
|
+
def dump
|
268
|
+
puts "watch: #{watch.join(" ")}" if watch
|
269
|
+
puts "stamp: #{stamp.join(" ")}" if stamp
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
258
273
|
end
|
259
274
|
|
data/lib/mikras_utils/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mikras_utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg_conn
|