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 INTO Hostsharing (uuid) VALUES ((SELECT uuid FROM RbacObject WHERE objectTable='hostsharing'));
insert
into RbacObject (objecttable) values ('hostsharing');
insert
into Hostsharing (uuid) values ((select uuid from RbacObject where objectTable = 'hostsharing'));
CREATE OR REPLACE FUNCTION hostsharingAdmin()
RETURNS RbacRoleDescriptor
RETURNS NULL ON NULL INPUT
STABLE LEAKPROOF
LANGUAGE sql AS $$
SELECT 'global', (SELECT uuid FROM RbacObject WHERE objectTable='hostsharing'), 'admin'::RbacRoleType;
create or replace function hostsharingAdmin()
returns RbacRoleDescriptor
returns null on null input
stable leakproof
language sql as $$
select 'global', (select uuid from RbacObject where objectTable = 'hostsharing'), 'admin'::RbacRoleType;
$$;
-- create administrators role with two assigned users
@ -32,7 +31,7 @@ do language plpgsql $$
$$;
BEGIN TRANSACTION;
SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';
select * from RbacUser where uuid=currentUserId();
END TRANSACTION;
begin transaction;
set local hsadminng.currentUser = 'mike@hostsharing.net';
select * from RbacUser where uuid = currentUserId();
end transaction;

View File

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

View File

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

View File

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

View File

@ -5,20 +5,24 @@
/*
Creates a view which presents some statistics about the RBAC tables.
*/
create view RbacStatisticsView AS
select no, to_char("count", '9 999 999 999') as "count", "table"
from (
select 1 as no, count(*) as "count", 'login users' as "table" from RbacUser
union
select 2 as no, count(*) as "count", 'roles' as "table" from RbacRole
union
select 3 as no, count(*) as "count", 'permissions' as "table" from RbacPermission
union
select 4 as no, count(*) as "count", 'references' as "table" from RbacReference
union
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
create view RbacStatisticsView as
select no, to_char("count", '9 999 999 999') as "count", "table"
from (select 1 as no, count(*) as "count", 'login users' as "table"
from RbacUser
union
select 2 as no, count(*) as "count", 'roles' as "table"
from RbacRole
union
select 3 as no, count(*) as "count", 'permissions' as "table"
from RbacPermission
union
select 4 as no, count(*) as "count", 'references' as "table"
from RbacReference
union
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;
--//

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

View File

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