fix(admin): allow administrator list reads
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
begin;
|
||||
|
||||
do $$
|
||||
begin
|
||||
if exists (select 1 from pg_roles where rolname = 'admin_runtime') then
|
||||
drop policy if exists admin_users_admin_read on public.admin_users;
|
||||
create policy admin_users_admin_read on public.admin_users
|
||||
for select to admin_runtime using (true);
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
commit;
|
||||
@@ -15,6 +15,10 @@ const authPolicy = readFileSync(new URL("../src/lib/admin/auth-policy.ts", impor
|
||||
const authFactory = readFileSync(new URL("../src/modules/identity/auth-factory.ts", import.meta.url), "utf8");
|
||||
const adminHttp = readFileSync(new URL("../src/lib/admin/http.ts", import.meta.url), "utf8");
|
||||
const rbacMigration = readFileSync(new URL("../supabase/migrations/20260806010000_admin_rbac.sql", import.meta.url), "utf8");
|
||||
const adminUsersReadPolicyMigration = readFileSync(
|
||||
new URL("../supabase/migrations/20260810010000_admin_users_admin_runtime_read_policy.sql", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const ownerRecoveryMigration = readFileSync(new URL("../db/migrations/20260807010000_recover_initial_admin_owner.sql", import.meta.url), "utf8");
|
||||
const bootstrapRoles = readFileSync(new URL("../../deploy/postgres/001-bootstrap-roles.sh", import.meta.url), "utf8");
|
||||
const compatibilityRoles = readFileSync(new URL("../../deploy/postgres/002-ensure-business-compatibility-roles.sql", import.meta.url), "utf8");
|
||||
@@ -281,6 +285,11 @@ test("admin runtime cannot assume service_role and keeps explicit RBAC grants",
|
||||
assert.match(rbacMigration, /public\.admin_read_customer_birth_data\(uuid, uuid\[\], text\)[\s\S]*to admin_runtime/);
|
||||
});
|
||||
|
||||
test("admin runtime can read administrators through RLS", () => {
|
||||
assert.match(adminUsersReadPolicyMigration, /admin_users_admin_read/);
|
||||
assert.match(adminUsersReadPolicyMigration, /for select to admin_runtime using \(true\)/);
|
||||
});
|
||||
|
||||
test("last Owner revocations are serialized by one transaction advisory lock", () => {
|
||||
assert.match(rbacMigration, /pg_advisory_xact_lock\(1096040772, 1\)[\s\S]*v_owner_count/);
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ const compatibilitySql = readFileSync(
|
||||
"utf8",
|
||||
);
|
||||
const unknownUserId = "00000000-0000-4000-8000-000000000001";
|
||||
const visibleAdminId = "00000000-0000-4000-8000-000000000002";
|
||||
|
||||
test("service and restricted admin database identities stay separated", async () => {
|
||||
const fixture = startPostgresFixture();
|
||||
@@ -74,6 +75,26 @@ test("service and restricted admin database identities stay separated", async ()
|
||||
assert.equal(fixture.psql("select pg_has_role('service_runtime','service_role','MEMBER')"), "t");
|
||||
assert.equal(fixture.psql("select pg_has_role('admin_runtime','service_role','MEMBER')"), "f");
|
||||
|
||||
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `
|
||||
insert into identity.users (id, name, email, email_verified, email_verified_at, role)
|
||||
values ('${visibleAdminId}', 'Visible Admin', 'visible-admin@example.com', true, now(), 'admin')
|
||||
`);
|
||||
fixture.psql(`
|
||||
insert into public.admin_users (user_id, created_by)
|
||||
values ('${visibleAdminId}', '${visibleAdminId}');
|
||||
insert into public.admin_user_roles (admin_user_id, role_id, assigned_by)
|
||||
select '${visibleAdminId}', id, '${visibleAdminId}' from public.admin_roles where code = 'support';
|
||||
`);
|
||||
assert.equal(
|
||||
fixture.psqlAs(
|
||||
"admin_runtime",
|
||||
"admin-runtime-test-password",
|
||||
`select count(*) from public.admin_users where user_id = '${visibleAdminId}' and revoked_at is null`,
|
||||
),
|
||||
"1",
|
||||
"admin list reads must not be hidden by admin_users RLS",
|
||||
);
|
||||
|
||||
const previousAuthProvider = process.env.AUTH_PROVIDER;
|
||||
const previousServiceDatabaseUrl = process.env.SERVICE_DATABASE_URL;
|
||||
const previousAdminDatabaseUrl = process.env.ADMIN_DATABASE_URL;
|
||||
|
||||
Reference in New Issue
Block a user