mikras_utils 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a66c5d0dcdafd3fb71c101b1cb02d5acb3ebe464418c070ed98c9e192301a45
4
- data.tar.gz: 6bf90eead1cf7fd35b8a5082bae1a68cc6aec3a8ef158f025d6f3643c49f0f8f
3
+ metadata.gz: 1c7d48444e75236d0188b539182a80aefeaea2770cdf7982188d7f055c8b552e
4
+ data.tar.gz: 5d790361a1c314d7bda6013c1db9c904b50c81f3be39c8396d6c9e4da43e68bd
5
5
  SHA512:
6
- metadata.gz: 8339a0a8a6f4180aa26b3f1430ce722d3f399d45b9875e59dfa0ace4086dcd9db82dc8b8b2c66596b52391e4140e0475bfb033b29470ee71cc2de000c138b0c1
7
- data.tar.gz: 525550b69df7f1151f6f617c4cf9b9f5f034501cff135d2de268ed2d76c7e393f57b8aa3cf68f8e768d03dfd04669cea9c81536891c0c73b645f4e00493eeaf8
6
+ metadata.gz: 8e117ef1b45f0217ec14065d37405cf4b043259465c82a7df09caebc416a60ec15b80293c45c349ffb9cd0b9bd07fe8b9420b85e19f24a88a5dd62d3be62380f
7
+ data.tar.gz: 285faf4c815810af7111d49176218401f86afc1568a915c721ee67787cbb11df2cd6c016bad9d7b33db61b6120d5726d65aa8e2f0d27fc4cfb06a5685f69de9f
@@ -68,8 +68,8 @@ module MkAcl
68
68
  _domain_id integer;
69
69
  _acls integer[]; -- per-rule ACLs
70
70
  _acl_select integer[][]; -- per-action ACLs
71
- _acl_update integer[][];
72
- _acl_delete integer[][];
71
+ _acl_update integer[][]; --
72
+ _acl_delete integer[][]; --
73
73
  begin
74
74
  ).align
75
75
  indent {
@@ -9,7 +9,7 @@ module MkAcl
9
9
 
10
10
  # Map from domain table to list of [table, path, links] tuples. The
11
11
  # entries describes the SQL link chain from the table to the given domain
12
- # table (cases or events)
12
+ # table (cases, events, or visits)
13
13
  attr_reader :chains
14
14
 
15
15
  def initialize(generator)
@@ -30,7 +30,7 @@ module MkAcl
30
30
  meta.chains
31
31
  where src_schema_name = '#{app_schema}'
32
32
  and dst_schema_name = '#{app_schema}'
33
- and dst_table_name in ('cases', 'events')
33
+ and dst_table_name in #{conn.quote_value_list(DOMAIN_TABLES)}
34
34
  )
35
35
 
36
36
  generate_per_table_id_functions
@@ -45,10 +45,10 @@ module MkAcl
45
45
  # def table_seq() @table_seq ||= spec.tables.map(&:uid).join(', ') end
46
46
 
47
47
  # Generate a set of per-table functions that returns the associated
48
- # case_id/event_id for the given record. The Functions are generated for
49
- # each table in the spec file
48
+ # case_id/event_id/vist_id for the given record. The Functions are
49
+ # generated for each table in the spec file
50
50
  #
51
- # The geneated functions are
51
+ # The geneated functions are
52
52
  #
53
53
  # case_id_of_RECORD(id integer)
54
54
  # event_id_of_RECORD(id integer)
@@ -72,7 +72,7 @@ module MkAcl
72
72
  #
73
73
  def generate_per_table_id_functions
74
74
  # Generate functions by domain
75
- for domain in %w(case event)
75
+ for domain in DOMAINS
76
76
 
77
77
  # Create the identity functions first. The identity functions are
78
78
  # case_id_of_case() and event_id_of_event(). This makes some stuff
@@ -95,7 +95,7 @@ module MkAcl
95
95
  record_name = Prick::Inflector.singularize(table_name)
96
96
  id_arg = "_#{id_field}"
97
97
  signature = "#{acl_schema}.#{id_field}_of_#{record_name}(#{id_arg} integer)"
98
- puts "drop function if exists #{signature} cascade;"
98
+ puts "drop function if exists #{signature} cascade;"
99
99
  puts "create function #{signature} returns integer as $$"
100
100
  indent {
101
101
  tables.pop
@@ -120,14 +120,17 @@ module MkAcl
120
120
  end
121
121
  end
122
122
 
123
+ # Tables that references visits, events, and cases
124
+ visit_tables = chains['visits'].map(&:first)
125
+
123
126
  # Tables that references events and cases
124
- event_tables = chains['events'].map(&:first)
127
+ event_tables = chains['events'].map(&:first) - visit_tables
125
128
 
126
- # Tables that references only cases
129
+ # Tables that references only cases
127
130
  case_tables = chains['cases'].map(&:first) - event_tables
128
131
 
129
132
  # Create domain functions
130
- for domain, tables in { case: case_tables, event: event_tables }
133
+ for domain, tables in { case: case_tables, event: event_tables, visit: visit_tables }
131
134
  for table_name in tables
132
135
  record_name = Prick::Inflector.singularize(table_name)
133
136
  signature = "#{acl_schema}.domain_id_of_#{record_name}(_id integer)"
@@ -154,7 +157,7 @@ module MkAcl
154
157
  # specialized versions above
155
158
  #
156
159
  def generate_general_id_functions
157
- for domain in %w(case event)
160
+ for domain in DOMAINS
158
161
  domain_table = Prick::Inflector.pluralize(domain)
159
162
  field = "#{domain}_id"
160
163
  signature = "#{acl_schema}.#{field}_of(_table varchar, _id integer)"
@@ -162,7 +165,7 @@ module MkAcl
162
165
  puts %(
163
166
  drop function if exists #{signature} cascade;
164
167
  create function #{signature} returns integer as $$
165
- select
168
+ select
166
169
  ).align
167
170
  indent(2) {
168
171
  puts "case _table"
@@ -174,6 +177,7 @@ module MkAcl
174
177
  end
175
178
  puts "when 'cases' then _id"
176
179
  puts "when 'events' then _id"
180
+ puts "when 'visits' then _id"
177
181
  puts "else null"
178
182
  }
179
183
  puts "end case;"
@@ -187,7 +191,11 @@ module MkAcl
187
191
  -- FIXME: Ugly implementation
188
192
  drop function if exists #{signature} cascade;
189
193
  create function #{signature} returns integer as $$
190
- select coalesce(acl_portal.event_id_of(_table, _id), acl_portal.case_id_of(_table, _id));
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
+ );
191
199
  $$ language sql;
192
200
  ).align
193
201
  puts
@@ -199,8 +207,8 @@ module MkAcl
199
207
  # event_id_of(r record)
200
208
  # domain_id_of(r record)
201
209
  #
202
- # Returns the case or event id associated with the given record. The
203
- # record should be an ACL table record type
210
+ # Returns the case, event, or visit id associated with the given record.
211
+ # The record should be an ACL table record type
204
212
  #
205
213
  # Very useful in before insert triggers because it takes a record (eg.
206
214
  # NEW) instead of an ID
@@ -216,7 +224,7 @@ module MkAcl
216
224
  where schema_name = '#{app_schema}'
217
225
  )).group_by(&:table_name)
218
226
 
219
- for domain in %w(case event)
227
+ for domain in MkAcl::DOMAINS
220
228
  domain_id_field = "#{domain}_id"
221
229
  signature = "#{acl_schema}.#{domain_id_field}_of(_r record)"
222
230
  puts %(
@@ -233,22 +241,22 @@ module MkAcl
233
241
  ).align
234
242
  indent {
235
243
  indent {
236
-
237
-
238
244
  for table in spec.tables
239
245
  next if domain == "event" && table.domain == "case"
240
- # puts "------------------------"
241
- # p table.name
242
- # p links.keys
246
+ next if domain == "visit" && table.domain == "event"
243
247
  link = links[table.name]&.first or next
244
248
  if table.name == "cases"
245
249
  puts "when '#{table.uid}' then return _r.id;"
246
250
  elsif domain == "event" && table.name == "events"
247
251
  puts "when '#{table.uid}' then return _r.id;"
252
+ elsif domain == "visit" && table.name == "visits"
253
+ puts "when '#{table.uid}' then return _r.id;"
248
254
  elsif link.ref_table_name == "cases"
249
255
  puts "when '#{table.uid}' then return _r.#{link.column_name};"
250
256
  elsif link.ref_table_name == "events" && table.domain == "event"
251
257
  puts "when '#{table.uid}' then return _r.#{link.column_name};"
258
+ elsif link.ref_table_name == "visits" && table.domain == "visit"
259
+ puts "when '#{table.uid}' then return _r.#{link.column_name};"
252
260
  else
253
261
  ref_record = spec[link.ref_table_name]&.record_name or next
254
262
  id_of_function = "#{acl_schema}.#{domain_id_field}_of_#{ref_record}"
@@ -276,9 +284,8 @@ module MkAcl
276
284
  declare
277
285
  _id integer;
278
286
  begin
279
- select coalesce(acl_portal.event_id_of(_r), acl_portal.case_id_of(_r))
287
+ select coalesce(acl_portal.visit_id_of(_r), acl_portal.event_id_of(_r), acl_portal.case_id_of(_r))
280
288
  into _id;
281
-
282
289
  return _id;
283
290
  end;
284
291
  $$ language plpgsql
@@ -11,59 +11,128 @@ module MkAcl
11
11
  end
12
12
 
13
13
  def generate
14
- generate_role_functions
14
+ generate_user_role_functions
15
+ generate_current_role_functions
15
16
  end
16
17
 
17
18
  def self.generate(generator) self.new(generator).generate end
18
-
19
+
19
20
  private
20
- def generate_role_functions
21
+ def generate_user_role_functions
21
22
  # TODO: Test. Then combine with per-table methods
22
- signature = "public.current_is_role(_case_id integer, _roles text[])"
23
+ signature = "#{acl_schema}.user_is_role(_user_id integer, _domain_id integer, _roles text[])"
23
24
  puts %(
25
+ -- Return true if the user possess one or more of the given roles on the
26
+ -- domain record (cases, events, visits) with the given ID
27
+ --
24
28
  drop function if exists #{signature} cascade;
25
29
  create function #{signature} returns boolean as $$
26
- select exists(
30
+ select exists (
27
31
  select
28
32
  from #{app_schema}.case_roles cr
29
33
  join #{app_schema}.case_role_users cru on cru.case_role_id = cr.id
30
- where cr.case_id = _case_id
31
- and cru.user_id = public.current_user_id()
34
+ where cr.case_id = _domain_id
35
+ and cru.user_id = _user_id
36
+ and cr.kind = any(_roles)
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
32
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
+
33
56
  );
34
57
  $$ language sql
35
58
  security definer;
36
59
  ).align
37
60
  puts
38
61
 
39
- signature = "public.current_is_role(_case_id integer, role text)"
62
+ signature = "#{acl_schema}.user_is_role(_user_id integer, _domain_id integer, role text)"
63
+ puts %(
64
+ -- Return true if the user has the given role on the domain record
65
+ -- (cases, events, visits). Note that this function overloads the multi-role
66
+ -- version
67
+ --
68
+ drop function if exists #{signature} cascade;
69
+ create function #{signature} returns boolean as $$
70
+ select #{acl_schema}.user_is_role(_user_id, _domain_id, array[role]);
71
+ $$ language sql
72
+ security definer;
73
+ ).align
74
+ puts
75
+
76
+ for domain, roles in DOMAINS.zip([CASE_ROLES, EVENT_ROLES, VISIT_ROLES])
77
+ for role in roles
78
+ signature = "#{acl_schema}.user_is_#{role.downcase}(_user_id integer, _domain_id integer)"
79
+ puts %(
80
+ -- Return true if the user possess the '#{role}' role
81
+ --
82
+ drop function if exists #{signature} cascade;
83
+ create function #{signature} returns boolean as $$
84
+ select #{acl_schema}.user_is_role(_user_id, _domain_id, '#{role}');
85
+ $$ language sql
86
+ security definer;
87
+ ).align
88
+ puts
89
+ end
90
+ end
91
+ end
92
+
93
+ def generate_current_role_functions
94
+ # TODO: Test. Then combine with per-table methods
95
+ signature = "public.current_is_role(_domain_id integer, _roles text[])"
96
+ puts %(
97
+ -- Return true if the current user possess one or more of the given roles on the
98
+ -- domain record (cases, events, visits) with the given ID
99
+ --
100
+ drop function if exists #{signature} cascade;
101
+ create function #{signature} returns boolean as $$
102
+ select #{acl_schema}.user_is_role(public.current_user_id(), _domain_id, _roles);
103
+ $$ language sql
104
+ security definer;
105
+ ).align
106
+ puts
107
+
108
+ signature = "public.current_is_role(_domain_id integer, _role text)"
40
109
  puts %(
110
+ -- Return true if the current user possess the given role on the domain record
111
+ -- (cases, events, visits). Note that this function overloads the multi-role
112
+ -- version
113
+ --
41
114
  drop function if exists #{signature} cascade;
42
115
  create function #{signature} returns boolean as $$
43
- select public.current_is_role(_case_id, array[role]);
116
+ select #{acl_schema}.user_is_role(public.current_user_id(), _domain_id, array[_role]);
44
117
  $$ language sql
45
118
  security definer;
46
119
  ).align
47
120
  puts
48
121
 
49
- for role in MkAcl::ROLES
50
- name = role.downcase
51
- signature = "public.current_is_#{name}(_case_id integer)"
52
- puts %(
53
- drop function if exists #{signature} cascade;
54
- create function #{signature} returns boolean as $$
55
- select exists(
56
- select
57
- from #{app_schema}.case_roles cr
58
- join #{app_schema}.case_role_users cru on cru.case_role_id = cr.id
59
- where cr.case_id = _case_id
60
- and cru.user_id = public.current_user_id()
61
- and cr.kind = '#{role}'
62
- );
63
- $$ language sql
64
- security definer;
65
- ).align
66
- puts
122
+ for domain, roles in DOMAINS.zip([CASE_ROLES, EVENT_ROLES, VISIT_ROLES])
123
+ for role in roles
124
+ signature = "public.current_is_#{role.downcase}(_domain_id integer)"
125
+ puts %(
126
+ -- Return true if the current user possess the '#{role}' role
127
+ --
128
+ drop function if exists #{signature} cascade;
129
+ create function #{signature} returns boolean as $$
130
+ select #{acl_schema}.user_is_role(public.current_user_id(), _domain_id, '#{role}');
131
+ $$ language sql
132
+ security definer;
133
+ ).align
134
+ puts
135
+ end
67
136
  end
68
137
  end
69
138
  end
@@ -6,7 +6,7 @@ module MkAcl
6
6
  # problem is that a record that fails the rule check is silently ignored
7
7
  # which is probably not what you want
8
8
  #
9
- # The roles matches a acl_* array against a role action entry in the spec
9
+ # The roles matches an acl_* array against a role action entry in the spec
10
10
  # file. The acl_* arrays are themselves array of role ids. Each subarray is
11
11
  # indexed using the order in the acl.spec file
12
12
  #
@@ -14,7 +14,7 @@ module MkAcl
14
14
  def parse_spec
15
15
  hash = YAML.load(IO.read(file), symbolize_names: true)
16
16
 
17
- schema = hash.delete(:schema) or raise ArgumentError, "Can't find 'schema' declaration"
17
+ schema = hash.delete(:schema) or raise ArgumentError, "Can't find 'schema' declaration in #{file}"
18
18
  app_schema = schema[:app] or raise ArgumentError, "Can't find 'schema.app' attribute"
19
19
  acl_schema = schema[:acl] or raise ArgumentError, "Can't find 'schema.acl' attribute"
20
20
  spec = Spec.new(file, app_schema, acl_schema)
@@ -9,7 +9,14 @@ require 'prick-inflector'
9
9
  module MkAcl
10
10
  class ParseError < RuntimeError; end
11
11
 
12
- ROLES = %w(RLA LA TA KON AKK CLA CTA ELA ETA)
12
+ DOMAINS = %w(case event visit)
13
+ DOMAIN_TABLES = DOMAINS.map { "#{_1}s" }
14
+
15
+ CASE_ROLES = %w(LA TA KON AKK RLA CLA CTA)
16
+ EVENT_ROLES = %w(ELA ETA)
17
+ VISIT_ROLES = %w(VLA VTA)
18
+
19
+ ROLES = CASE_ROLES + EVENT_ROLES + VISIT_ROLES
13
20
  end
14
21
 
15
22
  require_relative 'mkacl/spec.rb'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MikrasUtils
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
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.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-08 00:00:00.000000000 Z
11
+ date: 2024-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg_conn