mikras_utils 0.3.2 → 0.4.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/exe/mkacl +31 -14
- data/exe/rls +30 -0
- data/lib/mikras_utils/mikras.rb +24 -0
- data/lib/mikras_utils/mkacl/analyzer.rb +35 -21
- data/lib/mikras_utils/mkacl/generator.rb +4 -2
- data/lib/mikras_utils/mkacl/generators/acl_functions.rb +26 -9
- data/lib/mikras_utils/mkacl/generators/id_functions.rb +92 -103
- data/lib/mikras_utils/mkacl/generators/role_functions.rb +37 -40
- data/lib/mikras_utils/mkacl/generators/rules.rb +2 -2
- data/lib/mikras_utils/mkacl/generators/seeds.rb +80 -0
- data/lib/mikras_utils/mkacl/parser.rb +39 -15
- data/lib/mikras_utils/mkacl/simple_symtab.rb +58 -0
- data/lib/mikras_utils/mkacl/spec.rb +67 -29
- data/lib/mikras_utils/mkacl.rb +3 -1
- data/lib/mikras_utils/rls/analyzer.rb +108 -0
- data/lib/mikras_utils/rls/parser.rb +70 -0
- data/lib/mikras_utils/rls/spec.rb +86 -0
- data/lib/mikras_utils/version.rb +1 -1
- metadata +10 -2
@@ -1,4 +1,21 @@
|
|
1
1
|
module MkAcl
|
2
|
+
# Generates the functions
|
3
|
+
#
|
4
|
+
# acl_portal.user_is_role(_user_id integer, _object_id integer, _roles text[])
|
5
|
+
# acl_portal.user_is_role(_user_id integer, _object_id integer, _roles text)
|
6
|
+
# Returns true if the user has one of the given roles on the domain
|
7
|
+
# object
|
8
|
+
#
|
9
|
+
# public.current_is_role(_object_id integer, _roles text[])
|
10
|
+
# public.current_is_role(_object_id integer, _roles text)
|
11
|
+
# Returns true if the current user has one of the given roles on the
|
12
|
+
# domain object
|
13
|
+
#
|
14
|
+
# public.current_is_ROLE(_object_id integer)
|
15
|
+
# ROLE is one of the role kinds. Returns true if the current user has the
|
16
|
+
# given role
|
17
|
+
#
|
18
|
+
|
2
19
|
class Generator
|
3
20
|
class RoleFunctions
|
4
21
|
using String::Text
|
@@ -20,54 +37,34 @@ module MkAcl
|
|
20
37
|
private
|
21
38
|
def generate_user_role_functions
|
22
39
|
# TODO: Test. Then combine with per-table methods
|
23
|
-
signature = "#{acl_schema}.user_is_role(_user_id integer,
|
40
|
+
signature = "#{acl_schema}.user_is_role(_user_id integer, _object_id integer, _roles text[])"
|
24
41
|
puts %(
|
25
42
|
-- Return true if the user possess one or more of the given roles on the
|
26
|
-
--
|
43
|
+
-- object (cases, events, visits) with the given ID
|
27
44
|
--
|
28
45
|
drop function if exists #{signature} cascade;
|
29
46
|
create function #{signature} returns boolean as $$
|
30
47
|
select exists (
|
31
48
|
select
|
32
|
-
from
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
union
|
39
|
-
|
40
|
-
select
|
41
|
-
from #{app_schema}.event_roles cr
|
42
|
-
join #{app_schema}.event_role_users cru on cru.event_role_id = cr.id
|
43
|
-
where cr.event_id = _domain_id
|
44
|
-
and cru.user_id = _user_id
|
45
|
-
and cr.kind = any(_roles)
|
46
|
-
|
47
|
-
union
|
48
|
-
|
49
|
-
select
|
50
|
-
from #{app_schema}.visit_roles cr
|
51
|
-
join #{app_schema}.visit_role_users cru on cru.visit_role_id = cr.id
|
52
|
-
where cr.visit_id = _domain_id
|
53
|
-
and cru.user_id = _user_id
|
54
|
-
and cr.kind = any(_roles)
|
55
|
-
|
56
|
-
);
|
49
|
+
from app_portal.domain_users
|
50
|
+
where domain_id = _domain_id
|
51
|
+
and user_id = _user_id
|
52
|
+
and role = any(_roles)
|
53
|
+
)
|
57
54
|
$$ language sql
|
58
55
|
security definer;
|
59
56
|
).align
|
60
57
|
puts
|
61
58
|
|
62
|
-
signature = "#{acl_schema}.user_is_role(_user_id integer,
|
59
|
+
signature = "#{acl_schema}.user_is_role(_user_id integer, _object_id integer, role text)"
|
63
60
|
puts %(
|
64
|
-
-- Return true if the user has the given role on the
|
61
|
+
-- Return true if the user has the given role on the object
|
65
62
|
-- (cases, events, visits). Note that this function overloads the multi-role
|
66
63
|
-- version
|
67
64
|
--
|
68
65
|
drop function if exists #{signature} cascade;
|
69
66
|
create function #{signature} returns boolean as $$
|
70
|
-
select #{acl_schema}.user_is_role(_user_id,
|
67
|
+
select #{acl_schema}.user_is_role(_user_id, _object_id, array[role]);
|
71
68
|
$$ language sql
|
72
69
|
security definer;
|
73
70
|
).align
|
@@ -75,13 +72,13 @@ module MkAcl
|
|
75
72
|
|
76
73
|
for domain, roles in DOMAINS.zip([CASE_ROLES, EVENT_ROLES, VISIT_ROLES])
|
77
74
|
for role in roles
|
78
|
-
signature = "#{acl_schema}.user_is_#{role.downcase}(_user_id integer,
|
75
|
+
signature = "#{acl_schema}.user_is_#{role.downcase}(_user_id integer, _object_id integer)"
|
79
76
|
puts %(
|
80
77
|
-- Return true if the user possess the '#{role}' role
|
81
78
|
--
|
82
79
|
drop function if exists #{signature} cascade;
|
83
80
|
create function #{signature} returns boolean as $$
|
84
|
-
select #{acl_schema}.user_is_role(_user_id,
|
81
|
+
select #{acl_schema}.user_is_role(_user_id, _object_id, '#{role}');
|
85
82
|
$$ language sql
|
86
83
|
security definer;
|
87
84
|
).align
|
@@ -92,28 +89,28 @@ module MkAcl
|
|
92
89
|
|
93
90
|
def generate_current_role_functions
|
94
91
|
# TODO: Test. Then combine with per-table methods
|
95
|
-
signature = "public.current_is_role(
|
92
|
+
signature = "public.current_is_role(_object_id integer, _roles text[])"
|
96
93
|
puts %(
|
97
94
|
-- Return true if the current user possess one or more of the given roles on the
|
98
|
-
--
|
95
|
+
-- object (cases, events, visits) with the given ID
|
99
96
|
--
|
100
97
|
drop function if exists #{signature} cascade;
|
101
98
|
create function #{signature} returns boolean as $$
|
102
|
-
select #{acl_schema}.user_is_role(public.current_user_id(),
|
99
|
+
select #{acl_schema}.user_is_role(public.current_user_id(), _object_id, _roles);
|
103
100
|
$$ language sql
|
104
101
|
security definer;
|
105
102
|
).align
|
106
103
|
puts
|
107
104
|
|
108
|
-
signature = "public.current_is_role(
|
105
|
+
signature = "public.current_is_role(_object_id integer, _role text)"
|
109
106
|
puts %(
|
110
|
-
-- Return true if the current user possess the given role on the
|
107
|
+
-- Return true if the current user possess the given role on the object
|
111
108
|
-- (cases, events, visits). Note that this function overloads the multi-role
|
112
109
|
-- version
|
113
110
|
--
|
114
111
|
drop function if exists #{signature} cascade;
|
115
112
|
create function #{signature} returns boolean as $$
|
116
|
-
select #{acl_schema}.user_is_role(public.current_user_id(),
|
113
|
+
select #{acl_schema}.user_is_role(public.current_user_id(), _object_id, array[_role]);
|
117
114
|
$$ language sql
|
118
115
|
security definer;
|
119
116
|
).align
|
@@ -121,13 +118,13 @@ module MkAcl
|
|
121
118
|
|
122
119
|
for domain, roles in DOMAINS.zip([CASE_ROLES, EVENT_ROLES, VISIT_ROLES])
|
123
120
|
for role in roles
|
124
|
-
signature = "public.current_is_#{role.downcase}(
|
121
|
+
signature = "public.current_is_#{role.downcase}(_object_id integer)"
|
125
122
|
puts %(
|
126
123
|
-- Return true if the current user possess the '#{role}' role
|
127
124
|
--
|
128
125
|
drop function if exists #{signature} cascade;
|
129
126
|
create function #{signature} returns boolean as $$
|
130
|
-
select #{acl_schema}.user_is_role(public.current_user_id(),
|
127
|
+
select #{acl_schema}.user_is_role(public.current_user_id(), _object_id, '#{role}');
|
131
128
|
$$ language sql
|
132
129
|
security definer;
|
133
130
|
).align
|
@@ -56,13 +56,13 @@ module MkAcl
|
|
56
56
|
for action in %w(select update delete).map { table.actions[_1] }
|
57
57
|
rule_expr = action.rules.map { |rule|
|
58
58
|
exprs = []
|
59
|
-
exprs << "acl_#{action.name}[#{rule.
|
59
|
+
exprs << "acl_#{action.name}[#{rule.ordinal}:#{rule.ordinal}][1:] && public.current_role_ids()" \
|
60
60
|
if !rule.roles.empty?
|
61
61
|
exprs << "(#{rule.using})" if rule.using
|
62
62
|
"(#{exprs.join(' and ')})"
|
63
63
|
}.join(" or ")
|
64
64
|
rule_expr = "false" if rule_expr.empty?
|
65
|
-
|
65
|
+
|
66
66
|
puts %(
|
67
67
|
drop policy if exists rls_#{action.name} on #{table.uid} cascade;
|
68
68
|
create policy rls_#{action.name} on #{table.uid} for #{action.name}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module MkAcl
|
3
|
+
class Generator
|
4
|
+
class Seeds
|
5
|
+
using String::Text
|
6
|
+
|
7
|
+
attr_reader :generator
|
8
|
+
forward_to :generator, :conn, :spec, :app_schema, :acl_schema
|
9
|
+
|
10
|
+
def initialize(generator)
|
11
|
+
@generator = generator
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate
|
15
|
+
clean_tables
|
16
|
+
generate_seeds
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.generate(generator) self.new(generator).generate end
|
20
|
+
|
21
|
+
private
|
22
|
+
def clean_tables
|
23
|
+
puts %(
|
24
|
+
delete from acl_portal.acl_rules;
|
25
|
+
delete from acl_portal.acl_actions;
|
26
|
+
delete from acl_portal.acl_tables;
|
27
|
+
).align
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_seeds
|
31
|
+
for table in spec.tables
|
32
|
+
puts %(
|
33
|
+
insert into acl_portal.acl_tables (
|
34
|
+
schema_name, table_name, domain,
|
35
|
+
parent_schema_name, parent_table_name, parent_link_field,
|
36
|
+
acl)
|
37
|
+
values (
|
38
|
+
'#{app_schema}', '#{table}', #{conn.quote_value(table.domain)},
|
39
|
+
'#{table.parent && table.app_schema}', '#{table.parent}', '#{table.parent_link_field}',
|
40
|
+
#{table.acl || 'false'})
|
41
|
+
returning id as "table_id"
|
42
|
+
\\gset
|
43
|
+
).align
|
44
|
+
puts
|
45
|
+
|
46
|
+
table.actions.values.each { |action|
|
47
|
+
puts %(
|
48
|
+
insert into acl_portal.acl_actions (acl_table_id, kind)
|
49
|
+
values (:table_id, '#{action.name.upcase}')
|
50
|
+
returning id as "action_id"
|
51
|
+
\\gset
|
52
|
+
).align
|
53
|
+
puts
|
54
|
+
|
55
|
+
action.rules.each { |rule|
|
56
|
+
fields = %w(roles filter assert fields tables ordinal)
|
57
|
+
values = fields.map { |field| conn.quote_value(rule.send(field.to_sym), elem_type: :text) }
|
58
|
+
puts %(
|
59
|
+
insert into acl_portal.acl_rules (acl_action_id, roles, filter, assert, fields, tables, ordinal)
|
60
|
+
values (:action_id, #{values.join(', ')});
|
61
|
+
).align
|
62
|
+
puts
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
puts %(
|
68
|
+
update acl_portal.acl_tables sub
|
69
|
+
set parent_id = (
|
70
|
+
select id
|
71
|
+
from acl_portal.acl_tables super
|
72
|
+
where super.schema_name = sub.parent_schema_name
|
73
|
+
and super.table_name = sub.parent_table_name
|
74
|
+
);
|
75
|
+
).align
|
76
|
+
puts
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -2,14 +2,29 @@
|
|
2
2
|
module MkAcl
|
3
3
|
class Parser
|
4
4
|
attr_reader :file
|
5
|
+
attr_reader :symtab
|
5
6
|
|
6
|
-
def initialize(file)
|
7
|
+
def initialize(file)
|
8
|
+
@file = file
|
9
|
+
@symtab = SimpleSymtab::SimpleSymtab.new
|
10
|
+
end
|
7
11
|
def parse() parse_spec end
|
8
12
|
def self.parse(file) Parser.new(file).parse end
|
9
13
|
|
10
14
|
private
|
11
15
|
def error(*msg) raise ParseError, *msg end
|
12
|
-
|
16
|
+
|
17
|
+
def norm_value(value)
|
18
|
+
value && symtab.interpolate(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def norm_array(value)
|
22
|
+
if value.is_a?(Array)
|
23
|
+
value.map { |v| norm_value(v) }
|
24
|
+
else
|
25
|
+
norm_value(value)&.split
|
26
|
+
end
|
27
|
+
end
|
13
28
|
|
14
29
|
def parse_spec
|
15
30
|
hash = YAML.load(IO.read(file), symbolize_names: true)
|
@@ -18,21 +33,31 @@ module MkAcl
|
|
18
33
|
app_schema = schema[:app] or raise ArgumentError, "Can't find 'schema.app' attribute"
|
19
34
|
acl_schema = schema[:acl] or raise ArgumentError, "Can't find 'schema.acl' attribute"
|
20
35
|
spec = Spec.new(file, app_schema, acl_schema)
|
36
|
+
parse_variables(hash)
|
21
37
|
parse_tables(spec, hash)
|
22
38
|
spec
|
23
39
|
end
|
24
40
|
|
41
|
+
def parse_variables(hash)
|
42
|
+
hash.delete_if { |k,v| k.to_s =~ /^(\$[A-Za-z0-9_]+)$/ and symtab[k] = v }
|
43
|
+
end
|
44
|
+
|
25
45
|
def parse_tables(spec, tables)
|
26
46
|
for table_name, actions in tables
|
27
|
-
|
47
|
+
acl = actions.key?(:acl) ? actions.delete(:acl) : true
|
48
|
+
parent = actions.delete(:parent)
|
49
|
+
domain = actions.delete(:domain)
|
50
|
+
table = Table.new(spec, table_name, domain, parent, acl)
|
28
51
|
parse_actions(table, actions)
|
29
52
|
end
|
30
53
|
end
|
31
54
|
|
32
55
|
def parse_actions(table, actions)
|
33
56
|
for action_name, rules in actions
|
34
|
-
constrain?(action_name, :insert, :select, :update, :delete) or
|
35
|
-
|
57
|
+
constrain?(action_name, :insert, :select, :update, :delete, :attach) or
|
58
|
+
error "Illegal action '#{action_name}'"
|
59
|
+
constrain?(rules, String, Array, Hash) or
|
60
|
+
error "Illegal value for #{action} action '#{rules}'"
|
36
61
|
action = Action.new(table, action_name)
|
37
62
|
|
38
63
|
# Normalize rules
|
@@ -40,7 +65,7 @@ module MkAcl
|
|
40
65
|
when Hash
|
41
66
|
rules = [rules]
|
42
67
|
when String
|
43
|
-
rules = [{
|
68
|
+
rules = [{ roles: rules }]
|
44
69
|
end
|
45
70
|
|
46
71
|
parse_rules(action, rules)
|
@@ -48,23 +73,22 @@ module MkAcl
|
|
48
73
|
end
|
49
74
|
|
50
75
|
def parse_rules(action, rules)
|
51
|
-
|
76
|
+
ordinal = 0
|
52
77
|
for entry in rules
|
53
|
-
rule = Rule.new(action,
|
54
|
-
|
78
|
+
rule = Rule.new(action, ordinal += 1)
|
55
79
|
for key, value in entry
|
56
80
|
case key
|
57
|
-
when :
|
58
|
-
when :
|
59
|
-
when :
|
60
|
-
when :
|
61
|
-
when :
|
81
|
+
when :roles; rule.roles = norm_array(value)
|
82
|
+
when :filter; rule.filter = norm_value(value)
|
83
|
+
when :assert; rule.assert = norm_value(value)
|
84
|
+
when :fields; rule.fields = norm_array(value)
|
85
|
+
when :tables; rule.tables = norm_array(value)
|
62
86
|
else
|
63
87
|
raise ArgumentError, "Illegal field '#{key}' in #{action.table}.#{action}"
|
64
88
|
end
|
65
89
|
end
|
66
90
|
|
67
|
-
!action.rules.empty? or
|
91
|
+
!action.rules.empty? or
|
68
92
|
error "At least one rule is required in #{action.table}.#{action}"
|
69
93
|
end
|
70
94
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'forward_to'
|
5
|
+
|
6
|
+
include ForwardTo
|
7
|
+
|
8
|
+
module SimpleSymtab
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
class SimpleSymtab
|
12
|
+
def [](key)
|
13
|
+
resolve if !resolved?
|
14
|
+
@variables[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
!@variables.key?(key) or raise Error, "Redefinition of '#{key}'"
|
19
|
+
@variables[key] = value
|
20
|
+
@unresolved << key
|
21
|
+
end
|
22
|
+
|
23
|
+
forward_to :"@variables", :key?, :size, :empty?
|
24
|
+
|
25
|
+
def initialize(hash = {})
|
26
|
+
@variables = hash.dup
|
27
|
+
@unresolved = @variables.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
def interpolate(s)
|
31
|
+
resolve if !resolved?
|
32
|
+
resolve_string(s)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def resolved?() @unresolved.empty? end
|
37
|
+
|
38
|
+
def resolve_string(val)
|
39
|
+
val.is_a?(String) or raise ArgumentError
|
40
|
+
seen = Set.new
|
41
|
+
while val =~ /(\$[A-Za-z0-9_]+\b)/
|
42
|
+
var = $1
|
43
|
+
seen.add?(var) or raise Error, "Circular definition of '#{var}'"
|
44
|
+
rep = @variables[var.to_sym] or raise Error, "Unknown variable '#{var}'"
|
45
|
+
val.gsub!(var, rep)
|
46
|
+
end
|
47
|
+
val
|
48
|
+
end
|
49
|
+
|
50
|
+
def resolve
|
51
|
+
@unresolved.each { |var|
|
52
|
+
@variables[var] = resolve_string(@variables[var])
|
53
|
+
}
|
54
|
+
@unresolved = []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module MkAcl
|
3
3
|
class Spec
|
4
4
|
# Source SPEC file. Only for informational purposes
|
5
|
-
attr_reader :file
|
5
|
+
attr_reader :file
|
6
6
|
|
7
7
|
# Application schema that contains the ACL controlled tables
|
8
8
|
attr_reader :app_schema
|
@@ -11,7 +11,7 @@ module MkAcl
|
|
11
11
|
# (relatively) clean
|
12
12
|
attr_reader :acl_schema
|
13
13
|
|
14
|
-
# List of tables.
|
14
|
+
# List of tables. Maintained by #attach_table
|
15
15
|
attr_reader :tables
|
16
16
|
|
17
17
|
# Spec acts as a hash from table name to Table object. Initialized by
|
@@ -45,11 +45,35 @@ module MkAcl
|
|
45
45
|
|
46
46
|
class Table
|
47
47
|
attr_reader :spec
|
48
|
-
|
49
|
-
|
48
|
+
forward_to :@spec, :app_schema, :acl_schema
|
49
|
+
|
50
|
+
# Hash from referenced table name to a tuple of the table object and the
|
51
|
+
# link field. Initialized by the analyzer
|
52
|
+
attr_accessor :references
|
53
|
+
|
54
|
+
# Table name and uid
|
50
55
|
attr_reader :name
|
51
|
-
attr_reader :
|
52
|
-
|
56
|
+
attr_reader :uid # SCHEMA.TABLE name
|
57
|
+
|
58
|
+
# Parent domain table. Initialized by the analyzer
|
59
|
+
attr_accessor :parent
|
60
|
+
|
61
|
+
# Name of parent table. May be nil. Initialized by the parser
|
62
|
+
attr_accessor :parent_name
|
63
|
+
|
64
|
+
# Name of link field to parent record. Initialized by the analyzer
|
65
|
+
attr_accessor :parent_link_field
|
66
|
+
|
67
|
+
# Security domain name for this object. Domain object have themselves as
|
68
|
+
# domain, all other portal objects use the parent's domain. Initialized by
|
69
|
+
# the analyzer
|
70
|
+
attr_accessor :domain
|
71
|
+
|
72
|
+
# SQL to create the ACL for a table. No ACL if false, default ACL if nil
|
73
|
+
attr_accessor :acl
|
74
|
+
|
75
|
+
# Associated record name. Used in function names
|
76
|
+
attr_reader :record_name
|
53
77
|
|
54
78
|
# Action objects
|
55
79
|
def insert = @actions["insert"]
|
@@ -60,13 +84,15 @@ module MkAcl
|
|
60
84
|
# Hash from action name to action object
|
61
85
|
attr_reader :actions
|
62
86
|
|
63
|
-
def initialize(spec, name, domain)
|
87
|
+
def initialize(spec, name, domain, parent_name, acl)
|
64
88
|
@spec = spec
|
65
|
-
@
|
89
|
+
@references = {}
|
66
90
|
@name = name.to_s
|
67
|
-
@uid = "#{
|
91
|
+
@uid = "#{app_schema}.#{@name}"
|
68
92
|
@record_name = Prick::Inflector.singularize(@name)
|
93
|
+
@parent_name = parent_name
|
69
94
|
@domain = domain
|
95
|
+
@acl = acl
|
70
96
|
@actions = {}
|
71
97
|
@spec.send :attach_table, self
|
72
98
|
for action_name in %w(insert select update delete)
|
@@ -79,12 +105,19 @@ module MkAcl
|
|
79
105
|
|
80
106
|
def dump
|
81
107
|
puts "#{name}:"
|
82
|
-
indent {
|
108
|
+
indent {
|
83
109
|
puts "domain: #{domain}" if domain
|
84
|
-
puts "
|
110
|
+
puts "parent: #{parent}" if parent
|
111
|
+
puts "references: [#{references.values.map(&:first).map(&:name).join(' ')}]"
|
85
112
|
for action_name in %w(insert select update delete)
|
86
113
|
actions[action_name]&.dump
|
87
114
|
end
|
115
|
+
case acl
|
116
|
+
when false; puts "acl: false"
|
117
|
+
when true;
|
118
|
+
puts "acl:"
|
119
|
+
indent { puts acl }
|
120
|
+
end
|
88
121
|
}
|
89
122
|
end
|
90
123
|
|
@@ -106,8 +139,6 @@ module MkAcl
|
|
106
139
|
@table.send :attach_action, self
|
107
140
|
end
|
108
141
|
|
109
|
-
def fields() @include + @exclude.map { "-#{_1}" } end
|
110
|
-
|
111
142
|
def to_s() name end
|
112
143
|
|
113
144
|
def dump
|
@@ -116,7 +147,7 @@ module MkAcl
|
|
116
147
|
else
|
117
148
|
puts name
|
118
149
|
indent {
|
119
|
-
for rule in rules
|
150
|
+
for rule in rules.sort_by(&:ordinal)
|
120
151
|
print "- "
|
121
152
|
indent(bol: false) { rule.dump }
|
122
153
|
end
|
@@ -131,33 +162,40 @@ module MkAcl
|
|
131
162
|
end
|
132
163
|
|
133
164
|
class Rule
|
165
|
+
using String::Text
|
166
|
+
|
134
167
|
attr_reader :action
|
135
168
|
forward_to :action, :table, :name
|
136
|
-
attr_reader :index
|
137
169
|
attr_accessor :roles
|
138
|
-
attr_accessor :
|
139
|
-
attr_accessor :
|
140
|
-
attr_accessor :
|
141
|
-
attr_accessor :
|
170
|
+
attr_accessor :filter # Goes into the postgres policy
|
171
|
+
attr_accessor :assert # Goes into the postgres trigger
|
172
|
+
attr_accessor :fields # Only used for insert and update
|
173
|
+
attr_accessor :tables # Only used for attach
|
174
|
+
attr_reader :ordinal
|
142
175
|
|
176
|
+
# admin, internal, etc.
|
143
177
|
def auth_roles() @auth_roles ||= roles.select { _1 == _1.downcase } end
|
178
|
+
|
179
|
+
# KON, AKK, etc.
|
144
180
|
def case_roles() @case_roles ||= roles.select { _1 == _1.upcase } end
|
145
181
|
|
146
|
-
def initialize(action,
|
182
|
+
def initialize(action, ordinal)
|
147
183
|
@action = action
|
148
|
-
@
|
149
|
-
@roles
|
184
|
+
@ordinal = ordinal
|
185
|
+
@roles = []
|
186
|
+
@fields = []
|
187
|
+
@tables = []
|
188
|
+
|
150
189
|
action.send :attach_rule, self
|
151
190
|
end
|
152
191
|
|
153
|
-
def fields() @include + @exclude.map { "-#{_1}" } end
|
154
|
-
|
155
192
|
def dump
|
156
|
-
puts "
|
157
|
-
puts "
|
158
|
-
puts "
|
159
|
-
puts "
|
160
|
-
puts "
|
193
|
+
puts "roles: [#{roles.join(' ')}]"
|
194
|
+
puts "filter: #{filter}" if filter
|
195
|
+
puts "assert: #{assert}" if assert
|
196
|
+
puts "fields: [#{fields.join(' ')}]" if !fields.empty?
|
197
|
+
puts "tables: [#{tables.join(' ')}]" if !tables.empty?
|
198
|
+
puts "ordinal: #{ordinal}"
|
161
199
|
end
|
162
200
|
end
|
163
201
|
end
|
data/lib/mikras_utils/mkacl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
require 'pg_conn'
|
3
3
|
require 'indented_io'
|
4
|
-
require 'string-text'
|
4
|
+
require 'string-text'; using String::Text
|
5
5
|
require 'forward_to'; include ForwardTo
|
6
6
|
require 'constrain'; include Constrain
|
7
7
|
require 'prick-inflector'
|
@@ -12,6 +12,7 @@ module MkAcl
|
|
12
12
|
DOMAINS = %w(case event visit)
|
13
13
|
DOMAIN_TABLES = DOMAINS.map { "#{_1}s" }
|
14
14
|
|
15
|
+
# TODO Read from database (or maybe not)
|
15
16
|
CASE_ROLES = %w(LA TA KON AKK RLA CLA CTA)
|
16
17
|
EVENT_ROLES = %w(ELA ETA)
|
17
18
|
VISIT_ROLES = %w(VLA VTA)
|
@@ -19,6 +20,7 @@ module MkAcl
|
|
19
20
|
ROLES = CASE_ROLES + EVENT_ROLES + VISIT_ROLES
|
20
21
|
end
|
21
22
|
|
23
|
+
require_relative 'mkacl/simple_symtab.rb'
|
22
24
|
require_relative 'mkacl/spec.rb'
|
23
25
|
require_relative 'mkacl/parser.rb'
|
24
26
|
require_relative 'mkacl/analyzer.rb'
|