mikras_utils 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74256281ee10bf5186723bfa260cd62dda3057216660aa945c7b591fa1581177
|
4
|
+
data.tar.gz: 882636fab035922c146cc33c33a5af9deaf0da818bec6897e17a84ad8f1f2289
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ca4be02024c23f2109cc7f3919331c180f6403cb04906d5652446d80241e7b502ed9cfcebfcc5ddf66b098c4b6def8e3f1cd3863942f9e988cb3e5bd1abc501
|
7
|
+
data.tar.gz: 6d82b691b1b68214910dac0acc847a68b928b6e0f45c8a157e272ded5fdc84b2f3fe5e3f23ddbadbee4bd82f1621164eea1d736deb7923787fb9f999926ddf0e
|
data/exe/mkacl
CHANGED
@@ -1,13 +1,22 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
SPEC = %(
|
4
|
-
@ Make RLS policies
|
4
|
+
@ Make RLS functions and policies
|
5
5
|
|
6
|
-
-- [DATABASE
|
6
|
+
-- [DATABASE] SPEC-FILE
|
7
7
|
|
8
|
-
Read SPEC and generate following functions, policies, and triggers
|
8
|
+
Read SPEC and generate following functions, policies, and triggers. The
|
9
|
+
default DATABASE and USERNAME variables are read from the environment and
|
10
|
+
then .prick.state.yml if present; otherwise the current user's database is
|
11
|
+
used
|
9
12
|
|
10
|
-
|
13
|
+
FUNCTIONS
|
14
|
+
todo
|
15
|
+
|
16
|
+
TRIGGERS
|
17
|
+
todo
|
18
|
+
|
19
|
+
SPEC FILE
|
11
20
|
The format is a YAML file where each root key represents a table. A table
|
12
21
|
consists of insert, select, update, and delete actions that are arrays of
|
13
22
|
ACL entries. An ACL entry list of associated roles and possibly a check SQL
|
@@ -25,17 +34,20 @@ SPEC = %(
|
|
25
34
|
The following roles are recognized: RLA, LA, TA, KON, AKK, CLA, CTA, ELA,
|
26
35
|
and ETA
|
27
36
|
|
28
|
-
Sagsys also defines the ADM role that is excluded because it is
|
29
|
-
|
30
|
-
Mikras
|
37
|
+
Sagsys also defines the ADM role that is excluded because it is a RBAC role
|
38
|
+
in Mikras, and PUP and NON roles that are excluded because they are not
|
39
|
+
implemented in Mikras
|
31
40
|
|
32
41
|
OPTIONS
|
42
|
+
-d,dump
|
43
|
+
Dump internal format. Used for debugging
|
44
|
+
|
33
45
|
-i,interactive
|
34
46
|
Generate code for interactive use by added ON_ERROR_STOP and other variables
|
35
47
|
|
36
48
|
-g,generate=LIST
|
37
49
|
Generate only the given modules. LIST is a comma-separated list of
|
38
|
-
modules. The following modules are currently available: id_functions,
|
50
|
+
modules. The following modules are currently available: seeds, id_functions,
|
39
51
|
insert_triggers, rules, acl_functions, and role_functions
|
40
52
|
|
41
53
|
-W,no-warn
|
@@ -46,14 +58,17 @@ SPEC = %(
|
|
46
58
|
require 'yaml'
|
47
59
|
require 'shellopts'
|
48
60
|
|
61
|
+
require_relative '../lib/mikras_utils/mikras.rb'
|
49
62
|
require_relative '../lib/mikras_utils/mkacl.rb'
|
50
63
|
|
64
|
+
PRICK_STATE_FILE = "prick.state.yml"
|
65
|
+
|
51
66
|
opts, args = ShellOpts::process(SPEC, ARGV)
|
52
67
|
file = args.extract(-1)
|
53
|
-
database
|
54
|
-
|
55
|
-
|
56
|
-
conn
|
68
|
+
database = args.extract(0..1)
|
69
|
+
|
70
|
+
conn = PgConn.new *Mikras.credentials(database)
|
71
|
+
conn.schema.exist?("prick") or ShellOpts.error "Database '#{database}' is not a prick database"
|
57
72
|
|
58
73
|
if opts.generate?
|
59
74
|
modules = opts.generate.split(',').map(&:to_sym)
|
@@ -64,8 +79,10 @@ end
|
|
64
79
|
|
65
80
|
spec = MkAcl::Parser.parse(file)
|
66
81
|
MkAcl::Analyzer.analyze(spec, conn, warn: !opts.no_warn?)
|
67
|
-
|
68
|
-
|
82
|
+
if opts.dump?
|
83
|
+
spec.dump
|
84
|
+
exit
|
85
|
+
end
|
69
86
|
MkAcl::Generator.generate(spec, conn, modules, interactive: opts.interactive?)
|
70
87
|
|
71
88
|
|
data/exe/rls
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'indented_io'
|
4
|
+
require 'shellopts'
|
5
|
+
require 'forward_to'
|
6
|
+
|
7
|
+
include ForwardTo
|
8
|
+
|
9
|
+
require_relative '../lib/mikras_utils/rls/spec.rb'
|
10
|
+
require_relative '../lib/mikras_utils/rls/parser.rb'
|
11
|
+
require_relative '../lib/mikras_utils/rls/analyzer.rb'
|
12
|
+
|
13
|
+
def error(msg) = ShellOpts.error(msg)
|
14
|
+
|
15
|
+
SPEC = %(
|
16
|
+
Parse RLS spec file
|
17
|
+
|
18
|
+
-- FILE
|
19
|
+
)
|
20
|
+
|
21
|
+
opts, args = ShellOpts.process(SPEC, ARGV)
|
22
|
+
|
23
|
+
file = args.expect(1)
|
24
|
+
spec = Parser.parse(file)
|
25
|
+
Analyzer.analyze(spec)
|
26
|
+
|
27
|
+
spec.dump
|
28
|
+
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Mikras
|
3
|
+
def self.credentials(database_argument)
|
4
|
+
if database_argument
|
5
|
+
database = database_argument
|
6
|
+
username = database
|
7
|
+
else
|
8
|
+
username ||= database || ENV['PRICK_USERNAME']
|
9
|
+
database ||= ENV['PRICK_DATABASE']
|
10
|
+
|
11
|
+
if database.nil? && File.exist?(PRICK_STATE_FILE)
|
12
|
+
prick_state = YAML.load(IO.read PRICK_STATE_FILE)
|
13
|
+
database = prick_state["database"]
|
14
|
+
username = prick_state["username"]
|
15
|
+
else
|
16
|
+
database ||= ENV["USER"]
|
17
|
+
username ||= database
|
18
|
+
end
|
19
|
+
end
|
20
|
+
[database, username]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -15,19 +15,21 @@ module MkAcl
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def analyze
|
18
|
+
# Find child-parent relations between linked tables
|
18
19
|
links = conn.tuples %(
|
19
20
|
select
|
20
|
-
table_name,
|
21
|
-
|
22
|
-
|
23
|
-
ref_schema_name || '.' || ref_table_name
|
21
|
+
table_name as "child_table_name",
|
22
|
+
schema_name || '.' || table_name as "child_table_uid",
|
23
|
+
ref_table_name as "parent_table_name",
|
24
|
+
ref_schema_name || '.' || ref_table_name as "parent_table_uid",
|
25
|
+
column_name as "parent_link_field"
|
24
26
|
from meta.links
|
25
|
-
where schema_name = '#{spec.app_schema}'
|
27
|
+
where schema_name = '#{spec.app_schema}'
|
26
28
|
and ref_schema_name = '#{spec.app_schema}'
|
27
29
|
)
|
28
30
|
|
29
|
-
#
|
30
|
-
for child_table_name,
|
31
|
+
# Assign table references
|
32
|
+
for child_table_name, child_table_uid, parent_table_name, parent_table_uid, parent_link_field in links
|
31
33
|
if !spec.key?(child_table_name)
|
32
34
|
@uncovered_tables << child_table_name
|
33
35
|
next
|
@@ -35,8 +37,11 @@ module MkAcl
|
|
35
37
|
@uncovered_tables << parent_table_name
|
36
38
|
next
|
37
39
|
end
|
38
|
-
|
39
|
-
spec[child_table_name]
|
40
|
+
|
41
|
+
child_table = spec[child_table_name] or raise "Can't find table #{parent_table_name.inspect}"
|
42
|
+
parent_table = spec[parent_table_name] or raise "Can't find referenced table #{parent_table_name.inspect}"
|
43
|
+
|
44
|
+
child_table.references[parent_table.name] = [parent_table, parent_link_field]
|
40
45
|
end
|
41
46
|
|
42
47
|
# if warn && !@uncovered_tables.empty?
|
@@ -44,29 +49,38 @@ module MkAcl
|
|
44
49
|
# indent { puts uncovered_tables }
|
45
50
|
# end
|
46
51
|
|
47
|
-
# Assign
|
48
|
-
spec.tables.each { |table|
|
49
|
-
resolve_domain(table)
|
50
|
-
}
|
51
|
-
|
52
|
-
# Check that no table has more than one parent. FIXME
|
52
|
+
# Assign parents
|
53
53
|
spec.tables.each { |table|
|
54
|
-
|
54
|
+
if table.parent_name
|
55
|
+
table.parent = spec[table.parent_name] or raise ArgumentError, "Can't find '#{table.parent_name}'"
|
56
|
+
table.parent_link_field = table.references[table.parent.name].last
|
57
|
+
else
|
58
|
+
table.references.size <= 1 or raise ArgumentError, "Table '#{table.name}' has multiple references"
|
59
|
+
parent, link_field = table.references.values.first
|
60
|
+
if parent&.acl
|
61
|
+
table.parent = parent
|
62
|
+
table.parent_link_field = link_field
|
63
|
+
end
|
64
|
+
end
|
55
65
|
}
|
56
66
|
|
67
|
+
# Resolve domains
|
68
|
+
spec.tables.select(&:acl).each { |t| resolve_domain(t) }
|
69
|
+
|
57
70
|
spec
|
58
71
|
end
|
59
72
|
|
60
73
|
def self.analyze(spec, conn, **opts) self.new(spec, conn, **opts).analyze end
|
61
74
|
|
62
75
|
private
|
63
|
-
def find_tables
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
76
|
def resolve_domain(table)
|
68
|
-
table.domain
|
77
|
+
if table.domain.nil?
|
78
|
+
!table.parent.nil? or raise ArgumentError, "Domain table without domain name - '#{table.name}'"
|
79
|
+
resolve_domain(table.parent)
|
80
|
+
table.domain = table.parent.domain
|
81
|
+
end
|
69
82
|
end
|
70
83
|
end
|
71
84
|
end
|
72
85
|
|
86
|
+
|
@@ -4,10 +4,11 @@ require_relative './generators/acl_functions.rb'
|
|
4
4
|
require_relative './generators/insert_triggers.rb'
|
5
5
|
require_relative './generators/role_functions.rb'
|
6
6
|
require_relative './generators/rules.rb'
|
7
|
+
require_relative './generators/seeds.rb'
|
7
8
|
|
8
9
|
module MkAcl
|
9
10
|
class Generator
|
10
|
-
MODULES = [:id_functions, :insert_triggers, :rules, :acl_functions, :role_functions]
|
11
|
+
MODULES = [:id_functions, :insert_triggers, :rules, :acl_functions, :role_functions, :seeds]
|
11
12
|
|
12
13
|
using String::Text
|
13
14
|
|
@@ -32,6 +33,7 @@ module MkAcl
|
|
32
33
|
|
33
34
|
matches = modules.map { |k| [k, true] }.to_h
|
34
35
|
|
36
|
+
Seeds.generate(self) if matches.key?(:seeds)
|
35
37
|
IdFunctions.generate(self) if matches.key?(:id_functions)
|
36
38
|
for table in spec.tables
|
37
39
|
InsertTriggers.generate(self, table) if matches.key?(:insert_triggers)
|
@@ -42,7 +44,7 @@ module MkAcl
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def self.generate(spec, conn, modules = MODULES, **opts)
|
45
|
-
self.new(spec, conn).generate(modules, **opts)
|
47
|
+
self.new(spec, conn).generate(modules, **opts)
|
46
48
|
end
|
47
49
|
|
48
50
|
def inspect() "Generator(#{@database.inspect}, #{@username.inspect}, #{@spec})" end
|
@@ -21,11 +21,28 @@ module MkAcl
|
|
21
21
|
def self.generate(generator) self.new(generator).generate end
|
22
22
|
|
23
23
|
private
|
24
|
+
|
25
|
+
# %(
|
26
|
+
# update TABLE tbl
|
27
|
+
# set read_acls = (
|
28
|
+
# select array_agg(role_id)
|
29
|
+
# from acl_portal.eff_object_roles eor
|
30
|
+
# where tbl.DOMAIN_ID = eor.object_id
|
31
|
+
# )
|
32
|
+
# )
|
33
|
+
|
34
|
+
# %(
|
35
|
+
# select
|
36
|
+
# from
|
37
|
+
# where
|
38
|
+
# domain_id_of(DOMAIN, TABLE_NAME, id)
|
39
|
+
# )
|
40
|
+
|
24
41
|
def generate_acl_update_function
|
25
42
|
signature = "#{acl_schema}.update_acls()"
|
26
43
|
|
27
|
-
stmts = spec.tables.map { |table|
|
28
|
-
"perform #{acl_schema}.#{table}_update_acls(id) from #{app_schema}.#{table};"
|
44
|
+
stmts = spec.tables.map { |table|
|
45
|
+
"perform #{acl_schema}.#{table}_update_acls(id) from #{app_schema}.#{table};"
|
29
46
|
}
|
30
47
|
|
31
48
|
puts %(
|
@@ -56,7 +73,7 @@ module MkAcl
|
|
56
73
|
-- Note: The different table-level variations of this function can be
|
57
74
|
-- collapsed into a single function but it requires dynamic execution of
|
58
75
|
-- a delete statement with array-of-array arguments :-O
|
59
|
-
--
|
76
|
+
--
|
60
77
|
-- Note: This function is auto-generated by #{$PROGRAM_NAME} using #{spec.file}
|
61
78
|
--
|
62
79
|
drop function if exists #{signature} cascade;
|
@@ -68,7 +85,7 @@ module MkAcl
|
|
68
85
|
_domain_id integer;
|
69
86
|
_acls integer[]; -- per-rule ACLs
|
70
87
|
_acl_select integer[][]; -- per-action ACLs
|
71
|
-
_acl_update integer[][]; --
|
88
|
+
_acl_update integer[][]; --
|
72
89
|
_acl_delete integer[][]; --
|
73
90
|
begin
|
74
91
|
).align
|
@@ -132,7 +149,7 @@ module MkAcl
|
|
132
149
|
end
|
133
150
|
puts %(
|
134
151
|
-- Update ACL fields
|
135
|
-
update #{table.uid}
|
152
|
+
update #{table.uid}
|
136
153
|
set acl_select = _acl_select,
|
137
154
|
acl_update = _acl_update,
|
138
155
|
acl_delete = _acl_delete
|
@@ -165,7 +182,7 @@ module MkAcl
|
|
165
182
|
|
166
183
|
-- Create attach ACLs
|
167
184
|
insert into acl_portal.attach_acls (parent_table, parent_id, child_table, child_field, acls)
|
168
|
-
with
|
185
|
+
with
|
169
186
|
case_role_acls as (
|
170
187
|
select
|
171
188
|
id
|
@@ -182,8 +199,8 @@ module MkAcl
|
|
182
199
|
where rolename = any(array[#{auth_role_list}]::text[])
|
183
200
|
),
|
184
201
|
role_acls as (
|
185
|
-
select * from case_role_acls
|
186
|
-
union
|
202
|
+
select * from case_role_acls
|
203
|
+
union
|
187
204
|
select * from auth_role_acls
|
188
205
|
)
|
189
206
|
select
|
@@ -192,7 +209,7 @@ module MkAcl
|
|
192
209
|
l.table_name as "child_table",
|
193
210
|
l.column_name as "child_field",
|
194
211
|
array_agg(ra.id) as "acls"
|
195
|
-
from
|
212
|
+
from
|
196
213
|
acl.links l,
|
197
214
|
role_acls ra
|
198
215
|
where l.table_name = '#{table}'
|
@@ -17,6 +17,42 @@ module MkAcl
|
|
17
17
|
@chains = {}
|
18
18
|
end
|
19
19
|
|
20
|
+
# Generate a set of per-table functions that returns the associated
|
21
|
+
# case_id/event_id/vist_id for the given record. The Functions are
|
22
|
+
# generated for each table in the spec file (todo read from meta)
|
23
|
+
#
|
24
|
+
# app_portal functions:
|
25
|
+
#
|
26
|
+
# case_id_of_RECORD(object_id integer)
|
27
|
+
# event_id_of_RECORD(object_id integer)
|
28
|
+
# visit_id_of_RECORD(object_id integer)
|
29
|
+
#
|
30
|
+
# 'RECORD' is substituted with the record name of a table. Record names
|
31
|
+
# are the singular name of a table. TODO: Make it plural again
|
32
|
+
#
|
33
|
+
# acl_portal functions with a record argument (used in triggers):
|
34
|
+
#
|
35
|
+
# case_id_of_RECORD(r record)
|
36
|
+
# event_id_of_RECORD(r record)
|
37
|
+
# visit_id_of_RECORD(r record)
|
38
|
+
#
|
39
|
+
# Returns the case, event, or visit id associated with the given record.
|
40
|
+
# The record should be an ACL table record type. These functions are used
|
41
|
+
# in before insert triggers because it takes a record (eg. NEW) instead
|
42
|
+
# of an ID - this saves a lookup
|
43
|
+
#
|
44
|
+
# acl_portal general id-of functions:
|
45
|
+
#
|
46
|
+
# case_id_of(table varchar, id integer)
|
47
|
+
# event_id_of(table varchar, id integer)
|
48
|
+
# visit_id_of(table varchar, id integer)
|
49
|
+
#
|
50
|
+
# These methods are practically as efficient as using the more
|
51
|
+
# specialized versions above
|
52
|
+
#
|
53
|
+
# Returns null if not found. Note that the record has to exists because
|
54
|
+
# we access it to read the foreign keys
|
55
|
+
#
|
20
56
|
# TODO: ACL checks so that a user can't get IDs of other users' records
|
21
57
|
def generate
|
22
58
|
# Find chains
|
@@ -44,46 +80,24 @@ module MkAcl
|
|
44
80
|
# # SQL sequence of spec file tables
|
45
81
|
# def table_seq() @table_seq ||= spec.tables.map(&:uid).join(', ') end
|
46
82
|
|
47
|
-
# Generate
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# The geneated functions are
|
52
|
-
#
|
53
|
-
# case_id_of_RECORD(id integer)
|
54
|
-
# event_id_of_RECORD(id integer)
|
55
|
-
# domain_id_of_RECORD(id integer)
|
56
|
-
#
|
57
|
-
# and the record variants
|
58
|
-
#
|
59
|
-
# case_id_of_RECORD(r record)
|
60
|
-
# event_id_of_RECORD(r record)
|
61
|
-
# domain_id_of_RECORD(r record)
|
62
|
-
#
|
63
|
-
# 'RECORD' is substituted with the record name of a table. Record names
|
64
|
-
# are the singular name of a table. TODO: Make it plural again
|
65
|
-
#
|
66
|
-
# The domain functions returns event_id if related to an event and
|
67
|
-
# case_id if not. It is meant to return the most specialized domin id for
|
68
|
-
# a record. TODO Is it used?
|
69
|
-
#
|
70
|
-
# Returns null if not found. Note that the record has to exists because
|
71
|
-
# we access it to read the foreign keys
|
83
|
+
# Generate
|
84
|
+
# case_id_of_RECORD(object_id integer)
|
85
|
+
# event_id_of_RECORD(object_id integer)
|
86
|
+
# visit_id_of_RECORD(object_id integer)
|
72
87
|
#
|
73
88
|
def generate_per_table_id_functions
|
74
|
-
# Generate functions by domain
|
75
|
-
for domain in DOMAINS
|
89
|
+
for domain in DOMAINS # Generate functions by domain
|
76
90
|
|
77
91
|
# Create the identity functions first. The identity functions are
|
78
92
|
# case_id_of_case() and event_id_of_event(). This makes some stuff
|
79
93
|
# easier later on
|
80
94
|
table = "#{domain}s"
|
81
95
|
id_field = "#{domain}_id"
|
82
|
-
signature = "#{
|
96
|
+
signature = "#{app_schema}.#{id_field}_of_#{domain}(_object_id integer)"
|
83
97
|
puts %(
|
84
98
|
drop function if exists #{signature} cascade;
|
85
99
|
create function #{signature} returns integer as $$
|
86
|
-
select
|
100
|
+
select _object_id;
|
87
101
|
$$ language sql
|
88
102
|
set search_path to ''; -- intentionally empty
|
89
103
|
).align
|
@@ -120,47 +134,43 @@ module MkAcl
|
|
120
134
|
end
|
121
135
|
end
|
122
136
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
137
|
+
# # Tables that references visits, events, and cases
|
138
|
+
# visit_tables = chains['visits'].map(&:first)
|
139
|
+
#
|
140
|
+
# # Tables that references events and cases
|
141
|
+
# event_tables = chains['events'].map(&:first) - visit_tables
|
142
|
+
#
|
143
|
+
# # Tables that references only cases
|
144
|
+
# case_tables = chains['cases'].map(&:first) - event_tables
|
145
|
+
#
|
146
|
+
# # Create domain functions
|
147
|
+
# for domain, tables in { case: case_tables, event: event_tables, visit: visit_tables }
|
148
|
+
# for table_name in tables
|
149
|
+
# record_name = Prick::Inflector.singularize(table_name)
|
150
|
+
# signature = "#{acl_schema}.domain_id_of_#{record_name}(_object_id integer)"
|
151
|
+
# domain_function = "#{acl_schema}.#{domain}_id_of_#{record_name}"
|
152
|
+
# puts %(
|
153
|
+
# drop function if exists #{signature} cascade;
|
154
|
+
# create function #{signature} returns integer as $$
|
155
|
+
# select #{domain_function}(_object_id);
|
156
|
+
# $$ language sql
|
157
|
+
# set search_path to ''; -- intentionally empty
|
158
|
+
# ).align
|
159
|
+
# puts
|
160
|
+
# end
|
161
|
+
# end
|
148
162
|
end
|
149
163
|
|
150
|
-
# Generate
|
151
|
-
#
|
164
|
+
# Generate
|
152
165
|
# case_id_of(table varchar, id integer)
|
153
166
|
# event_id_of(table varchar, id integer)
|
154
|
-
#
|
155
|
-
#
|
156
|
-
# These methods are practically as efficient as using the more
|
157
|
-
# specialized versions above
|
167
|
+
# visit_id_of(table varchar, id integer)
|
158
168
|
#
|
159
169
|
def generate_general_id_functions
|
160
170
|
for domain in DOMAINS
|
161
171
|
domain_table = Prick::Inflector.pluralize(domain)
|
162
172
|
field = "#{domain}_id"
|
163
|
-
signature = "#{acl_schema}.#{field}_of(_table varchar,
|
173
|
+
signature = "#{acl_schema}.#{field}_of(_table varchar, _object_id integer)"
|
164
174
|
|
165
175
|
puts %(
|
166
176
|
drop function if exists #{signature} cascade;
|
@@ -173,11 +183,11 @@ module MkAcl
|
|
173
183
|
for table_name in chains[domain_table].map(&:first)
|
174
184
|
record_name = Prick::Inflector.singularize(table_name)
|
175
185
|
function_name = "#{field}_of_#{record_name}"
|
176
|
-
puts "when '#{table_name}' then #{acl_schema}.#{function_name}(
|
186
|
+
puts "when '#{table_name}' then #{acl_schema}.#{function_name}(_object_id)"
|
177
187
|
end
|
178
|
-
puts "when 'cases' then
|
179
|
-
puts "when 'events' then
|
180
|
-
puts "when 'visits' then
|
188
|
+
puts "when 'cases' then _object_id"
|
189
|
+
puts "when 'events' then _object_id"
|
190
|
+
puts "when 'visits' then _object_id"
|
181
191
|
puts "else null"
|
182
192
|
}
|
183
193
|
puts "end case;"
|
@@ -185,34 +195,13 @@ module MkAcl
|
|
185
195
|
puts "$$ language sql;"
|
186
196
|
puts
|
187
197
|
end
|
188
|
-
|
189
|
-
signature = "#{acl_schema}.domain_id_of(_table varchar, _id integer)"
|
190
|
-
puts %(
|
191
|
-
-- FIXME: Ugly implementation
|
192
|
-
drop function if exists #{signature} cascade;
|
193
|
-
create function #{signature} returns integer as $$
|
194
|
-
select coalesce(
|
195
|
-
acl_portal.visit_id_of(_table, _id),
|
196
|
-
acl_portal.event_id_of(_table, _id),
|
197
|
-
acl_portal.case_id_of(_table, _id)
|
198
|
-
);
|
199
|
-
$$ language sql;
|
200
|
-
).align
|
201
|
-
puts
|
202
198
|
end
|
203
199
|
|
204
|
-
#
|
205
|
-
#
|
200
|
+
# Generate
|
206
201
|
# case_id_of(r record)
|
207
202
|
# event_id_of(r record)
|
208
203
|
# domain_id_of(r record)
|
209
204
|
#
|
210
|
-
# Returns the case, event, or visit id associated with the given record.
|
211
|
-
# The record should be an ACL table record type
|
212
|
-
#
|
213
|
-
# Very useful in before insert triggers because it takes a record (eg.
|
214
|
-
# NEW) instead of an ID
|
215
|
-
#
|
216
205
|
def generate_general_id_of_record_functions
|
217
206
|
links = (conn.structs %(
|
218
207
|
select
|
@@ -276,22 +265,22 @@ module MkAcl
|
|
276
265
|
puts
|
277
266
|
end
|
278
267
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
268
|
+
# signature = "#{acl_schema}.domain_id_of(_r record)"
|
269
|
+
# puts %(
|
270
|
+
# -- Note: Ugly implementation but maybe close to optimal
|
271
|
+
# drop function if exists #{signature} cascade;
|
272
|
+
# create function #{signature} returns integer as $$
|
273
|
+
# declare
|
274
|
+
# _object_id integer;
|
275
|
+
# begin
|
276
|
+
# select coalesce(acl_portal.visit_id_of(_r), acl_portal.event_id_of(_r), acl_portal.case_id_of(_r))
|
277
|
+
# into _object_id;
|
278
|
+
# return _object_id;
|
279
|
+
# end;
|
280
|
+
# $$ language plpgsql
|
281
|
+
# set search_path to ''; -- intentionally ''
|
282
|
+
# ).align
|
283
|
+
# puts
|
295
284
|
end
|
296
285
|
end
|
297
286
|
end
|