@creatornader/agent-bridge 0.4.0 → 0.5.1
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/CHANGELOG.md +85 -1
- package/README.md +76 -7
- package/ROADMAP.md +29 -0
- package/SECURITY.md +30 -0
- package/SKILL.md +5 -1
- package/dist/archive.d.ts +2 -2
- package/dist/{bridge-service-YTjGr91A.d.ts → bridge-service-CNBKdYXh.d.ts} +2 -1
- package/dist/{bridge-store-Boaj-ZbO.d.ts → bridge-store-ByJ6CaJk.d.ts} +1 -0
- package/dist/bridge.d.ts +15 -8
- package/dist/bridge.js +10 -8
- package/dist/{chunk-PDIHDBP3.js → chunk-7VJI2MXI.js} +116 -26
- package/dist/chunk-7VJI2MXI.js.map +1 -0
- package/dist/{chunk-4VB44KJU.js → chunk-AWE3DFZV.js} +3 -2
- package/dist/chunk-AWE3DFZV.js.map +1 -0
- package/dist/{chunk-IHZFI7TH.js → chunk-E4OWHGUE.js} +5 -2
- package/dist/chunk-E4OWHGUE.js.map +1 -0
- package/dist/{chunk-SYGONYMM.js → chunk-HUHG7XTB.js} +6 -5
- package/dist/chunk-HUHG7XTB.js.map +1 -0
- package/dist/{chunk-DQOZJJZ2.js → chunk-IVG4XWWS.js} +5 -5
- package/dist/{chunk-DQOZJJZ2.js.map → chunk-IVG4XWWS.js.map} +1 -1
- package/dist/{chunk-3YHVUPU2.js → chunk-P635FAN4.js} +4 -4
- package/dist/chunk-P635FAN4.js.map +1 -0
- package/dist/{chunk-6BTJE6D4.js → chunk-QNXHDOJ5.js} +56 -23
- package/dist/chunk-QNXHDOJ5.js.map +1 -0
- package/dist/{chunk-MCKKVNCE.js → chunk-QPC6JHA3.js} +5 -5
- package/dist/{chunk-SV3SVBYS.js → chunk-SN7PIVDF.js} +2 -2
- package/dist/{chunk-2DFNOLMO.js → chunk-XDNT6ZNZ.js} +9 -6
- package/dist/chunk-XDNT6ZNZ.js.map +1 -0
- package/dist/{chunk-LAHNQZDN.js → chunk-Z6JP6MYD.js} +2 -2
- package/dist/cli.js +9 -9
- package/dist/cli.js.map +1 -1
- package/dist/client-runtime.d.ts +3 -3
- package/dist/client-runtime.js +6 -6
- package/dist/contracts/generate.js +1 -1
- package/dist/dr.js +3 -3
- package/dist/{gateway-BQHpV-MZ.d.ts → gateway-CW-61lvs.d.ts} +2 -2
- package/dist/gateway-main.js +71 -7
- package/dist/gateway-main.js.map +1 -1
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +3 -3
- package/dist/index.js +7 -7
- package/dist/{postgres-bridge-store-WASumj26.d.ts → postgres-bridge-store-C9KG9Z69.d.ts} +1 -1
- package/dist/postgres-production-preflight.d.ts +32 -0
- package/dist/postgres-production-preflight.js +249 -0
- package/dist/postgres-production-preflight.js.map +1 -0
- package/dist/server.js +7 -7
- package/dist/{sqlite-edge-store-Bc15DvpX.d.ts → sqlite-edge-store-CdMIOrho.d.ts} +1 -1
- package/dist/sqlite.d.ts +3 -3
- package/dist/sqlite.js +2 -2
- package/docs/architecture-v2.md +36 -5
- package/docs/deployment.md +157 -0
- package/llms.txt +2 -1
- package/openapi/agent-bridge-v2.openapi.json +5 -1
- package/package.json +5 -1
- package/schemas/agent-bridge-v2.capabilities.json +2 -1
- package/schemas/agent-bridge-v2.mcp.json +5 -1
- package/schemas/agent-bridge-v2.schema.json +4 -0
- package/sql/migrations/007_runtime_role.sql +8 -4
- package/sql/migrations/013_row_isolation.sql +71 -8
- package/sql/migrations/014_owner_control_plane.sql +124 -31
- package/sql/migrations/015_portable_archives.sql +143 -41
- package/sql/migrations/016_native_dr_fence.sql +102 -35
- package/sql/migrations/018_endpoint_migration_challenges.sql +36 -10
- package/sql/migrations/019_endpoint_migration_same_successor.sql +34 -10
- package/sql/migrations/020_managed_authority_compat.sql +698 -0
- package/sql/migrations/021_native_backup_reader.sql +421 -0
- package/sql/setup.sql +1 -3
- package/dist/chunk-2DFNOLMO.js.map +0 -1
- package/dist/chunk-3YHVUPU2.js.map +0 -1
- package/dist/chunk-4VB44KJU.js.map +0 -1
- package/dist/chunk-6BTJE6D4.js.map +0 -1
- package/dist/chunk-IHZFI7TH.js.map +0 -1
- package/dist/chunk-PDIHDBP3.js.map +0 -1
- package/dist/chunk-SYGONYMM.js.map +0 -1
- /package/dist/{chunk-MCKKVNCE.js.map → chunk-QPC6JHA3.js.map} +0 -0
- /package/dist/{chunk-SV3SVBYS.js.map → chunk-SN7PIVDF.js.map} +0 -0
- /package/dist/{chunk-LAHNQZDN.js.map → chunk-Z6JP6MYD.js.map} +0 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
|
|
3
|
+
select pg_advisory_xact_lock(1646705660);
|
|
4
|
+
|
|
5
|
+
do $readiness$
|
|
6
|
+
begin
|
|
7
|
+
if not agent_bridge.security_schema_ready() then
|
|
8
|
+
raise exception 'managed authority compatibility security readiness validation failed';
|
|
9
|
+
elsif not agent_bridge.owner_control_plane_ready() then
|
|
10
|
+
raise exception 'managed authority compatibility owner readiness validation failed';
|
|
11
|
+
elsif not agent_bridge.gateway_authority_ready() then
|
|
12
|
+
raise exception 'managed authority compatibility gateway readiness validation failed';
|
|
13
|
+
elsif not agent_bridge.endpoint_migration_challenge_ready() then
|
|
14
|
+
raise exception 'managed authority compatibility endpoint readiness validation failed';
|
|
15
|
+
elsif not agent_bridge.portable_archive_ready() then
|
|
16
|
+
raise exception 'managed authority compatibility archive readiness validation failed';
|
|
17
|
+
end if;
|
|
18
|
+
end
|
|
19
|
+
$readiness$;
|
|
20
|
+
|
|
21
|
+
alter table agent_bridge.portable_archive_attestations
|
|
22
|
+
drop constraint portable_archive_attestation_name;
|
|
23
|
+
alter table agent_bridge.portable_archive_attestations
|
|
24
|
+
add constraint portable_archive_attestation_name
|
|
25
|
+
check(name ~ '^portable-archive-v[0-9]+$');
|
|
26
|
+
|
|
27
|
+
create or replace function agent_bridge.assert_control_actor(requested_capability text)
|
|
28
|
+
returns void language plpgsql security definer set search_path = '' as $$
|
|
29
|
+
declare
|
|
30
|
+
suffix text := substr(md5(current_database()),1,16);
|
|
31
|
+
schema_owner name;
|
|
32
|
+
operator_role name := ('agent_bridge_control_operator_'||suffix)::name;
|
|
33
|
+
auditor_role name := ('agent_bridge_control_auditor_'||suffix)::name;
|
|
34
|
+
operator_allowed boolean := false;
|
|
35
|
+
auditor_allowed boolean := false;
|
|
36
|
+
begin
|
|
37
|
+
select pg_catalog.pg_get_userbyid(namespace.nspowner)::name into schema_owner
|
|
38
|
+
from pg_catalog.pg_namespace namespace where namespace.nspname='agent_bridge';
|
|
39
|
+
if requested_capability is null or requested_capability not in ('operator','inventory') then
|
|
40
|
+
raise exception 'control capability is invalid';
|
|
41
|
+
end if;
|
|
42
|
+
|
|
43
|
+
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
44
|
+
'control-membership'||chr(31)||session_user||chr(31)||'global',1646705661
|
|
45
|
+
));
|
|
46
|
+
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
47
|
+
'control-membership'||chr(31)||session_user||chr(31)||'operator',1646705661
|
|
48
|
+
));
|
|
49
|
+
if requested_capability='inventory' then
|
|
50
|
+
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
51
|
+
'control-membership'||chr(31)||session_user||chr(31)||'auditor',1646705661
|
|
52
|
+
));
|
|
53
|
+
end if;
|
|
54
|
+
|
|
55
|
+
with latest as (
|
|
56
|
+
select distinct on (event.control_role) event.control_role,event.action
|
|
57
|
+
from agent_bridge.control_membership_events event
|
|
58
|
+
where event.member_role=session_user and event.control_role in ('operator','auditor')
|
|
59
|
+
order by event.control_role,event.sequence desc
|
|
60
|
+
), direct_membership as (
|
|
61
|
+
select granted.rolname control_role,
|
|
62
|
+
bool_or(membership.admin_option) admin_option,
|
|
63
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)) inherit_option,
|
|
64
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true)) set_option,
|
|
65
|
+
bool_and(case when member.rolname=schema_owner then
|
|
66
|
+
(membership.grantor=10 and membership.admin_option
|
|
67
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
68
|
+
=coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
69
|
+
or (membership.grantor=member.oid
|
|
70
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
71
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
72
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=schema_owner)
|
|
73
|
+
and not membership.admin_option
|
|
74
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
75
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true) end) grants_valid
|
|
76
|
+
from pg_catalog.pg_auth_members membership
|
|
77
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
78
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
79
|
+
where member.rolname=session_user and granted.rolname in (operator_role,auditor_role)
|
|
80
|
+
group by granted.rolname
|
|
81
|
+
)
|
|
82
|
+
select
|
|
83
|
+
coalesce(bool_or(membership.control_role=operator_role
|
|
84
|
+
and membership.inherit_option and membership.set_option and membership.grants_valid and (
|
|
85
|
+
(session_user=schema_owner and membership.admin_option)
|
|
86
|
+
or (latest.control_role='operator' and latest.action='register'
|
|
87
|
+
and not membership.admin_option)
|
|
88
|
+
)),false),
|
|
89
|
+
coalesce(bool_or(membership.control_role=auditor_role
|
|
90
|
+
and membership.inherit_option and membership.set_option and membership.grants_valid and (
|
|
91
|
+
(session_user=schema_owner and membership.admin_option)
|
|
92
|
+
or (latest.control_role='auditor' and latest.action='register'
|
|
93
|
+
and not membership.admin_option)
|
|
94
|
+
)),false)
|
|
95
|
+
into operator_allowed,auditor_allowed
|
|
96
|
+
from direct_membership membership left join latest on latest.control_role=case
|
|
97
|
+
membership.control_role when operator_role then 'operator' else 'auditor' end;
|
|
98
|
+
|
|
99
|
+
if session_user<>schema_owner and (exists (
|
|
100
|
+
select 1 from pg_catalog.pg_roles inherited
|
|
101
|
+
where inherited.rolname<>session_user
|
|
102
|
+
and pg_catalog.pg_has_role(session_user,inherited.rolname,'MEMBER')
|
|
103
|
+
and not (
|
|
104
|
+
(inherited.rolname=operator_role and operator_allowed)
|
|
105
|
+
or (inherited.rolname=auditor_role and auditor_allowed)
|
|
106
|
+
)
|
|
107
|
+
) or exists (
|
|
108
|
+
select 1 from pg_catalog.pg_roles candidate
|
|
109
|
+
where candidate.rolname not in (session_user,schema_owner)
|
|
110
|
+
and not candidate.rolsuper
|
|
111
|
+
and pg_catalog.pg_has_role(candidate.rolname,session_user,'MEMBER')
|
|
112
|
+
) or exists (
|
|
113
|
+
select 1 from pg_catalog.pg_roles actor
|
|
114
|
+
where actor.rolname=session_user and (
|
|
115
|
+
not actor.rolcanlogin or actor.rolsuper or actor.rolcreaterole or actor.rolcreatedb
|
|
116
|
+
or actor.rolreplication or actor.rolbypassrls
|
|
117
|
+
)
|
|
118
|
+
)) then
|
|
119
|
+
raise exception 'control actor has an unsafe membership graph';
|
|
120
|
+
end if;
|
|
121
|
+
|
|
122
|
+
if (requested_capability='operator' and not operator_allowed)
|
|
123
|
+
or (requested_capability='inventory' and not (operator_allowed or auditor_allowed)) then
|
|
124
|
+
raise exception 'control actor is not registered for requested capability';
|
|
125
|
+
end if;
|
|
126
|
+
end
|
|
127
|
+
$$;
|
|
128
|
+
|
|
129
|
+
create or replace function agent_bridge.assert_archive_actor()
|
|
130
|
+
returns void language plpgsql security definer set search_path = '' as $$
|
|
131
|
+
declare
|
|
132
|
+
suffix text := substr(md5(current_database()),1,16);
|
|
133
|
+
schema_owner name;
|
|
134
|
+
archive_role name := ('agent_bridge_archive_operator_'||suffix)::name;
|
|
135
|
+
archive_allowed boolean := false;
|
|
136
|
+
begin
|
|
137
|
+
if not agent_bridge.portable_archive_ready() then
|
|
138
|
+
raise exception 'portable archive readiness validation failed';
|
|
139
|
+
end if;
|
|
140
|
+
select pg_catalog.pg_get_userbyid(namespace.nspowner)::name into schema_owner
|
|
141
|
+
from pg_catalog.pg_namespace namespace where namespace.nspname='agent_bridge';
|
|
142
|
+
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
143
|
+
'archive-membership'||chr(31)||session_user||chr(31)||'global',1646705662
|
|
144
|
+
));
|
|
145
|
+
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
146
|
+
'archive-membership'||chr(31)||session_user||chr(31)||'operator',1646705662
|
|
147
|
+
));
|
|
148
|
+
|
|
149
|
+
with latest as (
|
|
150
|
+
select event.action from agent_bridge.archive_membership_events event
|
|
151
|
+
where event.member_role=session_user order by event.sequence desc limit 1
|
|
152
|
+
), direct_membership as (
|
|
153
|
+
select bool_or(membership.admin_option) admin_option,
|
|
154
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)) inherit_option,
|
|
155
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true)) set_option,
|
|
156
|
+
bool_and(case when member.rolname=schema_owner then
|
|
157
|
+
(membership.grantor=10 and membership.admin_option
|
|
158
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
159
|
+
=coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
160
|
+
or (membership.grantor=member.oid
|
|
161
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
162
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
163
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=schema_owner)
|
|
164
|
+
and not membership.admin_option
|
|
165
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
166
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true) end) grants_valid
|
|
167
|
+
from pg_catalog.pg_auth_members membership
|
|
168
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
169
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
170
|
+
where member.rolname=session_user and granted.rolname=archive_role
|
|
171
|
+
)
|
|
172
|
+
select coalesce(bool_or(
|
|
173
|
+
membership.inherit_option and membership.set_option and membership.grants_valid and (
|
|
174
|
+
(session_user=schema_owner and membership.admin_option)
|
|
175
|
+
or (latest.action='register' and not membership.admin_option)
|
|
176
|
+
)
|
|
177
|
+
),false) into archive_allowed
|
|
178
|
+
from direct_membership membership left join latest on true;
|
|
179
|
+
|
|
180
|
+
if session_user<>schema_owner and (exists (
|
|
181
|
+
select 1 from pg_catalog.pg_roles inherited
|
|
182
|
+
where inherited.rolname<>session_user
|
|
183
|
+
and pg_catalog.pg_has_role(session_user,inherited.rolname,'MEMBER')
|
|
184
|
+
and inherited.rolname<>archive_role
|
|
185
|
+
) or exists (
|
|
186
|
+
select 1 from pg_catalog.pg_roles candidate
|
|
187
|
+
where candidate.rolname not in (session_user,schema_owner)
|
|
188
|
+
and not candidate.rolsuper
|
|
189
|
+
and pg_catalog.pg_has_role(candidate.rolname,session_user,'MEMBER')
|
|
190
|
+
) or exists (
|
|
191
|
+
select 1 from pg_catalog.pg_roles actor where actor.rolname=session_user and (
|
|
192
|
+
not actor.rolcanlogin or actor.rolsuper or actor.rolcreaterole or actor.rolcreatedb
|
|
193
|
+
or actor.rolreplication or actor.rolbypassrls
|
|
194
|
+
)
|
|
195
|
+
)) then
|
|
196
|
+
raise exception 'archive actor has an unsafe membership graph';
|
|
197
|
+
end if;
|
|
198
|
+
if not archive_allowed then
|
|
199
|
+
raise exception 'archive actor is not registered';
|
|
200
|
+
end if;
|
|
201
|
+
end
|
|
202
|
+
$$;
|
|
203
|
+
|
|
204
|
+
do $ownership$
|
|
205
|
+
declare
|
|
206
|
+
owner_role text := 'agent_bridge_control_owner_'||substr(md5(current_database()),1,16);
|
|
207
|
+
begin
|
|
208
|
+
execute format('grant create on schema agent_bridge to %I',owner_role);
|
|
209
|
+
execute format(
|
|
210
|
+
'grant insert on agent_bridge.owner_control_attestations to %I',
|
|
211
|
+
owner_role
|
|
212
|
+
);
|
|
213
|
+
perform set_config('role',owner_role,false);
|
|
214
|
+
end
|
|
215
|
+
$ownership$;
|
|
216
|
+
|
|
217
|
+
create or replace function agent_bridge.owner_control_attestation_definition()
|
|
218
|
+
returns text language sql stable set search_path = '' as $$
|
|
219
|
+
with names as (select
|
|
220
|
+
('agent_bridge_control_owner_'||substr(md5(current_database()),1,16))::name owner_role,
|
|
221
|
+
('agent_bridge_control_operator_'||substr(md5(current_database()),1,16))::name operator_role,
|
|
222
|
+
('agent_bridge_control_auditor_'||substr(md5(current_database()),1,16))::name auditor_role,
|
|
223
|
+
(select namespace.nspowner from pg_catalog.pg_namespace namespace
|
|
224
|
+
where namespace.nspname='agent_bridge') schema_owner
|
|
225
|
+
), control_roles(role_name) as (
|
|
226
|
+
select owner_role from names union all select operator_role from names
|
|
227
|
+
union all select auditor_role from names
|
|
228
|
+
), raw_records(record) as (
|
|
229
|
+
select unnest(string_to_array(
|
|
230
|
+
agent_bridge.owner_control_catalog_definition(),E'\x1e'
|
|
231
|
+
))
|
|
232
|
+
), protected_relations(oid) as (
|
|
233
|
+
select distinct pg_catalog.to_regclass(split_part(record,E'\x1f',2))
|
|
234
|
+
from raw_records where split_part(record,E'\x1f',1) in ('relation','relation_acl')
|
|
235
|
+
union
|
|
236
|
+
select distinct relation.oid
|
|
237
|
+
from raw_records
|
|
238
|
+
join pg_catalog.pg_attribute attribute
|
|
239
|
+
on not attribute.attisdropped and attribute.attnum>0
|
|
240
|
+
join pg_catalog.pg_class relation on relation.oid=attribute.attrelid
|
|
241
|
+
and relation.oid::regclass::text||'.'||attribute.attname=
|
|
242
|
+
split_part(record,E'\x1f',2)
|
|
243
|
+
where split_part(record,E'\x1f',1)='column_acl'
|
|
244
|
+
), default_acl_keys(owner_oid,namespace_oid,object_type) as (
|
|
245
|
+
select role.oid,0::oid,object_type
|
|
246
|
+
from pg_catalog.pg_roles role
|
|
247
|
+
cross join (values ('r'::"char"),('S'::"char"),('f'::"char"),
|
|
248
|
+
('T'::"char"),('n'::"char")) object_types(object_type)
|
|
249
|
+
where role.oid in (
|
|
250
|
+
(select schema_owner from names),
|
|
251
|
+
(select oid from pg_catalog.pg_roles where rolname=(select owner_role from names))
|
|
252
|
+
)
|
|
253
|
+
union
|
|
254
|
+
select default_acl.defaclrole,default_acl.defaclnamespace,default_acl.defaclobjtype
|
|
255
|
+
from pg_catalog.pg_default_acl default_acl
|
|
256
|
+
left join pg_catalog.pg_namespace namespace on namespace.oid=default_acl.defaclnamespace
|
|
257
|
+
where (namespace.nspname='agent_bridge' or default_acl.defaclnamespace=0)
|
|
258
|
+
and default_acl.defaclrole in (
|
|
259
|
+
(select schema_owner from names),
|
|
260
|
+
(select oid from pg_catalog.pg_roles where rolname=(select owner_role from names))
|
|
261
|
+
)
|
|
262
|
+
), catalog_objects(kind,identity,definition) as (
|
|
263
|
+
select split_part(record,E'\x1f',1),split_part(record,E'\x1f',2),
|
|
264
|
+
split_part(record,E'\x1f',3)
|
|
265
|
+
from raw_records where split_part(record,E'\x1f',1) not in (
|
|
266
|
+
'membership','relation_acl','column_acl','default_acl'
|
|
267
|
+
)
|
|
268
|
+
union all
|
|
269
|
+
select 'membership',granted.rolname||'->'||member.rolname,
|
|
270
|
+
bool_or(membership.admin_option)::text||':'||
|
|
271
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true))::text||':'||
|
|
272
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true))::text
|
|
273
|
+
from pg_catalog.pg_auth_members membership
|
|
274
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
275
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
276
|
+
where member.rolname in (select role_name from control_roles)
|
|
277
|
+
or granted.rolname=(select owner_role from names)
|
|
278
|
+
group by granted.rolname,member.rolname
|
|
279
|
+
union all
|
|
280
|
+
select 'relation_acl',relation.oid::regclass::text,
|
|
281
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
282
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
283
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
284
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
285
|
+
from pg_catalog.pg_class relation
|
|
286
|
+
cross join lateral pg_catalog.aclexplode(coalesce(relation.relacl,
|
|
287
|
+
pg_catalog.acldefault(
|
|
288
|
+
case when relation.relkind='S' then 'S'::"char" else 'r'::"char" end,
|
|
289
|
+
relation.relowner
|
|
290
|
+
))) privilege
|
|
291
|
+
where relation.oid in (select oid from protected_relations)
|
|
292
|
+
and relation.relkind in ('r','p','v','m','f','S')
|
|
293
|
+
and privilege.grantee<>relation.relowner
|
|
294
|
+
union all
|
|
295
|
+
select 'column_acl',relation.oid::regclass::text||'.'||attribute.attname,
|
|
296
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
297
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
298
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
299
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
300
|
+
from pg_catalog.pg_attribute attribute
|
|
301
|
+
join pg_catalog.pg_class relation on relation.oid=attribute.attrelid
|
|
302
|
+
cross join lateral pg_catalog.aclexplode(attribute.attacl) privilege
|
|
303
|
+
where relation.oid in (select oid from protected_relations)
|
|
304
|
+
and attribute.attnum>0 and not attribute.attisdropped
|
|
305
|
+
and attribute.attacl is not null and privilege.grantee<>relation.relowner
|
|
306
|
+
union all
|
|
307
|
+
select 'default_acl',owner.rolname||':'||coalesce(namespace.nspname,'')||':'||
|
|
308
|
+
keys.object_type::text,
|
|
309
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
310
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
311
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
312
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
313
|
+
from default_acl_keys keys
|
|
314
|
+
join pg_catalog.pg_roles owner on owner.oid=keys.owner_oid
|
|
315
|
+
left join pg_catalog.pg_namespace namespace on namespace.oid=keys.namespace_oid
|
|
316
|
+
left join pg_catalog.pg_default_acl default_acl
|
|
317
|
+
on default_acl.defaclrole=keys.owner_oid
|
|
318
|
+
and default_acl.defaclnamespace=keys.namespace_oid
|
|
319
|
+
and default_acl.defaclobjtype=keys.object_type
|
|
320
|
+
cross join lateral pg_catalog.aclexplode(case when keys.namespace_oid=0 then
|
|
321
|
+
coalesce(default_acl.defaclacl,pg_catalog.acldefault(keys.object_type,keys.owner_oid))
|
|
322
|
+
else coalesce(default_acl.defaclacl,'{}'::pg_catalog.aclitem[]) end) privilege
|
|
323
|
+
where privilege.grantee<>keys.owner_oid
|
|
324
|
+
union all
|
|
325
|
+
select 'attestation_function',procedure.oid::regprocedure::text,
|
|
326
|
+
pg_catalog.pg_get_userbyid(procedure.proowner)||':'||
|
|
327
|
+
pg_catalog.pg_get_functiondef(procedure.oid)
|
|
328
|
+
from pg_catalog.pg_proc procedure
|
|
329
|
+
where procedure.oid=
|
|
330
|
+
'agent_bridge.owner_control_attestation_definition()'::regprocedure
|
|
331
|
+
)
|
|
332
|
+
select string_agg(kind||E'\x1f'||identity||E'\x1f'||definition,E'\x1e'
|
|
333
|
+
order by kind,identity,definition) from catalog_objects
|
|
334
|
+
$$;
|
|
335
|
+
|
|
336
|
+
reset role;
|
|
337
|
+
|
|
338
|
+
do $ownership$
|
|
339
|
+
declare
|
|
340
|
+
owner_role text := 'agent_bridge_control_owner_'||substr(md5(current_database()),1,16);
|
|
341
|
+
begin
|
|
342
|
+
execute format('revoke create on schema agent_bridge from %I',owner_role);
|
|
343
|
+
end
|
|
344
|
+
$ownership$;
|
|
345
|
+
|
|
346
|
+
create or replace function agent_bridge.portable_archive_attestation_definition()
|
|
347
|
+
returns text language sql stable set search_path = '' as $$
|
|
348
|
+
with names as (select
|
|
349
|
+
('agent_bridge_archive_operator_'||substr(md5(current_database()),1,16))::name archive_role,
|
|
350
|
+
namespace.nspowner schema_owner
|
|
351
|
+
from pg_catalog.pg_namespace namespace where namespace.nspname='agent_bridge'
|
|
352
|
+
), raw_records(record) as (
|
|
353
|
+
select unnest(string_to_array(
|
|
354
|
+
agent_bridge.portable_archive_catalog_definition(),E'\x1e'
|
|
355
|
+
))
|
|
356
|
+
), protected_relations(oid) as (
|
|
357
|
+
select distinct pg_catalog.to_regclass(split_part(record,E'\x1f',2))
|
|
358
|
+
from raw_records where split_part(record,E'\x1f',1)='relation'
|
|
359
|
+
), default_acl_keys(owner_oid,namespace_oid,object_type) as (
|
|
360
|
+
select (select schema_owner from names),0::oid,object_type
|
|
361
|
+
from (values ('r'::"char"),('S'::"char"),('f'::"char"),
|
|
362
|
+
('T'::"char"),('n'::"char")) object_types(object_type)
|
|
363
|
+
union
|
|
364
|
+
select default_acl.defaclrole,default_acl.defaclnamespace,default_acl.defaclobjtype
|
|
365
|
+
from pg_catalog.pg_default_acl default_acl
|
|
366
|
+
left join pg_catalog.pg_namespace namespace on namespace.oid=default_acl.defaclnamespace
|
|
367
|
+
where namespace.nspname='agent_bridge'
|
|
368
|
+
or default_acl.defaclrole=(select schema_owner from names)
|
|
369
|
+
), catalog_objects(kind,identity,definition) as (
|
|
370
|
+
select split_part(record,E'\x1f',1),split_part(record,E'\x1f',2),
|
|
371
|
+
split_part(record,E'\x1f',3)
|
|
372
|
+
from raw_records where split_part(record,E'\x1f',1) not in (
|
|
373
|
+
'owner_membership','relation_acl','column_acl','default_acl'
|
|
374
|
+
)
|
|
375
|
+
union all
|
|
376
|
+
select 'owner_membership',granted.rolname||'->'||member.rolname,
|
|
377
|
+
bool_or(membership.admin_option)::text||':'||
|
|
378
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true))::text||':'||
|
|
379
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true))::text
|
|
380
|
+
from pg_catalog.pg_auth_members membership
|
|
381
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
382
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
383
|
+
where granted.rolname=(select archive_role from names)
|
|
384
|
+
and member.oid=(select schema_owner from names)
|
|
385
|
+
group by granted.rolname,member.rolname
|
|
386
|
+
union all
|
|
387
|
+
select 'relation_acl',relation.oid::regclass::text,
|
|
388
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
389
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
390
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
391
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
392
|
+
from pg_catalog.pg_class relation
|
|
393
|
+
cross join lateral pg_catalog.aclexplode(coalesce(relation.relacl,
|
|
394
|
+
pg_catalog.acldefault(
|
|
395
|
+
case when relation.relkind='S' then 'S'::"char" else 'r'::"char" end,
|
|
396
|
+
relation.relowner
|
|
397
|
+
))) privilege
|
|
398
|
+
where relation.oid in (select oid from protected_relations)
|
|
399
|
+
and relation.relkind in ('r','p','v','m','f','S')
|
|
400
|
+
and privilege.grantee<>relation.relowner
|
|
401
|
+
union all
|
|
402
|
+
select 'column_acl',relation.oid::regclass::text||'.'||attribute.attname,
|
|
403
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
404
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
405
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
406
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
407
|
+
from pg_catalog.pg_attribute attribute
|
|
408
|
+
join pg_catalog.pg_class relation on relation.oid=attribute.attrelid
|
|
409
|
+
cross join lateral pg_catalog.aclexplode(attribute.attacl) privilege
|
|
410
|
+
where relation.oid in (select oid from protected_relations)
|
|
411
|
+
and attribute.attnum>0 and not attribute.attisdropped
|
|
412
|
+
and attribute.attacl is not null and privilege.grantee<>relation.relowner
|
|
413
|
+
union all
|
|
414
|
+
select 'default_acl',owner.rolname||':'||coalesce(namespace.nspname,'')||':'||
|
|
415
|
+
keys.object_type::text,
|
|
416
|
+
pg_catalog.pg_get_userbyid(privilege.grantor)||':'||
|
|
417
|
+
case when privilege.grantee=0 then 'PUBLIC'
|
|
418
|
+
else pg_catalog.pg_get_userbyid(privilege.grantee) end||':'||
|
|
419
|
+
privilege.privilege_type||':'||privilege.is_grantable::text
|
|
420
|
+
from default_acl_keys keys
|
|
421
|
+
join pg_catalog.pg_roles owner on owner.oid=keys.owner_oid
|
|
422
|
+
left join pg_catalog.pg_namespace namespace on namespace.oid=keys.namespace_oid
|
|
423
|
+
left join pg_catalog.pg_default_acl default_acl
|
|
424
|
+
on default_acl.defaclrole=keys.owner_oid
|
|
425
|
+
and default_acl.defaclnamespace=keys.namespace_oid
|
|
426
|
+
and default_acl.defaclobjtype=keys.object_type
|
|
427
|
+
cross join lateral pg_catalog.aclexplode(case when keys.namespace_oid=0 then
|
|
428
|
+
coalesce(default_acl.defaclacl,pg_catalog.acldefault(keys.object_type,keys.owner_oid))
|
|
429
|
+
else coalesce(default_acl.defaclacl,'{}'::pg_catalog.aclitem[]) end) privilege
|
|
430
|
+
where privilege.grantee<>keys.owner_oid
|
|
431
|
+
union all
|
|
432
|
+
select 'attestation_function',procedure.oid::regprocedure::text,
|
|
433
|
+
pg_catalog.pg_get_userbyid(procedure.proowner)||':'||
|
|
434
|
+
pg_catalog.pg_get_functiondef(procedure.oid)
|
|
435
|
+
from pg_catalog.pg_proc procedure
|
|
436
|
+
where procedure.oid=
|
|
437
|
+
'agent_bridge.portable_archive_attestation_definition()'::regprocedure
|
|
438
|
+
)
|
|
439
|
+
select string_agg(kind||E'\x1f'||identity||E'\x1f'||definition,E'\x1e'
|
|
440
|
+
order by kind,identity,definition) from catalog_objects
|
|
441
|
+
$$;
|
|
442
|
+
|
|
443
|
+
create or replace function agent_bridge.portable_archive_ready()
|
|
444
|
+
returns boolean language sql stable security definer set search_path = '' as $$
|
|
445
|
+
with names as (select
|
|
446
|
+
('agent_bridge_archive_operator_'||substr(md5(current_database()),1,16))::name archive_role,
|
|
447
|
+
(select pg_catalog.pg_get_userbyid(nspowner)::name from pg_catalog.pg_namespace
|
|
448
|
+
where nspname='agent_bridge') schema_owner
|
|
449
|
+
), latest_registry as (
|
|
450
|
+
select distinct on (event.member_role) event.member_role,event.action
|
|
451
|
+
from agent_bridge.archive_membership_events event order by event.member_role,event.sequence desc
|
|
452
|
+
), active_registry as (
|
|
453
|
+
select member_role from latest_registry where action='register'
|
|
454
|
+
), expected_memberships as (
|
|
455
|
+
select names.archive_role granted_role,names.schema_owner member_role,
|
|
456
|
+
true admin_option,true inherit_option,true set_option from names
|
|
457
|
+
union all select names.archive_role,registry.member_role,false,true,true
|
|
458
|
+
from active_registry registry cross join names
|
|
459
|
+
), raw_memberships as (
|
|
460
|
+
select granted.rolname::name granted_role,member.rolname::name member_role,
|
|
461
|
+
membership.admin_option,
|
|
462
|
+
coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
|
|
463
|
+
coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
|
|
464
|
+
membership.grantor,member.oid member_oid
|
|
465
|
+
from pg_catalog.pg_auth_members membership
|
|
466
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
467
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
468
|
+
where granted.rolname=(select archive_role from names)
|
|
469
|
+
), actual_memberships as (
|
|
470
|
+
select membership.granted_role,membership.member_role,
|
|
471
|
+
bool_or(membership.admin_option) admin_option,
|
|
472
|
+
bool_or(membership.inherit_option) inherit_option,
|
|
473
|
+
bool_or(membership.set_option) set_option,
|
|
474
|
+
bool_and(case when membership.member_role=names.schema_owner then
|
|
475
|
+
(membership.grantor=10 and membership.admin_option
|
|
476
|
+
and membership.inherit_option=membership.set_option)
|
|
477
|
+
or (membership.grantor=membership.member_oid
|
|
478
|
+
and membership.inherit_option and membership.set_option)
|
|
479
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
|
|
480
|
+
and not membership.admin_option
|
|
481
|
+
and membership.inherit_option and membership.set_option end) grants_valid
|
|
482
|
+
from raw_memberships membership cross join names
|
|
483
|
+
group by membership.granted_role,membership.member_role
|
|
484
|
+
)
|
|
485
|
+
select current_setting('server_version_num')::integer/10000=any(array[15,16,17,18])
|
|
486
|
+
and exists(select 1 from pg_catalog.pg_roles role,names where role.rolname=names.archive_role
|
|
487
|
+
and not role.rolcanlogin and not role.rolsuper and not role.rolcreatedb
|
|
488
|
+
and not role.rolcreaterole and not role.rolreplication and not role.rolbypassrls)
|
|
489
|
+
and (select count(*)=1 and bool_and(
|
|
490
|
+
attestation.catalog_definition=agent_bridge.portable_archive_attestation_definition()
|
|
491
|
+
) from agent_bridge.portable_archive_attestations attestation
|
|
492
|
+
where attestation.name='portable-archive-v4')
|
|
493
|
+
and not exists(
|
|
494
|
+
(select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
|
|
495
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
|
|
496
|
+
union all
|
|
497
|
+
(select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
|
|
498
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
|
|
499
|
+
)
|
|
500
|
+
and not exists(select 1 from actual_memberships membership
|
|
501
|
+
where not membership.grants_valid)
|
|
502
|
+
and not exists(select 1 from active_registry registry
|
|
503
|
+
left join pg_catalog.pg_roles role on role.rolname=registry.member_role
|
|
504
|
+
where role.oid is null or not role.rolcanlogin or role.rolsuper or role.rolcreaterole
|
|
505
|
+
or role.rolcreatedb or role.rolreplication or role.rolbypassrls)
|
|
506
|
+
and not exists(select 1 from active_registry registry
|
|
507
|
+
join pg_catalog.pg_roles inherited on inherited.rolname<>registry.member_role
|
|
508
|
+
cross join names
|
|
509
|
+
where pg_catalog.pg_has_role(registry.member_role,inherited.rolname,'MEMBER')
|
|
510
|
+
and inherited.rolname<>names.archive_role)
|
|
511
|
+
and not exists(select 1 from active_registry registry cross join pg_catalog.pg_roles candidate,names
|
|
512
|
+
where candidate.rolname not in (registry.member_role,names.schema_owner)
|
|
513
|
+
and not candidate.rolsuper
|
|
514
|
+
and pg_catalog.pg_has_role(candidate.rolname,registry.member_role,'MEMBER'))
|
|
515
|
+
and has_schema_privilege((select archive_role from names),'agent_bridge','USAGE')
|
|
516
|
+
and not has_schema_privilege((select archive_role from names),'agent_bridge','CREATE')
|
|
517
|
+
and not exists(select 1 from pg_catalog.pg_class relation
|
|
518
|
+
join pg_catalog.pg_namespace namespace on namespace.oid=relation.relnamespace
|
|
519
|
+
where namespace.nspname='agent_bridge' and relation.relkind in ('r','p','v','m','f')
|
|
520
|
+
and has_table_privilege((select archive_role from names),relation.oid,
|
|
521
|
+
'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER'))
|
|
522
|
+
and not exists(select 1 from pg_catalog.pg_class sequence_record
|
|
523
|
+
join pg_catalog.pg_namespace namespace on namespace.oid=sequence_record.relnamespace
|
|
524
|
+
where namespace.nspname='agent_bridge'
|
|
525
|
+
and case when sequence_record.relkind='S' then has_sequence_privilege(
|
|
526
|
+
(select archive_role from names),sequence_record.oid,'USAGE,SELECT,UPDATE'
|
|
527
|
+
) else false end)
|
|
528
|
+
and not exists(select 1 from pg_catalog.pg_proc procedure
|
|
529
|
+
join pg_catalog.pg_namespace namespace on namespace.oid=procedure.pronamespace
|
|
530
|
+
where namespace.nspname='agent_bridge' and has_function_privilege('public',procedure.oid,'EXECUTE'))
|
|
531
|
+
and not exists(select 1 from pg_catalog.pg_proc procedure
|
|
532
|
+
join pg_catalog.pg_namespace namespace on namespace.oid=procedure.pronamespace
|
|
533
|
+
where namespace.nspname='agent_bridge'
|
|
534
|
+
and has_function_privilege((select archive_role from names),procedure.oid,'EXECUTE')
|
|
535
|
+
and procedure.oid not in (
|
|
536
|
+
'agent_bridge.archive_begin_operation(uuid,text,text,character,bigint,bigint)'::regprocedure,
|
|
537
|
+
'agent_bridge.archive_authorize_transaction(uuid,text,text,character)'::regprocedure,
|
|
538
|
+
'agent_bridge.archive_close_transaction_authorization(uuid)'::regprocedure,
|
|
539
|
+
'agent_bridge.archive_export_messages(uuid,text,timestamp with time zone,uuid,integer)'::regprocedure,
|
|
540
|
+
'agent_bridge.archive_export_receipts(uuid,text,timestamp with time zone,uuid,text,integer)'::regprocedure,
|
|
541
|
+
'agent_bridge.archive_import_messages(uuid,text,character,bigint,jsonb)'::regprocedure,
|
|
542
|
+
'agent_bridge.archive_import_receipts(uuid,text,character,bigint,jsonb)'::regprocedure,
|
|
543
|
+
'agent_bridge.archive_complete_export(uuid,text,character,bigint,bigint,timestamp with time zone,text)'::regprocedure,
|
|
544
|
+
'agent_bridge.archive_reconcile_export(uuid,text,character,bigint,bigint,timestamp with time zone)'::regprocedure,
|
|
545
|
+
'agent_bridge.archive_complete_import(uuid,text,boolean)'::regprocedure,
|
|
546
|
+
'agent_bridge.archive_abandon_operation(uuid,text,text)'::regprocedure,
|
|
547
|
+
'agent_bridge.portable_archive_ready()'::regprocedure
|
|
548
|
+
))
|
|
549
|
+
and has_function_privilege((select archive_role from names),
|
|
550
|
+
'agent_bridge.archive_begin_operation(uuid,text,text,character,bigint,bigint)','EXECUTE')
|
|
551
|
+
and has_function_privilege((select archive_role from names),
|
|
552
|
+
'agent_bridge.archive_authorize_transaction(uuid,text,text,character)','EXECUTE')
|
|
553
|
+
and has_function_privilege((select archive_role from names),
|
|
554
|
+
'agent_bridge.archive_close_transaction_authorization(uuid)','EXECUTE')
|
|
555
|
+
and has_function_privilege((select archive_role from names),
|
|
556
|
+
'agent_bridge.archive_export_messages(uuid,text,timestamp with time zone,uuid,integer)','EXECUTE')
|
|
557
|
+
and has_function_privilege((select archive_role from names),
|
|
558
|
+
'agent_bridge.archive_export_receipts(uuid,text,timestamp with time zone,uuid,text,integer)','EXECUTE')
|
|
559
|
+
and has_function_privilege((select archive_role from names),
|
|
560
|
+
'agent_bridge.archive_import_messages(uuid,text,character,bigint,jsonb)','EXECUTE')
|
|
561
|
+
and has_function_privilege((select archive_role from names),
|
|
562
|
+
'agent_bridge.archive_import_receipts(uuid,text,character,bigint,jsonb)','EXECUTE')
|
|
563
|
+
and has_function_privilege((select archive_role from names),
|
|
564
|
+
'agent_bridge.archive_complete_export(uuid,text,character,bigint,bigint,timestamp with time zone,text)','EXECUTE')
|
|
565
|
+
and has_function_privilege((select archive_role from names),
|
|
566
|
+
'agent_bridge.archive_reconcile_export(uuid,text,character,bigint,bigint,timestamp with time zone)','EXECUTE')
|
|
567
|
+
and has_function_privilege((select archive_role from names),
|
|
568
|
+
'agent_bridge.archive_complete_import(uuid,text,boolean)','EXECUTE')
|
|
569
|
+
and has_function_privilege((select archive_role from names),
|
|
570
|
+
'agent_bridge.archive_abandon_operation(uuid,text,text)','EXECUTE')
|
|
571
|
+
and has_function_privilege((select archive_role from names),
|
|
572
|
+
'agent_bridge.portable_archive_ready()','EXECUTE')
|
|
573
|
+
$$;
|
|
574
|
+
|
|
575
|
+
create or replace function agent_bridge.owner_control_plane_ready()
|
|
576
|
+
returns boolean language sql stable security definer set search_path = '' as $$
|
|
577
|
+
with names as (select
|
|
578
|
+
('agent_bridge_control_owner_'||substr(md5(current_database()),1,16))::name owner_role,
|
|
579
|
+
('agent_bridge_control_operator_'||substr(md5(current_database()),1,16))::name operator_role,
|
|
580
|
+
('agent_bridge_control_auditor_'||substr(md5(current_database()),1,16))::name auditor_role,
|
|
581
|
+
(select pg_catalog.pg_get_userbyid(nspowner)::name from pg_catalog.pg_namespace where nspname='agent_bridge') schema_owner
|
|
582
|
+
), latest_registry as (
|
|
583
|
+
select distinct on (event.member_role,event.control_role) event.member_role,event.control_role,event.action
|
|
584
|
+
from agent_bridge.control_membership_events event
|
|
585
|
+
order by event.member_role,event.control_role,event.sequence desc
|
|
586
|
+
), active_registry as (
|
|
587
|
+
select member_role,control_role from latest_registry where action='register'
|
|
588
|
+
), expected_memberships as (
|
|
589
|
+
select names.owner_role granted_role,names.schema_owner member_role,
|
|
590
|
+
true admin_option,true inherit_option,true set_option from names
|
|
591
|
+
union all select names.operator_role,names.schema_owner,true,true,true from names
|
|
592
|
+
union all select names.auditor_role,names.schema_owner,true,true,true from names
|
|
593
|
+
union all select case registry.control_role when 'operator' then names.operator_role else names.auditor_role end,
|
|
594
|
+
registry.member_role,false,true,true from active_registry registry cross join names
|
|
595
|
+
), raw_memberships as (
|
|
596
|
+
select granted.rolname::name granted_role,member.rolname::name member_role,
|
|
597
|
+
membership.admin_option,
|
|
598
|
+
coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
|
|
599
|
+
coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
|
|
600
|
+
membership.grantor,member.oid member_oid
|
|
601
|
+
from pg_catalog.pg_auth_members membership
|
|
602
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
603
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
604
|
+
where granted.rolname in ((select owner_role from names),(select operator_role from names),(select auditor_role from names))
|
|
605
|
+
), actual_memberships as (
|
|
606
|
+
select membership.granted_role,membership.member_role,
|
|
607
|
+
bool_or(membership.admin_option) admin_option,
|
|
608
|
+
bool_or(membership.inherit_option) inherit_option,
|
|
609
|
+
bool_or(membership.set_option) set_option,
|
|
610
|
+
bool_and(case when membership.member_role=names.schema_owner then
|
|
611
|
+
(membership.grantor=10 and membership.admin_option
|
|
612
|
+
and membership.inherit_option=membership.set_option)
|
|
613
|
+
or (membership.grantor=membership.member_oid
|
|
614
|
+
and membership.inherit_option and membership.set_option)
|
|
615
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
|
|
616
|
+
and not membership.admin_option
|
|
617
|
+
and membership.inherit_option and membership.set_option end) grants_valid
|
|
618
|
+
from raw_memberships membership cross join names
|
|
619
|
+
group by membership.granted_role,membership.member_role
|
|
620
|
+
), authority_closure as (
|
|
621
|
+
select control_role.granted_role,candidate.rolname::name member_role
|
|
622
|
+
from names cross join lateral(values(names.owner_role),(names.operator_role),(names.auditor_role)) control_role(granted_role)
|
|
623
|
+
cross join pg_catalog.pg_roles candidate
|
|
624
|
+
where candidate.rolname not in(names.owner_role,names.operator_role,names.auditor_role)
|
|
625
|
+
and(not candidate.rolsuper or candidate.rolname=names.schema_owner)
|
|
626
|
+
and pg_catalog.pg_has_role(candidate.rolname,control_role.granted_role,'MEMBER')
|
|
627
|
+
) select
|
|
628
|
+
current_setting('server_version_num')::integer/10000=any(array[15,16,17,18])
|
|
629
|
+
and(select count(*)=1 and bool_and(catalog_definition=agent_bridge.owner_control_attestation_definition())
|
|
630
|
+
from agent_bridge.owner_control_attestations where name='owner-control-v7')
|
|
631
|
+
and not exists(
|
|
632
|
+
(select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
|
|
633
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
|
|
634
|
+
union all(select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
|
|
635
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
|
|
636
|
+
)
|
|
637
|
+
and not exists(select 1 from actual_memberships where not grants_valid)
|
|
638
|
+
and not exists(select 1 from active_registry registry left join pg_catalog.pg_roles role_record on role_record.rolname=registry.member_role
|
|
639
|
+
where role_record.oid is null or not role_record.rolcanlogin or role_record.rolsuper or role_record.rolcreaterole
|
|
640
|
+
or role_record.rolcreatedb or role_record.rolreplication or role_record.rolbypassrls)
|
|
641
|
+
and not exists(
|
|
642
|
+
(select granted_role,member_role from authority_closure except select granted_role,member_role from expected_memberships)
|
|
643
|
+
union all(select granted_role,member_role from expected_memberships except select granted_role,member_role from authority_closure)
|
|
644
|
+
)
|
|
645
|
+
and not exists(select 1 from active_registry registry join pg_catalog.pg_roles inherited on inherited.rolname<>registry.member_role
|
|
646
|
+
where pg_catalog.pg_has_role(registry.member_role,inherited.rolname,'MEMBER') and not exists(
|
|
647
|
+
select 1 from active_registry permitted cross join names where permitted.member_role=registry.member_role
|
|
648
|
+
and inherited.rolname=case permitted.control_role when 'operator' then names.operator_role else names.auditor_role end
|
|
649
|
+
))
|
|
650
|
+
and not exists(select 1 from active_registry registry cross join pg_catalog.pg_roles candidate,names
|
|
651
|
+
where candidate.rolname not in(registry.member_role,names.schema_owner) and not candidate.rolsuper
|
|
652
|
+
and pg_catalog.pg_has_role(candidate.rolname,registry.member_role,'MEMBER'))
|
|
653
|
+
$$;
|
|
654
|
+
|
|
655
|
+
select set_config(
|
|
656
|
+
'role','agent_bridge_control_owner_'||substr(md5(current_database()),1,16),false
|
|
657
|
+
);
|
|
658
|
+
do $owner_attestation$
|
|
659
|
+
begin
|
|
660
|
+
insert into agent_bridge.owner_control_attestations(name,catalog_definition)
|
|
661
|
+
values('owner-control-v7',agent_bridge.owner_control_attestation_definition());
|
|
662
|
+
exception when others then
|
|
663
|
+
raise exception 'managed authority compatibility owner attestation append failed: %',sqlerrm;
|
|
664
|
+
end
|
|
665
|
+
$owner_attestation$;
|
|
666
|
+
reset role;
|
|
667
|
+
|
|
668
|
+
do $archive_attestation$
|
|
669
|
+
begin
|
|
670
|
+
insert into agent_bridge.portable_archive_attestations(name,catalog_definition)
|
|
671
|
+
values('portable-archive-v4',agent_bridge.portable_archive_attestation_definition());
|
|
672
|
+
exception when others then
|
|
673
|
+
raise exception 'managed authority compatibility archive attestation append failed: %',sqlerrm;
|
|
674
|
+
end
|
|
675
|
+
$archive_attestation$;
|
|
676
|
+
|
|
677
|
+
do $final_readiness$
|
|
678
|
+
begin
|
|
679
|
+
if not agent_bridge.security_schema_ready() then
|
|
680
|
+
raise exception 'managed authority compatibility final security readiness validation failed';
|
|
681
|
+
elsif not agent_bridge.owner_control_plane_ready() then
|
|
682
|
+
raise exception 'managed authority compatibility final owner readiness validation failed';
|
|
683
|
+
elsif not agent_bridge.gateway_authority_ready() then
|
|
684
|
+
raise exception 'managed authority compatibility final gateway readiness validation failed';
|
|
685
|
+
elsif not agent_bridge.endpoint_migration_challenge_ready() then
|
|
686
|
+
raise exception 'managed authority compatibility final endpoint readiness validation failed';
|
|
687
|
+
elsif not agent_bridge.portable_archive_ready() then
|
|
688
|
+
raise exception 'managed authority compatibility final archive readiness validation failed';
|
|
689
|
+
end if;
|
|
690
|
+
exception when others then
|
|
691
|
+
raise exception 'managed authority compatibility final readiness execution failed: %',sqlerrm;
|
|
692
|
+
end
|
|
693
|
+
$final_readiness$;
|
|
694
|
+
|
|
695
|
+
insert into agent_bridge.schema_migrations(version,name,checksum)
|
|
696
|
+
values(20,'managed_authority_compat','__AGENT_BRIDGE_MIGRATION_CHECKSUM__');
|
|
697
|
+
|
|
698
|
+
commit;
|