letopis 1.4.1 → 2.0.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.
- package/AGENT-CHEATSHEET.md +329 -278
- package/CHANGELOG.md +558 -1
- package/README.md +606 -1037
- package/dist/acl.d.ts +78 -1
- package/dist/acl.js +264 -13
- package/dist/admin.d.ts +10 -141
- package/dist/admin.js +24 -175
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +5 -15
- package/dist/cache.d.ts +8 -1
- package/dist/cache.js +33 -10
- package/dist/chain.d.ts +19 -41
- package/dist/chain.js +169 -201
- package/dist/cli.js +51 -88
- package/dist/demo/booking.d.ts +352 -36
- package/dist/describe.d.ts +47 -0
- package/dist/describe.js +195 -0
- package/dist/errors.d.ts +59 -13
- package/dist/errors.js +190 -29
- package/dist/index.d.ts +52 -71
- package/dist/index.js +245 -130
- package/dist/internal.d.ts +12 -0
- package/dist/internal.js +5 -0
- package/dist/jsonschema.js +18 -17
- package/dist/load.d.ts +20 -12
- package/dist/load.js +225 -186
- package/dist/mcp.d.ts +5 -7
- package/dist/mcp.js +171 -215
- package/dist/messages/access.d.ts +90 -0
- package/dist/messages/access.js +113 -0
- package/dist/messages/api.d.ts +171 -0
- package/dist/messages/api.js +215 -0
- package/dist/messages/boot.d.ts +201 -0
- package/dist/messages/boot.js +256 -0
- package/dist/messages/codes.d.ts +172 -0
- package/dist/messages/codes.js +173 -0
- package/dist/messages/index.d.ts +95 -0
- package/dist/messages/index.js +118 -0
- package/dist/messages/mcp.d.ts +306 -0
- package/dist/messages/mcp.js +387 -0
- package/dist/messages/model.d.ts +233 -0
- package/dist/messages/model.js +283 -0
- package/dist/messages/query.d.ts +131 -0
- package/dist/messages/query.js +158 -0
- package/dist/messages/sql-access.d.ts +70 -0
- package/dist/messages/sql-access.js +87 -0
- package/dist/messages/sql-core.d.ts +150 -0
- package/dist/messages/sql-core.js +181 -0
- package/dist/messages/sql-time.d.ts +71 -0
- package/dist/messages/sql-time.js +88 -0
- package/dist/messages/sql-write.d.ts +246 -0
- package/dist/messages/sql-write.js +270 -0
- package/dist/messages/time.d.ts +23 -0
- package/dist/messages/time.js +29 -0
- package/dist/messages/types.d.ts +27 -0
- package/dist/messages/types.js +5 -0
- package/dist/messages/write.d.ts +405 -0
- package/dist/messages/write.js +509 -0
- package/dist/model.d.ts +73 -60
- package/dist/model.js +26 -29
- package/dist/ops.d.ts +35 -0
- package/dist/ops.js +101 -0
- package/dist/pglite.d.ts +0 -9
- package/dist/pglite.js +3 -1
- package/dist/registry.d.ts +3 -19
- package/dist/registry.js +3 -16
- package/dist/signals.d.ts +5 -9
- package/dist/signals.js +5 -9
- package/dist/sql.d.ts +13 -6
- package/dist/sql.js +176 -83
- package/dist/sync.d.ts +9 -3
- package/dist/sync.js +51 -21
- package/dist/tx.d.ts +26 -1
- package/dist/tx.js +44 -14
- package/dist/typed.d.ts +240 -45
- package/dist/types.d.ts +23 -91
- package/dist/types.js +12 -14
- package/dist/up.d.ts +53 -32
- package/dist/up.js +184 -73
- package/dist/uuid.js +5 -5
- package/dist/validate.d.ts +13 -1
- package/dist/validate.js +61 -29
- package/dist/watch.d.ts +11 -10
- package/dist/watch.js +33 -70
- package/dist/write.d.ts +9 -16
- package/dist/write.js +118 -187
- package/llms.txt +22 -25
- package/package.json +8 -11
- package/sql/10-core.sql +7 -50
- package/sql/15-errors.sql +64 -10
- package/sql/20-context.sql +7 -6
- package/sql/30-validate.sql +35 -27
- package/sql/40-class.sql +20 -114
- package/sql/50-acl.sql +8 -13
- package/sql/60-write.sql +220 -667
- package/sql/70-read.sql +20 -16
- package/sql/80-auth.sql +24 -27
- package/sql/90-time.sql +70 -859
- package/sql/95-seed.system.sql +7 -14
- package/sql/99-revision.sql +2 -1
- package/sql/README.md +32 -24
- package/sql/migrate/README.md +14 -0
- package/AGENT-CHEATSHEET.en.md +0 -485
- package/MIGRATION.md +0 -190
- package/README.en.md +0 -2859
- package/dist/import.d.ts +0 -185
- package/dist/import.js +0 -820
- package/dist/project.d.ts +0 -350
- package/dist/project.js +0 -1224
- package/sql/75-history.sql +0 -358
- package/sql/88-derived.sql +0 -1235
- package/sql/migrate/0006-changed-paths.sql +0 -3
- package/sql/migrate/0007-secret-account-fk.sql +0 -12
package/sql/40-class.sql
CHANGED
|
@@ -10,14 +10,9 @@
|
|
|
10
10
|
-- нескольким полям (anyOf, oneOf) потомок заменяет целиком, как поле
|
|
11
11
|
-- ends { <роль>: { class: <класс> | [<класс>…], many, required, min, max, onDelete, meta } }
|
|
12
12
|
-- key [<поле или роль>…] — из них вычисляется id (§2.3); [] — класс без ключа
|
|
13
|
-
-- history true | false
|
|
13
|
+
-- history true | false — вести ли журнал версий
|
|
14
14
|
-- ownerDefault 'tenant' | 'actor'; cache { ttl }; meta {…} до 64 КБ
|
|
15
|
-
--
|
|
16
|
-
-- aggregate { <поле>: { sum: <поле источника> | count: true, of: <класс-источник>, via: <роль>, where?: JSON Schema } }
|
|
17
|
-
-- агрегаты в базе (план 1.1, §2.4): поле держит триггер по живым строкам источника; наследуется,
|
|
18
|
-
-- потомок заменяет целиком
|
|
19
|
-
-- resolved развёрнутое описание с учётом предков — пишет триггер, присланное значение перезаписывается; у
|
|
20
|
-
-- источника агрегатов в нём ещё метка aggregatedBy — какие цели считают его строки (88-derived.sql)
|
|
15
|
+
-- resolved развёрнутое описание с учётом предков — пишет триггер, присланное значение перезаписывается
|
|
21
16
|
-- Скомпилированная проверка данных класса лежит в колонке validator строки Class.
|
|
22
17
|
|
|
23
18
|
create or replace function @SCHEMA@.class_metaschema() returns jsonb
|
|
@@ -62,35 +57,10 @@ return $json${
|
|
|
62
57
|
}
|
|
63
58
|
},
|
|
64
59
|
"key": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
|
65
|
-
"history": {"
|
|
66
|
-
"type": "object",
|
|
67
|
-
"additionalProperties": false,
|
|
68
|
-
"required": ["all"],
|
|
69
|
-
"properties": {
|
|
70
|
-
"all": {"type": "string"}, "daily": {"type": "string"}, "weekly": {"type": "string"},
|
|
71
|
-
"monthly": {"type": "string"}, "yearly": {"type": "string"}, "tz": {"type": "string"}
|
|
72
|
-
}
|
|
73
|
-
}]},
|
|
60
|
+
"history": {"type": "boolean"},
|
|
74
61
|
"ownerDefault": {"type": "string", "enum": ["tenant", "actor"]},
|
|
75
62
|
"cache": {"type": "object"},
|
|
76
63
|
"meta": {"type": "object"},
|
|
77
|
-
"derivedBy": {"type": "string", "pattern": "^[a-z0-9][a-z0-9_.:-]{0,63}$"},
|
|
78
|
-
"aggregate": {
|
|
79
|
-
"type": "object",
|
|
80
|
-
"propertyNames": {"type": "string", "pattern": "^[^$]"},
|
|
81
|
-
"additionalProperties": {
|
|
82
|
-
"type": "object",
|
|
83
|
-
"required": ["of", "via"],
|
|
84
|
-
"additionalProperties": false,
|
|
85
|
-
"properties": {
|
|
86
|
-
"sum": {"type": "string", "pattern": "^[^$]"},
|
|
87
|
-
"count": {"type": "boolean", "enum": [true]},
|
|
88
|
-
"of": {"type": "string", "pattern": "^[A-Za-zА-Яа-яЁё_][A-Za-zА-Яа-яЁё0-9_]{0,62}$"},
|
|
89
|
-
"via": {"type": "string", "pattern": "^[A-Za-zА-Яа-яЁё_][A-Za-zА-Яа-яЁё0-9_]{0,62}$"},
|
|
90
|
-
"where": {"type": "object"}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
64
|
"resolved": {"type": "object"}
|
|
95
65
|
}
|
|
96
66
|
}$json$::jsonb;
|
|
@@ -103,11 +73,12 @@ begin
|
|
|
103
73
|
select c.rules, c.validator, c.vars into rules, validator, vars from @SCHEMA@.compile_validator(@SCHEMA@.class_metaschema()) c;
|
|
104
74
|
end $$;
|
|
105
75
|
|
|
106
|
-
-- Строка класса по имени: собственная строка
|
|
76
|
+
-- Строка класса по имени: собственная строка арендатора, иначе системная. Поиск — по индексу
|
|
107
77
|
-- entity_class_name, без вычисления v5 (два SHA-1 стоили 30 мкс на поиск, а запись ищет описание
|
|
108
|
-
-- несколько раз). Пара «арендатор, имя» однозначна: id строки Class — v5 от имени.
|
|
109
|
-
--
|
|
110
|
-
--
|
|
78
|
+
-- несколько раз). Пара «арендатор, имя» однозначна: id строки Class — v5 от имени. Порядок важен:
|
|
79
|
+
-- арендатор не заведёт класс с именем уже существующего класса System (class_admit,
|
|
80
|
+
-- system_name_taken), но класс System может появиться позже одноимённого класса арендатора — тогда
|
|
81
|
+
-- строк две, и действует собственная (атака №54 в engine-attacks). Нет строки — null.
|
|
111
82
|
create or replace function @SCHEMA@.class_row(p_name text, p_tenant uuid) returns @SCHEMA@.entity
|
|
112
83
|
language sql stable
|
|
113
84
|
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
@@ -136,8 +107,6 @@ declare
|
|
|
136
107
|
hist jsonb;
|
|
137
108
|
od text;
|
|
138
109
|
cache jsonb;
|
|
139
|
-
der text;
|
|
140
|
-
agg jsonb;
|
|
141
110
|
comp jsonb := '{}';
|
|
142
111
|
i int;
|
|
143
112
|
v jsonb;
|
|
@@ -146,10 +115,10 @@ begin
|
|
|
146
115
|
while cur->>'parent' is not null loop
|
|
147
116
|
cur := (@SCHEMA@.class_row(cur->>'parent', p_tenant)).data;
|
|
148
117
|
if cur is null then
|
|
149
|
-
perform @SCHEMA@.
|
|
118
|
+
perform @SCHEMA@.lt_fail('sql-core.parent_not_found', jsonb_build_object('parent', chain[cardinality(chain)]->>'parent'));
|
|
150
119
|
end if;
|
|
151
120
|
if cur->>'name' = any (names) then
|
|
152
|
-
perform @SCHEMA@.
|
|
121
|
+
perform @SCHEMA@.lt_fail('sql-core.inheritance_cycle', jsonb_build_object('cls', cur->>'name'));
|
|
153
122
|
end if;
|
|
154
123
|
names := names || (cur->>'name');
|
|
155
124
|
chain := chain || cur;
|
|
@@ -167,16 +136,6 @@ begin
|
|
|
167
136
|
if v ? 'history' then hist := v->'history'; end if;
|
|
168
137
|
if v ? 'ownerDefault' then od := v->>'ownerDefault'; end if;
|
|
169
138
|
if v ? 'cache' then cache := v->'cache'; end if;
|
|
170
|
-
-- производный класс (план 1.1, §2.4): проектор задаёт предок, потомок его не меняет
|
|
171
|
-
if v ? 'derivedBy' then
|
|
172
|
-
if der is not null and der <> v->>'derivedBy' then
|
|
173
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class',
|
|
174
|
-
format('derivedBy класса %s (%s) не совпадает с предком (%s)', v->>'name', v->>'derivedBy', der));
|
|
175
|
-
end if;
|
|
176
|
-
der := v->>'derivedBy';
|
|
177
|
-
end if;
|
|
178
|
-
-- агрегаты (план 1.1, §2.4): потомок заменяет описание предка целиком, {} — снимает унаследованное
|
|
179
|
-
if v ? 'aggregate' then agg := v->'aggregate'; end if;
|
|
180
139
|
for m in select * from jsonb_each(coalesce(v->'meta', '{}')) loop
|
|
181
140
|
if m.value = 'null'::jsonb then meta := meta - m.key; else meta := meta || jsonb_build_object(m.key, m.value); end if;
|
|
182
141
|
end loop;
|
|
@@ -192,12 +151,10 @@ begin
|
|
|
192
151
|
'history', coalesce(hist, 'true'),
|
|
193
152
|
'ownerDefault', coalesce(od, 'tenant'),
|
|
194
153
|
'cache', cache,
|
|
195
|
-
|
|
196
|
-
'meta', meta) || case when der is not null then jsonb_build_object('derivedBy', der) else '{}'::jsonb end
|
|
197
|
-
|| case when agg is not null and agg <> '{}'::jsonb then jsonb_build_object('aggregate', agg) else '{}'::jsonb end;
|
|
154
|
+
'meta', meta);
|
|
198
155
|
end $$;
|
|
199
156
|
|
|
200
|
-
-- Проверка описания класса: метасхема, правила ключа (§2.3), правило unset (§2.10), размер meta
|
|
157
|
+
-- Проверка описания класса: метасхема, правила ключа (§2.3), правило unset (§2.10), размер meta, журнал системных классов.
|
|
201
158
|
-- Возвращает строку с заполненными resolved и validator.
|
|
202
159
|
create or replace function @SCHEMA@.class_prepare(p_data jsonb, p_tenant uuid, out data jsonb, out validator jsonpath)
|
|
203
160
|
language plpgsql stable
|
|
@@ -211,18 +168,15 @@ declare
|
|
|
211
168
|
fld jsonb;
|
|
212
169
|
e record;
|
|
213
170
|
c record;
|
|
214
|
-
prev interval;
|
|
215
|
-
tiers text[];
|
|
216
|
-
i int;
|
|
217
171
|
begin
|
|
218
172
|
data := p_data - 'resolved';
|
|
219
173
|
select * into ms from @SCHEMA@.class_meta_validator();
|
|
220
174
|
bad := @SCHEMA@.validate_data(ms.rules, ms.validator, ms.vars, data);
|
|
221
175
|
if bad <> '[]'::jsonb then
|
|
222
|
-
perform @SCHEMA@.
|
|
176
|
+
perform @SCHEMA@.lt_fail('sql-core.class_metaschema', '{}'::jsonb, jsonb_build_object('issues', bad));
|
|
223
177
|
end if;
|
|
224
178
|
if octet_length(coalesce(data->'meta', '{}')::text) > 65536 then
|
|
225
|
-
perform @SCHEMA@.
|
|
179
|
+
perform @SCHEMA@.lt_fail('sql-core.meta_too_large');
|
|
226
180
|
end if;
|
|
227
181
|
res := @SCHEMA@.class_resolve(data, p_tenant);
|
|
228
182
|
-- ключ: только обязательные строковые поля и обязательные одиночные концы
|
|
@@ -230,74 +184,26 @@ begin
|
|
|
230
184
|
fld := res->'schema'->'properties'->k;
|
|
231
185
|
if fld is not null then
|
|
232
186
|
if not (res->'schema'->'required' ? k) or coalesce(fld->>'type', '') <> 'string' then
|
|
233
|
-
perform @SCHEMA@.
|
|
187
|
+
perform @SCHEMA@.lt_fail('sql-core.key_field_type', jsonb_build_object('field', k));
|
|
234
188
|
end if;
|
|
235
189
|
elsif res->'ends' ? k then
|
|
236
190
|
if coalesce((res->'ends'->k->>'many')::boolean, false) or not coalesce((res->'ends'->k->>'required')::boolean, false) then
|
|
237
|
-
perform @SCHEMA@.
|
|
191
|
+
perform @SCHEMA@.lt_fail('sql-core.key_end_type', jsonb_build_object('end', k));
|
|
238
192
|
end if;
|
|
239
193
|
else
|
|
240
|
-
perform @SCHEMA@.
|
|
194
|
+
perform @SCHEMA@.lt_fail('sql-core.key_unknown_name', jsonb_build_object('name', k));
|
|
241
195
|
end if;
|
|
242
196
|
end loop;
|
|
243
197
|
-- unset — только для необязательного одиночного конца и множественного без min
|
|
244
198
|
for e in select key, value from jsonb_each(res->'ends') loop
|
|
245
199
|
if e.value->>'onDelete' = 'unset' and (coalesce((e.value->>'required')::boolean, false) or coalesce((e.value->>'min')::int, 0) > 0) then
|
|
246
|
-
perform @SCHEMA@.
|
|
200
|
+
perform @SCHEMA@.lt_fail('sql-core.unset_required_end', jsonb_build_object('end', e.key));
|
|
247
201
|
end if;
|
|
248
202
|
end loop;
|
|
249
|
-
--
|
|
250
|
-
if coalesce((data->>'system')::boolean, false) and jsonb_typeof(res->'history') <> 'boolean' then
|
|
251
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', 'системным классам политика хранения не задаётся');
|
|
252
|
-
end if;
|
|
203
|
+
-- журнал системных классов — аудит прав и схемы: его не выключают
|
|
253
204
|
if coalesce((data->>'system')::boolean, false) and res->'history' = 'false'::jsonb then
|
|
254
|
-
perform @SCHEMA@.
|
|
255
|
-
end if;
|
|
256
|
-
-- политика хранения (§2.11): сроки ярусов по возрастанию, all не меньше суток, 'forever' — только последний
|
|
257
|
-
if jsonb_typeof(res->'history') = 'object' then
|
|
258
|
-
prev := interval '0';
|
|
259
|
-
tiers := array(select u.tn from unnest(array['all', 'daily', 'weekly', 'monthly', 'yearly']) with ordinality u(tn, ord)
|
|
260
|
-
where res->'history' ? u.tn order by u.ord);
|
|
261
|
-
for i in 1 .. cardinality(tiers) loop
|
|
262
|
-
k := res->'history'->>tiers[i];
|
|
263
|
-
if k = 'forever' then
|
|
264
|
-
if i <> cardinality(tiers) then
|
|
265
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', format('history: forever допустим только у последнего яруса, а стоит у %s', tiers[i]));
|
|
266
|
-
end if;
|
|
267
|
-
elsif not pg_input_is_valid(k, 'interval') then
|
|
268
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', format('history: срок яруса %s не разбирается: %s', tiers[i], k));
|
|
269
|
-
elsif k::interval <= prev then
|
|
270
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', format('history: сроки ярусов должны возрастать (%s)', tiers[i]));
|
|
271
|
-
else
|
|
272
|
-
prev := k::interval;
|
|
273
|
-
end if;
|
|
274
|
-
end loop;
|
|
275
|
-
if (res->'history'->>'all') <> 'forever' and (res->'history'->>'all')::interval < interval '1 day' then
|
|
276
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', 'history: ярус all не меньше суток');
|
|
277
|
-
end if;
|
|
278
|
-
if res->'history' ? 'tz' and not exists (select 1 from pg_timezone_names where name = res->'history'->>'tz') then
|
|
279
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', format('history: неизвестный часовой пояс %s', res->'history'->>'tz'));
|
|
280
|
-
end if;
|
|
281
|
-
end if;
|
|
282
|
-
-- производный класс (план 1.1, §2.4): системным не бывает; его строки проектор удаляет и строит заново,
|
|
283
|
-
-- поэтому они не мешают удалять строки, на которые ссылаются (onDelete только cascade или unset)
|
|
284
|
-
if res ? 'derivedBy' then
|
|
285
|
-
if coalesce((data->>'system')::boolean, false) then
|
|
286
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class', 'системный класс не бывает производным');
|
|
287
|
-
end if;
|
|
288
|
-
for e in select key, value from jsonb_each(res->'ends') loop
|
|
289
|
-
if coalesce(e.value->>'onDelete', 'restrict') not in ('cascade', 'unset') then
|
|
290
|
-
perform @SCHEMA@.lt_raise('LT010', 'invalid_class',
|
|
291
|
-
format('конец %s производного класса — onDelete cascade или unset: производная строка не мешает удалять цель', e.key));
|
|
292
|
-
end if;
|
|
293
|
-
end loop;
|
|
294
|
-
end if;
|
|
295
|
-
-- агрегаты (план 1.1, §2.4; 88-derived.sql): цель — поля, источники, цикл, условия where; источник — метка
|
|
296
|
-
-- aggregatedBy и та же сверка против агрегатов, которые считают его строки (источник часто описывают после цели)
|
|
297
|
-
if res ? 'aggregate' then
|
|
298
|
-
res := @SCHEMA@.agg_prepare(res, data, p_tenant);
|
|
205
|
+
perform @SCHEMA@.lt_fail('sql-core.system_history_off');
|
|
299
206
|
end if;
|
|
300
|
-
res := @SCHEMA@.agg_source(res, p_tenant);
|
|
301
207
|
select * into c from @SCHEMA@.compile_validator(res->'schema');
|
|
302
208
|
data := data || jsonb_build_object('resolved', res || jsonb_build_object('rules', c.rules, 'vars', c.vars));
|
|
303
209
|
validator := c.validator;
|
package/sql/50-acl.sql
CHANGED
|
@@ -306,7 +306,7 @@ declare
|
|
|
306
306
|
c record;
|
|
307
307
|
begin
|
|
308
308
|
if p_op not in ('READ', 'WRITE', 'DELETE') then
|
|
309
|
-
perform @SCHEMA@.lt_fail('
|
|
309
|
+
perform @SCHEMA@.lt_fail('sql-access.acl_bad_op', jsonb_build_object('op', p_op));
|
|
310
310
|
end if;
|
|
311
311
|
select * into c from @SCHEMA@.ctx();
|
|
312
312
|
if c.account is null then
|
|
@@ -382,14 +382,14 @@ begin
|
|
|
382
382
|
select e.data into a from @SCHEMA@.entity e
|
|
383
383
|
where e.id = p_account and e.class = 'Account' and e.tenant = '@SYSTEM@'::uuid and coalesce((e.data->>'enabled')::boolean, true);
|
|
384
384
|
if a is null then
|
|
385
|
-
perform @SCHEMA@.lt_fail('
|
|
385
|
+
perform @SCHEMA@.lt_fail('sql-access.account_unavailable');
|
|
386
386
|
end if;
|
|
387
387
|
if p_tenant <> p_account then
|
|
388
388
|
select x.data into m from @SCHEMA@.entity x
|
|
389
389
|
where x.id = @SCHEMA@.v5_id(p_tenant, 'member', array[p_account::text, p_tenant::text]) and x.class = 'member' and x.tenant = p_tenant;
|
|
390
390
|
if m is null or not exists (select 1 from @SCHEMA@.entity e where e.id = p_tenant and e.class = 'Account'
|
|
391
391
|
and e.tenant = '@SYSTEM@'::uuid and coalesce((e.data->>'enabled')::boolean, true)) then
|
|
392
|
-
perform @SCHEMA@.lt_fail('
|
|
392
|
+
perform @SCHEMA@.lt_fail('sql-access.no_membership');
|
|
393
393
|
end if;
|
|
394
394
|
end if;
|
|
395
395
|
cats := array(select jsonb_array_elements_text(coalesce(a->'categories', '[]'::jsonb)));
|
|
@@ -417,7 +417,7 @@ begin
|
|
|
417
417
|
where @SCHEMA@.acl_endpoint_ok(r.pattern, p_endpoint)
|
|
418
418
|
order by r.weight desc, r.deny desc, r.alias, r.id
|
|
419
419
|
limit 1) w),
|
|
420
|
-
jsonb_build_object('allow', false, 'message',
|
|
420
|
+
jsonb_build_object('allow', false, 'message', @SCHEMA@.lt_text('sql-access.default_deny')));
|
|
421
421
|
end $$;
|
|
422
422
|
|
|
423
423
|
-- db.acl.check(endpoint): решение для актора сессии по правилам System и его арендатора.
|
|
@@ -430,7 +430,7 @@ declare
|
|
|
430
430
|
begin
|
|
431
431
|
select * into c from @SCHEMA@.ctx();
|
|
432
432
|
if c.account is null then
|
|
433
|
-
perform @SCHEMA@.lt_fail('no_session'
|
|
433
|
+
perform @SCHEMA@.lt_fail('sql-access.no_session');
|
|
434
434
|
end if;
|
|
435
435
|
return @SCHEMA@.acl_api_for(c.tenant, c.categories, c.roles, p_endpoint, false);
|
|
436
436
|
end $$;
|
|
@@ -445,10 +445,10 @@ declare
|
|
|
445
445
|
begin
|
|
446
446
|
select * into c from @SCHEMA@.ctx();
|
|
447
447
|
if c.account is null then
|
|
448
|
-
perform @SCHEMA@.lt_fail('no_session'
|
|
448
|
+
perform @SCHEMA@.lt_fail('sql-access.no_session');
|
|
449
449
|
end if;
|
|
450
450
|
if not coalesce((@SCHEMA@.acl_api_for(c.tenant, c.categories, c.roles, p_endpoint, true)->>'allow')::boolean, false) then
|
|
451
|
-
perform @SCHEMA@.lt_fail('
|
|
451
|
+
perform @SCHEMA@.lt_fail('sql-access.no_right', jsonb_build_object('endpoint', p_endpoint));
|
|
452
452
|
end if;
|
|
453
453
|
end $$;
|
|
454
454
|
|
|
@@ -479,9 +479,7 @@ return (p_tenant = c_tenant and (p_class = 'Class' or @SCHEMA@.acl_row_ok(c_plan
|
|
|
479
479
|
or (p_class = 'Account' and p_id = c_account)
|
|
480
480
|
or (p_class = 'member' and p_links->>'User' = c_account::text);
|
|
481
481
|
|
|
482
|
-
--
|
|
483
|
-
drop function if exists @SCHEMA@.row_visible(uuid, text, uuid, uuid, uuid);
|
|
484
|
-
|
|
482
|
+
-- Политики: create policy не знает or replace — повторный накат пересоздаёт их (drop policy if exists и create policy).
|
|
485
483
|
drop policy if exists entity_select on @SCHEMA@.entity;
|
|
486
484
|
create policy entity_select on @SCHEMA@.entity for select to @APP@
|
|
487
485
|
using (
|
|
@@ -520,9 +518,6 @@ create policy log_select on @SCHEMA@.log for select to @APP@
|
|
|
520
518
|
or (class = 'member' and links->>'User' = (select @SCHEMA@.ctx_account())::text)
|
|
521
519
|
);
|
|
522
520
|
|
|
523
|
-
-- ранняя редакция этапа 5: проверка строки по одной карте плана в политиках
|
|
524
|
-
drop function if exists @SCHEMA@.acl_ok(jsonb, text, uuid, uuid, uuid, text[], jsonb, jsonb);
|
|
525
|
-
|
|
526
521
|
do $$
|
|
527
522
|
declare f text;
|
|
528
523
|
begin
|