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
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
|