formatted SQL code

This commit is contained in:
Michael Hoennig 2022-07-29 08:46:04 +02:00
parent fb8862c37e
commit 4c403b0436
14 changed files with 1167 additions and 1116 deletions

View File

@ -1,22 +1,21 @@
create table Hostsharing
CREATE TABLE Hostsharing
( (
uuid uuid PRIMARY KEY REFERENCES RbacObject(uuid) uuid uuid primary key references RbacObject (uuid)
); );
CREATE UNIQUE INDEX Hostsharing_Singleton ON Hostsharing ((0)); create unique index Hostsharing_Singleton on Hostsharing ((0));
INSERT INTO RbacObject (objecttable) VALUES ('hostsharing'); insert
INSERT INTO Hostsharing (uuid) VALUES ((SELECT uuid FROM RbacObject WHERE objectTable='hostsharing')); into RbacObject (objecttable) values ('hostsharing');
insert
into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing'));
CREATE OR REPLACE FUNCTION hostsharingAdmin() create or replace function hostsharingAdmin()
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
STABLE LEAKPROOF stable leakproof
LANGUAGE sql AS $$ language sql as $$
SELECT 'global', (SELECT uuid FROM RbacObject WHERE objectTable='hostsharing'), 'admin'::RbacRoleType; select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType;
$$; $$;
-- create administrators role with two assigned users -- create administrators role with two assigned users
@ -32,7 +31,7 @@ do language plpgsql $$
$$; $$;
BEGIN TRANSACTION; begin transaction;
SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net'; set local hsadminng.currentUser = 'mike@hostsharing.net';
select * from RbacUser where uuid=currentUserId(); select * from RbacUser where uuid = currentUserId();
END TRANSACTION; end transaction;

View File

@ -15,11 +15,11 @@ create or replace function intToVarChar(i integer, len integer)
declare declare
partial varchar; partial varchar;
begin begin
select chr(ascii('a') + i%26) into partial; select chr(ascii('a') + i % 26) into partial;
if len > 1 then if len > 1 then
return intToVarChar(i/26, len-1) || partial; return intToVarChar(i / 26, len - 1) || partial;
else else
return partial; return partial;
end if; end if;
END; $$; end; $$;
--// --//

View File

@ -12,9 +12,9 @@
create or replace function randomInRange(min integer, max integer) create or replace function randomInRange(min integer, max integer)
returns integer returns integer
returns null on null input returns null on null input
language 'plpgsql' AS $$ language 'plpgsql' as $$
begin begin
return floor(random() * (max-min + 1) + min); return floor(random() * (max - min + 1) + min);
end; $$; end; $$;
--// --//

View File

@ -5,5 +5,5 @@
/* /*
Makes improved uuid generation available. Makes improved uuid generation available.
*/ */
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; create extension if not exists "uuid-ossp";
--// --//

File diff suppressed because it is too large Load Diff

View File

@ -9,17 +9,18 @@
*/ */
CREATE TYPE RbacPermissions AS create type RbacPermissions as
( (
permissionUuids uuid[] permissionUuids uuid[]
); );
CREATE OR REPLACE FUNCTION grantingPermissions(forObjectUuid uuid, permitOps RbacOp[]) create or replace function grantingPermissions(forObjectUuid uuid, permitOps RbacOp[])
RETURNS RbacPermissions returns RbacPermissions
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN ROW(createPermissions(forObjectUuid, permitOps))::RbacPermissions; begin
END; $$; return row (createPermissions(forObjectUuid, permitOps))::RbacPermissions;
end; $$;
--// --//
@ -28,45 +29,50 @@ END; $$;
/* /*
*/ */
CREATE TYPE RbacSuperRoles AS create type RbacSuperRoles as
( (
roleUuids uuid[] roleUuids uuid[]
); );
CREATE OR REPLACE FUNCTION beneathRoles(roleDescriptors RbacRoleDescriptor[]) create or replace function beneathRoles(roleDescriptors RbacRoleDescriptor[])
RETURNS RbacSuperRoles returns RbacSuperRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
declare
superRoleDescriptor RbacRoleDescriptor; superRoleDescriptor RbacRoleDescriptor;
superRoleUuids uuid[] := ARRAY[]::uuid[]; superRoleUuids uuid[] := array []::uuid[];
BEGIN begin
FOREACH superRoleDescriptor IN ARRAY roleDescriptors LOOP foreach superRoleDescriptor in array roleDescriptors
superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail'); loop
END LOOP; superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail');
end loop;
RETURN ROW(superRoleUuids)::RbacSuperRoles; return row (superRoleUuids)::RbacSuperRoles;
END; $$; end; $$;
CREATE OR REPLACE FUNCTION beneathRole(roleDescriptor RbacRoleDescriptor) create or replace function beneathRole(roleDescriptor RbacRoleDescriptor)
RETURNS RbacSuperRoles returns RbacSuperRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN beneathRoles(ARRAY[roleDescriptor]); begin
END; $$; return beneathRoles(array [roleDescriptor]);
end; $$;
CREATE OR REPLACE FUNCTION beneathRole(roleUuid uuid) create or replace function beneathRole(roleUuid uuid)
RETURNS RbacSuperRoles returns RbacSuperRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSuperRoles; begin
END; $$; return row (array [roleUuid]::uuid[])::RbacSuperRoles;
end; $$;
CREATE OR REPLACE FUNCTION asTopLevelRole() create or replace function asTopLevelRole()
RETURNS RbacSuperRoles returns RbacSuperRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN ROW(ARRAY[]::uuid[])::RbacSuperRoles; begin
END; $$; return row (array []::uuid[])::RbacSuperRoles;
end; $$;
--// --//
@ -78,26 +84,28 @@ END; $$;
/* /*
*/ */
CREATE TYPE RbacSubRoles AS create type RbacSubRoles as
( (
roleUuids uuid[] roleUuids uuid[]
); );
-- drop FUNCTION beingItselfA(roleUuid uuid) -- drop FUNCTION beingItselfA(roleUuid uuid)
CREATE OR REPLACE FUNCTION beingItselfA(roleUuid uuid) create or replace function beingItselfA(roleUuid uuid)
RETURNS RbacSubRoles returns RbacSubRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSubRoles; begin
END; $$; return row (array [roleUuid]::uuid[])::RbacSubRoles;
end; $$;
-- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) -- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor)
CREATE OR REPLACE FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) create or replace function beingItselfA(roleDescriptor RbacRoleDescriptor)
RETURNS RbacSubRoles returns RbacSubRoles
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
RETURN beingItselfA(getRoleId(roleDescriptor, 'fail')); begin
END; $$; return beingItselfA(getRoleId(roleDescriptor, 'fail'));
end; $$;
--// --//
@ -108,33 +116,35 @@ END; $$;
/* /*
*/ */
CREATE TYPE RbacUsers AS create type RbacUsers as
( (
userUuids uuid[] userUuids uuid[]
); );
CREATE OR REPLACE FUNCTION withUsers(userNames varchar[]) create or replace function withUsers(userNames varchar[])
RETURNS RbacUsers returns RbacUsers
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
userName varchar; declare
userUuids uuid[] := ARRAY[]::uuid[]; userName varchar;
BEGIN userUuids uuid[] := array []::uuid[];
FOREACH userName IN ARRAY userNames LOOP begin
userUuids := userUuids || getRbacUserId(userName, 'fail'); foreach userName in array userNames
END LOOP; loop
userUuids := userUuids || getRbacUserId(userName, 'fail');
end loop;
RETURN ROW(userUuids)::RbacUsers; return row (userUuids)::RbacUsers;
END; $$; end; $$;
CREATE OR REPLACE FUNCTION withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail') create or replace function withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail')
RETURNS RbacUsers returns RbacUsers
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
BEGIN begin
RETURN ROW(ARRAY[getRbacUserId(userName, whenNotExists )]); return row (array [getRbacUserId(userName, whenNotExists)]);
END; $$; end; $$;
--// --//
@ -145,72 +155,75 @@ END; $$;
/* /*
*/ */
CREATE OR REPLACE FUNCTION createRole( create or replace function createRole(
roleDescriptor RbacRoleDescriptor, roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions, permissions RbacPermissions,
superRoles RbacSuperRoles, superRoles RbacSuperRoles,
subRoles RbacSubRoles = null, subRoles RbacSubRoles = null,
users RbacUsers = null users RbacUsers = null
) )
RETURNS uuid returns uuid
CALLED ON NULL INPUT called on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
DECLARE declare
roleUuid uuid; roleUuid uuid;
superRoleUuid uuid; superRoleUuid uuid;
subRoleUuid uuid; subRoleUuid uuid;
userUuid uuid; userUuid uuid;
BEGIN begin
RAISE NOTICE 'will createRole for %', roleDescriptor; raise notice 'will createRole for %', roleDescriptor;
RAISE NOTICE 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype; raise notice 'will createRole for % % %', roleDescriptor.objecttable, roleDescriptor.objectuuid, roleDescriptor.roletype;
roleUuid = createRole(roleDescriptor); roleUuid = createRole(roleDescriptor);
call grantPermissionsToRole(roleUuid, permissions.permissionUuids); call grantPermissionsToRole(roleUuid, permissions.permissionUuids);
IF superRoles IS NOT NULL THEN if superRoles is not null then
FOREACH superRoleUuid IN ARRAY superRoles.roleuUids LOOP foreach superRoleUuid in array superRoles.roleuUids
call grantRoleToRole(roleUuid, superRoleUuid); loop
END LOOP; call grantRoleToRole(roleUuid, superRoleUuid);
END IF; end loop;
end if;
IF subRoles IS NOT NULL THEN if subRoles is not null then
FOREACH subRoleUuid IN ARRAY subRoles.roleuUids LOOP foreach subRoleUuid in array subRoles.roleuUids
call grantRoleToRole(subRoleUuid, roleUuid); loop
END LOOP; call grantRoleToRole(subRoleUuid, roleUuid);
END IF; end loop;
end if;
IF users IS NOT NULL THEN if users is not null then
FOREACH userUuid IN ARRAY users.useruUids LOOP foreach userUuid in array users.useruUids
call grantRoleToUser(roleUuid, userUuid); loop
END LOOP; call grantRoleToUser(roleUuid, userUuid);
END IF; end loop;
end if;
RETURN roleUuid; return roleUuid;
END; $$; end; $$;
CREATE OR REPLACE FUNCTION createRole( create or replace function createRole(
roleDescriptor RbacRoleDescriptor, roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions, permissions RbacPermissions,
users RbacUsers = null users RbacUsers = null
) )
RETURNS uuid returns uuid
CALLED ON NULL INPUT called on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
BEGIN begin
RETURN createRole(roleDescriptor, permissions, null, null, users); return createRole(roleDescriptor, permissions, null, null, users);
END; $$; end; $$;
CREATE OR REPLACE FUNCTION createRole( create or replace function createRole(
roleDescriptor RbacRoleDescriptor, roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions, permissions RbacPermissions,
subRoles RbacSubRoles, subRoles RbacSubRoles,
users RbacUsers = null users RbacUsers = null
) )
RETURNS uuid returns uuid
CALLED ON NULL INPUT called on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
BEGIN begin
RETURN createRole(roleDescriptor, permissions, null, subRoles, users); return createRole(roleDescriptor, permissions, null, subRoles, users);
END; $$; end; $$;
--// --//

View File

@ -5,20 +5,24 @@
/* /*
Creates a view which presents some statistics about the RBAC tables. Creates a view which presents some statistics about the RBAC tables.
*/ */
create view RbacStatisticsView AS create view RbacStatisticsView as
select no, to_char("count", '9 999 999 999') as "count", "table" select no, to_char("count", '9 999 999 999') as "count", "table"
from ( from (select 1 as no, count(*) as "count", 'login users' as "table"
select 1 as no, count(*) as "count", 'login users' as "table" from RbacUser from RbacUser
union union
select 2 as no, count(*) as "count", 'roles' as "table" from RbacRole select 2 as no, count(*) as "count", 'roles' as "table"
union from RbacRole
select 3 as no, count(*) as "count", 'permissions' as "table" from RbacPermission union
union select 3 as no, count(*) as "count", 'permissions' as "table"
select 4 as no, count(*) as "count", 'references' as "table" from RbacReference from RbacPermission
union union
select 5 as no, count(*) as "count", 'grants' as "table" from RbacGrants select 4 as no, count(*) as "count", 'references' as "table"
union from RbacReference
select 6 as no, count(*) as "count", 'objects' as "table" from RbacObject union
) as totals select 5 as no, count(*) as "count", 'grants' as "table"
from RbacGrants
union
select 6 as no, count(*) as "count", 'objects' as "table"
from RbacObject) as totals
order by totals.no; order by totals.no;
--// --//

View File

@ -0,0 +1,183 @@
-- ========================================================
-- Customer example with RBAC
-- --------------------------------------------------------
set session session authorization default;
create table if not exists customer
(
uuid uuid unique references RbacObject (uuid),
reference int not null unique check (reference between 10000 and 99999),
prefix character(3) unique,
adminUserName varchar(63)
);
drop trigger if exists createRbacObjectForCustomer_Trigger on customer;
create trigger createRbacObjectForCustomer_Trigger
before insert
on customer
for each row
execute procedure createRbacObject();
create or replace function customerOwner(customer customer)
returns RbacRoleDescriptor
language plpgsql
strict as $$
begin
return roleDescriptor('customer', customer.uuid, 'owner');
end; $$;
create or replace function customerAdmin(customer customer)
returns RbacRoleDescriptor
language plpgsql
strict as $$
begin
return roleDescriptor('customer', customer.uuid, 'admin');
end; $$;
create or replace function customerTenant(customer customer)
returns RbacRoleDescriptor
language plpgsql
strict as $$
begin
return roleDescriptor('customer', customer.uuid, 'tenant');
end; $$;
create or replace function createRbacRulesForCustomer()
returns trigger
language plpgsql
strict as $$
declare
customerOwnerUuid uuid;
customerAdminUuid uuid;
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if;
-- the owner role with full access for Hostsharing administrators
customerOwnerUuid = createRole(
customerOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(hostsharingAdmin())
);
-- the admin role for the customer's admins, who can view and add products
customerAdminUuid = createRole(
customerAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view', 'add-package']),
-- NO auto follow for customer owner to avoid exploding permissions for administrators
withUser(NEW.adminUserName, 'create') -- implicitly ignored if null
);
-- allow the customer owner role (thus administrators) to assume the customer admin role
call grantRoleToRole(customerAdminUuid, customerOwnerUuid, false);
-- the tenant role which later can be used by owners+admins of sub-objects
perform createRole(
customerTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view'])
);
return NEW;
end; $$;
drop trigger if exists createRbacRulesForCustomer_Trigger on customer;
create trigger createRbacRulesForCustomer_Trigger
after insert
on customer
for each row
execute procedure createRbacRulesForCustomer();
create or replace function deleteRbacRulesForCustomer()
returns trigger
language plpgsql
strict as $$
declare
objectTable varchar = 'customer';
begin
if TG_OP = 'DELETE' then
-- delete the owner role (for admininstrators)
call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.owner'));
-- delete the customer admin role
call deleteRole(findRoleId(objectTable || '#' || NEW.prefix || '.admin'));
else
raise exception 'invalid usage of TRIGGER BEFORE DELETE';
end if;
end; $$;
drop trigger if exists deleteRbacRulesForCustomer_Trigger on customer;
create trigger deleteRbacRulesForCustomer_Trigger
before delete
on customer
for each row
execute procedure deleteRbacRulesForCustomer();
-- create a restricted view to access the textual customer ids a idName
set session session authorization default;
-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
drop view if exists customer_iv;
create or replace view customer_iv as
select distinct target.uuid, target.prefix as idName
from customer as target;
-- TODO: Is it ok that everybody has access to this information?
grant all privileges on customer_iv to restricted;
create or replace function customerUuidByIdName(idName varchar)
returns uuid
language sql
strict as $$
select uuid from customer_iv iv where iv.idName = customerUuidByIdName.idName;
$$;
-- create RBAC restricted view
set session session authorization default;
-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
drop view if exists customer_rv;
create or replace view customer_rv as
select distinct target.*
from customer as target
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'customer', currentSubjectIds()));
grant all privileges on customer_rv to restricted;
-- generate Customer test data
set session session authorization default;
do language plpgsql $$
declare
currentTask varchar;
custReference integer;
custRowId uuid;
custPrefix varchar;
custAdminName varchar;
begin
set hsadminng.currentUser to '';
for t in 0..9
loop
currentTask = 'creating RBAC test customer #' || t;
set local hsadminng.currentUser to 'mike@hostsharing.net';
set local hsadminng.assumedRoles = '';
set local hsadminng.currentTask to currentTask;
-- When a new customer is created,
custReference = 10000 + t;
custRowId = uuid_generate_v4();
custPrefix = intToVarChar(t, 3);
custAdminName = 'admin@' || custPrefix || '.example.com';
raise notice 'creating customer %:%', custReference, custPrefix;
insert
into customer (reference, prefix, adminUserName)
values (custReference, custPrefix, custAdminName);
commit;
end loop;
end;
$$;

View File

@ -1,169 +0,0 @@
-- ========================================================
-- Customer example with RBAC
-- --------------------------------------------------------
SET SESSION SESSION AUTHORIZATION DEFAULT ;
CREATE TABLE IF NOT EXISTS customer (
uuid uuid UNIQUE REFERENCES RbacObject(uuid),
reference int not null unique CHECK (reference BETWEEN 10000 AND 99999),
prefix character(3) unique,
adminUserName varchar(63)
);
DROP TRIGGER IF EXISTS createRbacObjectForCustomer_Trigger ON customer;
CREATE TRIGGER createRbacObjectForCustomer_Trigger
BEFORE INSERT ON customer
FOR EACH ROW EXECUTE PROCEDURE createRbacObject();
CREATE OR REPLACE FUNCTION customerOwner(customer customer)
RETURNS RbacRoleDescriptor
LANGUAGE plpgsql STRICT AS $$
begin
return roleDescriptor('customer', customer.uuid, 'owner');
end; $$;
CREATE OR REPLACE FUNCTION customerAdmin(customer customer)
RETURNS RbacRoleDescriptor
LANGUAGE plpgsql STRICT AS $$
begin
return roleDescriptor('customer', customer.uuid, 'admin');
end; $$;
CREATE OR REPLACE FUNCTION customerTenant(customer customer)
RETURNS RbacRoleDescriptor
LANGUAGE plpgsql STRICT AS $$
begin
return roleDescriptor('customer', customer.uuid, 'tenant');
end; $$;
CREATE OR REPLACE FUNCTION createRbacRulesForCustomer()
RETURNS trigger
LANGUAGE plpgsql STRICT AS $$
DECLARE
customerOwnerUuid uuid;
customerAdminUuid uuid;
BEGIN
IF TG_OP <> 'INSERT' THEN
RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT';
END IF;
-- the owner role with full access for Hostsharing administrators
customerOwnerUuid = createRole(
customerOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']),
beneathRole(hostsharingAdmin())
);
-- the admin role for the customer's admins, who can view and add products
customerAdminUuid = createRole(
customerAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view', 'add-package']),
-- NO auto follow for customer owner to avoid exploding permissions for administrators
withUser(NEW.adminUserName, 'create') -- implicitly ignored if null
);
-- allow the customer owner role (thus administrators) to assume the customer admin role
call grantRoleToRole(customerAdminUuid, customerOwnerUuid, FALSE);
-- the tenant role which later can be used by owners+admins of sub-objects
perform createRole(
customerTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view'])
);
RETURN NEW;
END; $$;
DROP TRIGGER IF EXISTS createRbacRulesForCustomer_Trigger ON customer;
CREATE TRIGGER createRbacRulesForCustomer_Trigger
AFTER INSERT ON customer
FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForCustomer();
CREATE OR REPLACE FUNCTION deleteRbacRulesForCustomer()
RETURNS trigger
LANGUAGE plpgsql STRICT AS $$
DECLARE
objectTable varchar = 'customer';
BEGIN
IF TG_OP = 'DELETE' THEN
-- delete the owner role (for admininstrators)
call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.owner'));
-- delete the customer admin role
call deleteRole(findRoleId(objectTable||'#'||NEW.prefix||'.admin'));
ELSE
RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE';
END IF;
END; $$;
DROP TRIGGER IF EXISTS deleteRbacRulesForCustomer_Trigger ON customer;
CREATE TRIGGER deleteRbacRulesForCustomer_Trigger
BEFORE DELETE ON customer
FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForCustomer();
-- create a restricted view to access the textual customer ids a idName
SET SESSION SESSION AUTHORIZATION DEFAULT;
-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS customer_iv;
CREATE OR REPLACE VIEW customer_iv AS
SELECT DISTINCT target.uuid, target.prefix as idName
FROM customer AS target;
-- TODO: Is it ok that everybody has access to this information?
GRANT ALL PRIVILEGES ON customer_iv TO restricted;
CREATE OR REPLACE FUNCTION customerUuidByIdName(idName varchar)
RETURNS uuid
LANGUAGE sql STRICT AS $$
SELECT uuid FROM customer_iv iv WHERE iv.idName=customerUuidByIdName.idName;
$$;
-- create RBAC restricted view
SET SESSION SESSION AUTHORIZATION DEFAULT;
-- ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS customer_rv;
CREATE OR REPLACE VIEW customer_rv AS
SELECT DISTINCT target.*
FROM customer AS target
WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'customer', currentSubjectIds()));
GRANT ALL PRIVILEGES ON customer_rv TO restricted;
-- generate Customer test data
SET SESSION SESSION AUTHORIZATION DEFAULT;
DO LANGUAGE plpgsql $$
DECLARE
currentTask varchar;
custReference integer;
custRowId uuid;
custPrefix varchar;
custAdminName varchar;
BEGIN
SET hsadminng.currentUser TO '';
FOR t IN 0..9 LOOP
currentTask = 'creating RBAC test customer #' || t;
SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net';
SET LOCAL hsadminng.assumedRoles = '';
SET LOCAL hsadminng.currentTask TO currentTask;
-- When a new customer is created,
custReference = 10000 + t;
custRowId = uuid_generate_v4();
custPrefix = intToVarChar(t, 3 );
custAdminName = 'admin@' || custPrefix || '.example.com';
raise notice 'creating customer %:%', custReference, custPrefix;
insert into customer (reference, prefix, adminUserName)
VALUES (custReference, custPrefix, custAdminName);
COMMIT;
END LOOP;
END;
$$;

View File

@ -1,149 +1,161 @@
-- ======================================================== -- ========================================================
-- Package example with RBAC -- Package example with RBAC
-- -------------------------------------------------------- -- --------------------------------------------------------
SET SESSION SESSION AUTHORIZATION DEFAULT ; set session session authorization default;
CREATE TABLE IF NOT EXISTS package ( create table if not exists package
uuid uuid UNIQUE REFERENCES RbacObject(uuid), (
name character varying(5), uuid uuid unique references RbacObject (uuid),
customerUuid uuid REFERENCES customer(uuid) name character varying(5),
customerUuid uuid references customer (uuid)
); );
CREATE OR REPLACE FUNCTION packageOwner(pac package) create or replace function packageOwner(pac package)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
declare declare
roleDesc RbacRoleDescriptor; roleDesc RbacRoleDescriptor;
begin begin
return roleDescriptor('package', pac.uuid, 'admin'); return roleDescriptor('package', pac.uuid, 'admin');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION packageAdmin(pac package) create or replace function packageAdmin(pac package)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('package', pac.uuid, 'admin'); return roleDescriptor('package', pac.uuid, 'admin');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION packageTenant(pac package) create or replace function packageTenant(pac package)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('package', pac.uuid, 'tenant'); return roleDescriptor('package', pac.uuid, 'tenant');
end; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacObjectForPackage_Trigger ON package; drop trigger if exists createRbacObjectForPackage_Trigger on package;
CREATE TRIGGER createRbacObjectForPackage_Trigger create trigger createRbacObjectForPackage_Trigger
BEFORE INSERT ON package before insert
FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); on package
for each row
execute procedure createRbacObject();
CREATE OR REPLACE FUNCTION createRbacRulesForPackage() create or replace function createRbacRulesForPackage()
RETURNS trigger returns trigger
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
parentCustomer customer; declare
parentCustomer customer;
packageOwnerRoleUuid uuid; packageOwnerRoleUuid uuid;
packageAdminRoleUuid uuid; packageAdminRoleUuid uuid;
BEGIN begin
IF TG_OP <> 'INSERT' THEN if TG_OP <> 'INSERT' then
RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
END IF; end if;
SELECT * FROM customer AS c WHERE c.uuid=NEW.customerUuid INTO parentCustomer; select * from customer as c where c.uuid = NEW.customerUuid into parentCustomer;
-- an owner role is created and assigned to the customer's admin role -- an owner role is created and assigned to the customer's admin role
packageOwnerRoleUuid = createRole( packageOwnerRoleUuid = createRole(
packageOwner(NEW), packageOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(customerAdmin(parentCustomer)) beneathRole(customerAdmin(parentCustomer))
); );
-- an owner role is created and assigned to the package owner role -- an owner role is created and assigned to the package owner role
packageAdminRoleUuid = createRole( packageAdminRoleUuid = createRole(
packageAdmin(NEW), packageAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser', 'add-domain']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit', 'add-unixuser', 'add-domain']),
beneathRole(packageOwnerRoleUuid) beneathRole(packageOwnerRoleUuid)
); );
-- and a package tenant role is created and assigned to the package admin as well -- and a package tenant role is created and assigned to the package admin as well
perform createRole( perform createRole(
packageTenant(NEW), packageTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY ['view']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
beneathRole(packageAdminRoleUuid), beneathRole(packageAdminRoleUuid),
beingItselfA(customerTenant(parentCustomer)) beingItselfA(customerTenant(parentCustomer))
); );
RETURN NEW; return NEW;
END; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacRulesForPackage_Trigger ON package; drop trigger if exists createRbacRulesForPackage_Trigger on package;
CREATE TRIGGER createRbacRulesForPackage_Trigger create trigger createRbacRulesForPackage_Trigger
AFTER INSERT ON package after insert
FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForPackage(); on package
for each row
execute procedure createRbacRulesForPackage();
CREATE OR REPLACE FUNCTION deleteRbacRulesForPackage() create or replace function deleteRbacRulesForPackage()
RETURNS trigger returns trigger
LANGUAGE plpgsql STRICT AS $$ language plpgsql
BEGIN strict as $$
IF TG_OP = 'DELETE' THEN begin
if TG_OP = 'DELETE' then
-- TODO -- TODO
ELSE else
RAISE EXCEPTION 'invalid usage of TRIGGER BEFORE DELETE'; raise exception 'invalid usage of TRIGGER BEFORE DELETE';
END IF; end if;
END; $$; end; $$;
DROP TRIGGER IF EXISTS deleteRbacRulesForPackage_Trigger ON customer; drop trigger if exists deleteRbacRulesForPackage_Trigger on customer;
CREATE TRIGGER deleteRbacRulesForPackage_Trigger create trigger deleteRbacRulesForPackage_Trigger
BEFORE DELETE ON customer before delete
FOR EACH ROW EXECUTE PROCEDURE deleteRbacRulesForPackage(); on customer
for each row
execute procedure deleteRbacRulesForPackage();
-- create RBAC-restricted view -- create RBAC-restricted view
SET SESSION SESSION AUTHORIZATION DEFAULT; set session session authorization default;
-- ALTER TABLE package ENABLE ROW LEVEL SECURITY; -- ALTER TABLE package ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS package_rv; drop view if exists package_rv;
CREATE OR REPLACE VIEW package_rv AS create or replace view package_rv as
SELECT DISTINCT target.* select distinct target.*
FROM package AS target from package as target
WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'package', currentSubjectIds())); where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'package', currentSubjectIds()));
GRANT ALL PRIVILEGES ON package_rv TO restricted; grant all privileges on package_rv to restricted;
-- generate Package test data -- generate Package test data
DO LANGUAGE plpgsql $$ do language plpgsql $$
DECLARE declare
cust customer; cust customer;
pacName varchar; pacName varchar;
currentTask varchar; currentTask varchar;
custAdmin varchar; custAdmin varchar;
BEGIN begin
SET hsadminng.currentUser TO ''; set hsadminng.currentUser to '';
FOR cust IN (SELECT * FROM customer) LOOP for cust in (select * from customer)
-- CONTINUE WHEN cust.reference < 18000; loop
-- CONTINUE WHEN cust.reference < 18000;
FOR t IN 0..randominrange(1, 2) LOOP for t in 0..randominrange(1, 2)
pacName = cust.prefix || TO_CHAR(t, 'fm00'); loop
currentTask = 'creating RBAC test package #'|| pacName || ' for customer ' || cust.prefix || ' #' || cust.uuid; pacName = cust.prefix || to_char(t, 'fm00');
RAISE NOTICE 'task: %', currentTask; currentTask = 'creating RBAC test package #' || pacName || ' for customer ' || cust.prefix || ' #' ||
cust.uuid;
raise notice 'task: %', currentTask;
custAdmin = 'admin@' || cust.prefix || '.example.com'; custAdmin = 'admin@' || cust.prefix || '.example.com';
SET LOCAL hsadminng.currentUser TO custAdmin; set local hsadminng.currentUser to custAdmin;
SET LOCAL hsadminng.assumedRoles = ''; set local hsadminng.assumedRoles = '';
SET LOCAL hsadminng.currentTask TO currentTask; set local hsadminng.currentTask to currentTask;
insert into package (name, customerUuid) insert
VALUES (pacName, cust.uuid); into package (name, customerUuid)
values (pacName, cust.uuid);
COMMIT; commit;
END LOOP; end loop;
END LOOP; end loop;
END; end;
$$; $$;

View File

@ -1,152 +1,159 @@
-- ======================================================== -- ========================================================
-- UnixUser example with RBAC -- UnixUser example with RBAC
-- -------------------------------------------------------- -- --------------------------------------------------------
SET SESSION SESSION AUTHORIZATION DEFAULT ; set session session authorization default;
CREATE TABLE IF NOT EXISTS UnixUser ( create table if not exists UnixUser
uuid uuid UNIQUE REFERENCES RbacObject(uuid), (
name character varying(32), uuid uuid unique references RbacObject (uuid),
comment character varying(96), name character varying(32),
packageUuid uuid REFERENCES package(uuid) comment character varying(96),
packageUuid uuid references package (uuid)
); );
CREATE OR REPLACE FUNCTION unixUserOwner(uu UnixUser) create or replace function unixUserOwner(uu UnixUser)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('unixuser', uu.uuid, 'owner'); return roleDescriptor('unixuser', uu.uuid, 'owner');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION unixUserAdmin(uu UnixUser) create or replace function unixUserAdmin(uu UnixUser)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('unixuser', uu.uuid, 'admin'); return roleDescriptor('unixuser', uu.uuid, 'admin');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION unixUserTenant(uu UnixUser) create or replace function unixUserTenant(uu UnixUser)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('unixuser', uu.uuid, 'tenant'); return roleDescriptor('unixuser', uu.uuid, 'tenant');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser) create or replace function createUnixUserTenantRoleIfNotExists(unixUser UnixUser)
RETURNS uuid returns uuid
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
DECLARE declare
unixUserTenantRoleDesc RbacRoleDescriptor; unixUserTenantRoleDesc RbacRoleDescriptor;
unixUserTenantRoleUuid uuid; unixUserTenantRoleUuid uuid;
BEGIN begin
unixUserTenantRoleDesc = unixUserTenant(unixUser); unixUserTenantRoleDesc = unixUserTenant(unixUser);
unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc); unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc);
IF unixUserTenantRoleUuid IS NOT NULL THEN if unixUserTenantRoleUuid is not null then
RETURN unixUserTenantRoleUuid; return unixUserTenantRoleUuid;
END IF; end if;
RETURN createRole( return createRole(
unixUserTenantRoleDesc, unixUserTenantRoleDesc,
grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['view']), grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => array ['view']),
beneathRole(unixUserAdmin(unixUser)) beneathRole(unixUserAdmin(unixUser))
); );
END; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacObjectForUnixUser_Trigger ON UnixUser; drop trigger if exists createRbacObjectForUnixUser_Trigger on UnixUser;
CREATE TRIGGER createRbacObjectForUnixUser_Trigger create trigger createRbacObjectForUnixUser_Trigger
BEFORE INSERT ON UnixUser before insert
FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); on UnixUser
for each row
execute procedure createRbacObject();
CREATE OR REPLACE FUNCTION createRbacRulesForUnixUser() create or replace function createRbacRulesForUnixUser()
RETURNS trigger returns trigger
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
parentPackage package; declare
parentPackage package;
unixuserOwnerRoleId uuid; unixuserOwnerRoleId uuid;
unixuserAdminRoleId uuid; unixuserAdminRoleId uuid;
BEGIN begin
IF TG_OP <> 'INSERT' THEN if TG_OP <> 'INSERT' then
RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
END IF; end if;
SELECT * FROM package WHERE uuid=NEW.packageUuid into parentPackage; select * from package where uuid = NEW.packageUuid into parentPackage;
-- an owner role is created and assigned to the package's admin group -- an owner role is created and assigned to the package's admin group
unixuserOwnerRoleId = createRole( unixuserOwnerRoleId = createRole(
unixUserOwner(NEW), unixUserOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(packageAdmin(parentPackage)) beneathRole(packageAdmin(parentPackage))
); );
-- and a unixuser admin role is created and assigned to the unixuser owner as well -- and a unixuser admin role is created and assigned to the unixuser owner as well
unixuserAdminRoleId = createRole( unixuserAdminRoleId = createRole(
unixUserAdmin(NEW), unixUserAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
beneathRole(unixuserOwnerRoleId), beneathRole(unixuserOwnerRoleId),
beingItselfA(packageTenant(parentPackage)) beingItselfA(packageTenant(parentPackage))
); );
-- a tenent role is only created on demand -- a tenent role is only created on demand
RETURN NEW; return NEW;
END; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacRulesForUnixUser_Trigger ON UnixUser; drop trigger if exists createRbacRulesForUnixUser_Trigger on UnixUser;
CREATE TRIGGER createRbacRulesForUnixUser_Trigger create trigger createRbacRulesForUnixUser_Trigger
AFTER INSERT ON UnixUser after insert
FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForUnixUser(); on UnixUser
for each row
execute procedure createRbacRulesForUnixUser();
-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser() -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser()
-- create RBAC-restricted view -- create RBAC-restricted view
SET SESSION SESSION AUTHORIZATION DEFAULT; set session session authorization default;
-- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY; -- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS unixuser_rv; drop view if exists unixuser_rv;
CREATE OR REPLACE VIEW unixuser_rv AS create or replace view unixuser_rv as
SELECT DISTINCT target.* select distinct target.*
FROM unixuser AS target from unixuser as target
WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'unixuser', currentSubjectIds())); where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'unixuser', currentSubjectIds()));
GRANT ALL PRIVILEGES ON unixuser_rv TO restricted; grant all privileges on unixuser_rv to restricted;
-- generate UnixUser test data -- generate UnixUser test data
DO LANGUAGE plpgsql $$ do language plpgsql $$
DECLARE declare
pac record; pac record;
pacAdmin varchar; pacAdmin varchar;
currentTask varchar; currentTask varchar;
BEGIN begin
SET hsadminng.currentUser TO ''; set hsadminng.currentUser to '';
FOR pac IN ( for pac in (select p.uuid, p.name
SELECT p.uuid, p.name from package p
FROM package p join customer c on p.customeruuid = c.uuid
JOIN customer c ON p.customeruuid = c.uuid -- WHERE c.reference >= 18000
-- WHERE c.reference >= 18000 )
) LOOP loop
FOR t IN 0..9 LOOP for t in 0..9
currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name|| ' #' || pac.uuid; loop
RAISE NOTICE 'task: %', currentTask; currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name || ' #' || pac.uuid;
pacAdmin = 'admin@' || pac.name || '.example.com'; raise notice 'task: %', currentTask;
SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; -- TODO: use a package-admin pacAdmin = 'admin@' || pac.name || '.example.com';
SET LOCAL hsadminng.assumedRoles = ''; set local hsadminng.currentUser to 'mike@hostsharing.net'; -- TODO: use a package-admin
SET LOCAL hsadminng.currentTask TO currentTask; set local hsadminng.assumedRoles = '';
set local hsadminng.currentTask to currentTask;
INSERT INTO unixuser (name, packageUuid) insert
VALUES (pac.name||'-'|| intToVarChar(t, 4), pac.uuid); into unixuser (name, packageUuid)
values (pac.name || '-' || intToVarChar(t, 4), pac.uuid);
COMMIT; commit;
END LOOP; end loop;
END LOOP; end loop;
END; end;
$$; $$;

View File

@ -1,144 +1,151 @@
-- ======================================================== -- ========================================================
-- Domain example with RBAC -- Domain example with RBAC
-- -------------------------------------------------------- -- --------------------------------------------------------
SET SESSION SESSION AUTHORIZATION DEFAULT ; set session session authorization default;
CREATE TABLE IF NOT EXISTS Domain ( create table if not exists Domain
uuid uuid UNIQUE REFERENCES RbacObject(uuid), (
name character varying(32), uuid uuid unique references RbacObject (uuid),
unixUserUuid uuid REFERENCES unixuser(uuid) name character varying(32),
unixUserUuid uuid references unixuser (uuid)
); );
DROP TRIGGER IF EXISTS createRbacObjectForDomain_Trigger ON Domain; drop trigger if exists createRbacObjectForDomain_Trigger on Domain;
CREATE TRIGGER createRbacObjectForDomain_Trigger create trigger createRbacObjectForDomain_Trigger
BEFORE INSERT ON Domain before insert
FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); on Domain
for each row
execute procedure createRbacObject();
CREATE OR REPLACE FUNCTION domainOwner(dom Domain) create or replace function domainOwner(dom Domain)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('domain', dom.uuid, 'owner'); return roleDescriptor('domain', dom.uuid, 'owner');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION domainAdmin(dom Domain) create or replace function domainAdmin(dom Domain)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('domain', dom.uuid, 'admin'); return roleDescriptor('domain', dom.uuid, 'admin');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION domainTenant(dom Domain) create or replace function domainTenant(dom Domain)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('domain', dom.uuid, 'tenant'); return roleDescriptor('domain', dom.uuid, 'tenant');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION createRbacRulesForDomain() create or replace function createRbacRulesForDomain()
RETURNS trigger returns trigger
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
parentUser UnixUser; declare
parentPackage package; parentUser UnixUser;
parentPackage package;
domainOwnerRoleUuid uuid; domainOwnerRoleUuid uuid;
domainAdminRoleUuid uuid; domainAdminRoleUuid uuid;
BEGIN begin
IF TG_OP <> 'INSERT' THEN if TG_OP <> 'INSERT' then
RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
END IF; end if;
SELECT * FROM UnixUser WHERE uuid=NEW.unixUserUuid into parentUser; select * from UnixUser where uuid = NEW.unixUserUuid into parentUser;
SELECT * FROM Package WHERE uuid=parentUser.packageuuid into parentPackage; select * from Package where uuid = parentUser.packageuuid into parentPackage;
-- a domain owner role is created and assigned to the unixuser's admin role -- a domain owner role is created and assigned to the unixuser's admin role
domainOwnerRoleUuid = createRole( domainOwnerRoleUuid = createRole(
domainOwner(NEW), domainOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(packageAdmin(parentPackage)) beneathRole(packageAdmin(parentPackage))
); );
-- a domain admin role is created and assigned to the domain's owner role -- a domain admin role is created and assigned to the domain's owner role
domainAdminRoleUuid = createRole( domainAdminRoleUuid = createRole(
domainAdmin(NEW), domainAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-emailaddress']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit', 'add-emailaddress']),
beneathRole(domainOwnerRoleUuid) beneathRole(domainOwnerRoleUuid)
); );
-- and a domain tenant role is created and assigned to the domain's admiin role -- and a domain tenant role is created and assigned to the domain's admiin role
perform createRole( perform createRole(
domainTenant(NEW), domainTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(domainAdminRoleUuid), beneathRole(domainAdminRoleUuid),
beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser)) beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser))
); );
RETURN NEW; return NEW;
END; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacRulesForDomain_Trigger ON Domain; drop trigger if exists createRbacRulesForDomain_Trigger on Domain;
CREATE TRIGGER createRbacRulesForDomain_Trigger create trigger createRbacRulesForDomain_Trigger
AFTER INSERT ON Domain after insert
FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForDomain(); on Domain
for each row
execute procedure createRbacRulesForDomain();
-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForDomain() -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForDomain()
-- create RBAC-restricted view -- create RBAC-restricted view
SET SESSION SESSION AUTHORIZATION DEFAULT; set session session authorization default;
-- ALTER TABLE Domain ENABLE ROW LEVEL SECURITY; -- ALTER TABLE Domain ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS domain_rv; drop view if exists domain_rv;
CREATE OR REPLACE VIEW domain_rv AS create or replace view domain_rv as
SELECT DISTINCT target.* select distinct target.*
FROM Domain AS target from Domain as target
WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'domain', currentSubjectIds())); where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectIds()));
GRANT ALL PRIVILEGES ON domain_rv TO restricted; grant all privileges on domain_rv to restricted;
-- generate Domain test data -- generate Domain test data
DO LANGUAGE plpgsql $$ do language plpgsql $$
DECLARE declare
uu record; uu record;
pac package; pac package;
pacAdmin varchar; pacAdmin varchar;
currentTask varchar; currentTask varchar;
BEGIN begin
SET hsadminng.currentUser TO ''; set hsadminng.currentUser to '';
FOR uu IN ( for uu in (select u.uuid, u.name, u.packageuuid, c.reference
SELECT u.uuid, u.name, u.packageuuid, c.reference from unixuser u
FROM unixuser u join package p on u.packageuuid = p.uuid
JOIN package p ON u.packageuuid = p.uuid join customer c on p.customeruuid = c.uuid
JOIN customer c ON p.customeruuid = c.uuid -- WHERE c.reference >= 18000
-- WHERE c.reference >= 18000 )
) LOOP loop
IF ( random() < 0.3 ) THEN if (random() < 0.3) then
FOR t IN 0..1 LOOP for t in 0..1
currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name|| ' #' || uu.uuid; loop
RAISE NOTICE 'task: %', currentTask; currentTask = 'creating RBAC test Domain #' || t || ' for UnixUser ' || uu.name || ' #' || uu.uuid;
raise notice 'task: %', currentTask;
SELECT * FROM package WHERE uuid=uu.packageUuid INTO pac; select * from package where uuid = uu.packageUuid into pac;
pacAdmin = 'admin@' || pac.name || '.example.com'; pacAdmin = 'admin@' || pac.name || '.example.com';
SET LOCAL hsadminng.currentUser TO pacAdmin; set local hsadminng.currentUser to pacAdmin;
SET LOCAL hsadminng.assumedRoles = ''; set local hsadminng.assumedRoles = '';
SET LOCAL hsadminng.currentTask TO currentTask; set local hsadminng.currentTask to currentTask;
INSERT INTO Domain (name, unixUserUuid) insert
VALUES ('dom-' || t || '.' || uu.name || '.example.org' , uu.uuid); into Domain (name, unixUserUuid)
values ('dom-' || t || '.' || uu.name || '.example.org', uu.uuid);
COMMIT; commit;
END LOOP; end loop;
END IF; end if;
END LOOP; end loop;
END; end;
$$; $$;

View File

@ -1,123 +1,131 @@
-- ======================================================== -- ========================================================
-- EMailAddress example with RBAC -- EMailAddress example with RBAC
-- -------------------------------------------------------- -- --------------------------------------------------------
SET SESSION SESSION AUTHORIZATION DEFAULT ; set session session authorization default;
CREATE TABLE IF NOT EXISTS EMailAddress ( create table if not exists EMailAddress
uuid uuid UNIQUE REFERENCES RbacObject(uuid), (
localPart character varying(64), uuid uuid unique references RbacObject (uuid),
domainUuid uuid REFERENCES domain(uuid) localPart character varying(64),
domainUuid uuid references domain (uuid)
); );
DROP TRIGGER IF EXISTS createRbacObjectForEMailAddress_Trigger ON EMailAddress; drop trigger if exists createRbacObjectForEMailAddress_Trigger on EMailAddress;
CREATE TRIGGER createRbacObjectForEMailAddress_Trigger create trigger createRbacObjectForEMailAddress_Trigger
BEFORE INSERT ON EMailAddress before insert
FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); on EMailAddress
for each row
execute procedure createRbacObject();
CREATE OR REPLACE FUNCTION emailAddressOwner(emAddr EMailAddress) create or replace function emailAddressOwner(emAddr EMailAddress)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('emailaddress', emAddr.uuid, 'owner'); return roleDescriptor('emailaddress', emAddr.uuid, 'owner');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION emailAddressAdmin(emAddr EMailAddress) create or replace function emailAddressAdmin(emAddr EMailAddress)
RETURNS RbacRoleDescriptor returns RbacRoleDescriptor
RETURNS NULL ON NULL INPUT returns null on null input
LANGUAGE plpgsql AS $$ language plpgsql as $$
begin begin
return roleDescriptor('emailaddress', emAddr.uuid, 'admin'); return roleDescriptor('emailaddress', emAddr.uuid, 'admin');
end; $$; end; $$;
CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() create or replace function createRbacRulesForEMailAddress()
RETURNS trigger returns trigger
LANGUAGE plpgsql STRICT AS $$ language plpgsql
DECLARE strict as $$
declare
parentDomain Domain; parentDomain Domain;
eMailAddressOwnerRoleUuid uuid; eMailAddressOwnerRoleUuid uuid;
BEGIN begin
IF TG_OP <> 'INSERT' THEN if TG_OP <> 'INSERT' then
RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
END IF; end if;
SELECT d.* select d.*
FROM domain d from domain d
LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid left join unixuser u on u.uuid = d.unixuseruuid
WHERE d.uuid=NEW.domainUuid INTO parentDomain; where d.uuid = NEW.domainUuid
into parentDomain;
-- an owner role is created and assigned to the domains's admin group -- an owner role is created and assigned to the domains's admin group
eMailAddressOwnerRoleUuid = createRole( eMailAddressOwnerRoleUuid = createRole(
emailAddressOwner(NEW), emailAddressOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(domainAdmin( parentDomain)) beneathRole(domainAdmin(parentDomain))
); );
-- and an admin role is created and assigned to the unixuser owner as well -- and an admin role is created and assigned to the unixuser owner as well
perform createRole( perform createRole(
emailAddressAdmin(NEW), emailAddressAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
beneathRole(eMailAddressOwnerRoleUuid), beneathRole(eMailAddressOwnerRoleUuid),
beingItselfA(domainTenant(parentDomain)) beingItselfA(domainTenant(parentDomain))
); );
RETURN NEW; return NEW;
END; $$; end; $$;
DROP TRIGGER IF EXISTS createRbacRulesForEMailAddress_Trigger ON EMailAddress; drop trigger if exists createRbacRulesForEMailAddress_Trigger on EMailAddress;
CREATE TRIGGER createRbacRulesForEMailAddress_Trigger create trigger createRbacRulesForEMailAddress_Trigger
AFTER INSERT ON EMailAddress after insert
FOR EACH ROW EXECUTE PROCEDURE createRbacRulesForEMailAddress(); on EMailAddress
for each row
execute procedure createRbacRulesForEMailAddress();
-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForEMailAddress() -- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForEMailAddress()
-- create RBAC-restricted view -- create RBAC-restricted view
SET SESSION SESSION AUTHORIZATION DEFAULT; set session session authorization default;
-- ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY; -- ALTER TABLE EMailAddress ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS EMailAddress_rv; drop view if exists EMailAddress_rv;
CREATE OR REPLACE VIEW EMailAddress_rv AS create or replace view EMailAddress_rv as
SELECT DISTINCT target.* select distinct target.*
FROM EMailAddress AS target from EMailAddress as target
WHERE target.uuid IN (SELECT queryAccessibleObjectUuidsOfSubjectIds( 'view', 'emailaddress', currentSubjectIds())); where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'emailaddress', currentSubjectIds()));
GRANT ALL PRIVILEGES ON EMailAddress_rv TO restricted; grant all privileges on EMailAddress_rv to restricted;
-- generate EMailAddress test data -- generate EMailAddress test data
DO LANGUAGE plpgsql $$ do language plpgsql $$
DECLARE declare
dom record; dom record;
pacAdmin varchar; pacAdmin varchar;
currentTask varchar; currentTask varchar;
BEGIN begin
SET hsadminng.currentUser TO ''; set hsadminng.currentUser to '';
FOR dom IN ( for dom in (select d.uuid, d.name, p.name as packageName
SELECT d.uuid, d.name, p.name as packageName from domain d
FROM domain d join unixuser u on u.uuid = d.unixuseruuid
JOIN unixuser u ON u.uuid = d.unixuseruuid join package p on u.packageuuid = p.uuid
JOIN package p ON u.packageuuid = p.uuid join customer c on p.customeruuid = c.uuid
JOIN customer c ON p.customeruuid = c.uuid -- WHERE c.reference >= 18000
-- WHERE c.reference >= 18000 )
) LOOP loop
FOR t IN 0..4 LOOP for t in 0..4
currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name; loop
RAISE NOTICE 'task: %', currentTask; currentTask = 'creating RBAC test EMailAddress #' || t || ' for Domain ' || dom.name;
raise notice 'task: %', currentTask;
pacAdmin = 'admin@' || dom.packageName || '.example.com'; pacAdmin = 'admin@' || dom.packageName || '.example.com';
SET LOCAL hsadminng.currentUser TO pacAdmin; set local hsadminng.currentUser to pacAdmin;
SET LOCAL hsadminng.assumedRoles = ''; set local hsadminng.assumedRoles = '';
SET LOCAL hsadminng.currentTask TO currentTask; set local hsadminng.currentTask to currentTask;
INSERT INTO EMailAddress (localPart, domainUuid) insert
VALUES ('local' || t, dom.uuid); into EMailAddress (localPart, domainUuid)
values ('local' || t, dom.uuid);
COMMIT; commit;
END LOOP; end loop;
END LOOP; end loop;
END; end;
$$; $$;

View File

@ -1,26 +1,28 @@
-- ======================================================== -- ========================================================
-- Some Business Table Statistics -- Some Business Table Statistics
-- -------------------------------------------------------- -- --------------------------------------------------------
DROP VIEW IF EXISTS "BusinessTableStatisticsV"; drop view if exists "BusinessTableStatisticsV";
CREATE VIEW "BusinessTableStatisticsV" AS create view "BusinessTableStatisticsV" as
SELECT no, to_char("count", '999 999 999') as "count", to_char("required", '999 999 999') as "required", to_char("count"::float/"required"::float, '990.999') as "factor", "table" select no,
FROM (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table" to_char("count", '999 999 999') as "count",
from customer to_char("required", '999 999 999') as "required",
UNION to_char("count"::float / "required"::float, '990.999') as "factor",
select 2 as no, count(*) as "count", 15000 as "required", 'packages' as "table" "table"
from package from (select 1 as no, count(*) as "count", 7000 as "required", 'customers' as "table"
UNION from customer
select 3 as no, count(*) as "count", 150000 as "required", 'unixuser' as "table" union
from unixuser select 2 as no, count(*) as "count", 15000 as "required", 'packages' as "table"
UNION from package
select 4 as no, count(*) as "count", 100000 as "required", 'domain' as "table" union
from domain select 3 as no, count(*) as "count", 150000 as "required", 'unixuser' as "table"
UNION from unixuser
select 5 as no, count(*) as "count", 500000 as "required", 'emailaddress' as "table" union
from emailaddress select 4 as no, count(*) as "count", 100000 as "required", 'domain' as "table"
) totals from domain
ORDER BY totals.no; union
select 5 as no, count(*) as "count", 500000 as "required", 'emailaddress' as "table"
from emailaddress) totals
order by totals.no;
SELECT * FROM "BusinessTableStatisticsV"; select * from "BusinessTableStatisticsV";