From 1dde6b26092ca9ce64ce9d1f21af151c0a7ef3ad Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 27 Jul 2022 19:54:05 +0200 Subject: [PATCH] introduce referential integrity for role identification - part 1 --- sql/10-rbac-base.sql | 171 +++++++++++++++++++++-------------- sql/12-rbac-role-builder.sql | 48 ++++------ sql/20-hs-base.sql | 22 ++++- sql/21-hs-customer.sql | 28 +++--- sql/22-hs-packages.sql | 41 +++++---- sql/23-hs-unixuser.sql | 49 +++++----- sql/24-hs-domain.sql | 41 +++++---- sql/25-hs-emailaddress.sql | 37 ++++---- 8 files changed, 246 insertions(+), 191 deletions(-) diff --git a/sql/10-rbac-base.sql b/sql/10-rbac-base.sql index 07120878..68124563 100644 --- a/sql/10-rbac-base.sql +++ b/sql/10-rbac-base.sql @@ -29,10 +29,21 @@ CREATE TABLE RbacUser name varchar(63) not null unique ); +-- DROP TABLE IF EXISTS RbacObject; +CREATE TABLE RbacObject +( + uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + objectTable varchar(64) not null, + unique (objectTable, uuid) +); + +CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); + CREATE TABLE RbacRole ( uuid uuid primary key references RbacReference (uuid) ON DELETE CASCADE, - name varchar(63) not null unique + objectUuid uuid references RbacObject(uuid) not null, + roleType RbacRoleType not null ); CREATE TABLE RbacGrants @@ -56,14 +67,6 @@ CREATE DOMAIN RbacOp AS VARCHAR(67) OR VALUE ~ '^add-[a-z]+$' ); --- DROP TABLE IF EXISTS RbacObject; -CREATE TABLE RbacObject -( - uuid uuid UNIQUE DEFAULT uuid_generate_v4(), - objectTable varchar(64) not null, - unique (objectTable, uuid) -); - CREATE OR REPLACE FUNCTION createRbacObject() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ @@ -144,7 +147,51 @@ BEGIN END; $$; -CREATE OR REPLACE FUNCTION createRole(roleName varchar) +CREATE TYPE RbacRoleDescriptor AS + ( + objectTable varchar(63), -- TODO: needed? remove? + objectUuid uuid, + roleType RbacRoleType + ); + +-- TODO: this ... +CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + STABLE LEAKPROOF + LANGUAGE plpgsql AS $$ +BEGIN + RETURN ROW(objectTable, objectUuid, roleType); +END; $$; + +CREATE FUNCTION new_emp() RETURNS emp AS $$ +SELECT text 'None' AS name, + 1000.0 AS salary, + 25 AS age, + point '(2,2)' AS cubicle; +$$ LANGUAGE SQL; + +DO LANGUAGE plpgsql $$ +DECLARE + roleDesc RbacRoleDescriptor; +BEGIN + select * FROM roleDescriptor('global', gen_random_uuid(), 'admin') into roleDesc; + RAISE NOTICE 'result: % % %', roleDesc.objecttable, roleDesc.objectuuid, roleDesc.roletype; +END; $$; + +-- TODO: ... or that? +CREATE OR REPLACE FUNCTION roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType ) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + -- STABLE LEAKPROOF + LANGUAGE sql AS $$ + SELECT objectTable, objectUuid, roleType::RbacRoleType; + $$; + + + + +CREATE OR REPLACE FUNCTION createRole(roleDescriptor RbacRoleDescriptor) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ @@ -152,10 +199,7 @@ declare referenceId uuid; BEGIN INSERT INTO RbacReference (type) VALUES ('RbacRole') RETURNING uuid INTO referenceId; - INSERT INTO RbacRole (uuid, name) VALUES (referenceId, roleName); - IF (referenceId IS NULL) THEN - RAISE EXCEPTION 'referenceId for roleName "%" is unexpectedly null', roleName; - end if; + INSERT INTO RbacRole (uuid, objectUuid, roleType) VALUES (referenceId, roleDescriptor.objectUuid, roleDescriptor.roleType); return referenceId; END; $$; @@ -168,27 +212,27 @@ BEGIN END; $$; -CREATE OR REPLACE FUNCTION findRoleId(roleName varchar) +CREATE OR REPLACE FUNCTION findRoleId(roleDescriptor RbacRoleDescriptor) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE sql AS $$ - SELECT uuid FROM RbacRole WHERE name = roleName + SELECT uuid FROM RbacRole WHERE objectUuid = roleDescriptor.objectUuid AND roleType = roleDescriptor.roleType; $$; -CREATE OR REPLACE FUNCTION getRoleId(roleName varchar, whenNotExists RbacWhenNotExists) +CREATE OR REPLACE FUNCTION getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists) RETURNS uuid RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE roleUuid uuid; BEGIN - roleUuid = findRoleId(roleName); + roleUuid = findRoleId(roleDescriptor); IF ( roleUuid IS NULL ) THEN IF ( whenNotExists = 'fail') THEN - RAISE EXCEPTION 'RbacRole with name="%" not found', roleName; + RAISE EXCEPTION 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType; END IF; IF ( whenNotExists = 'create') THEN - roleUuid = createRole(roleName); + roleUuid = createRole(roleDescriptor); END IF; END IF; return roleUuid; @@ -204,6 +248,7 @@ DECLARE refId uuid; permissionIds uuid[] = ARRAY[]::uuid[]; BEGIN + RAISE NOTICE 'createPermission for: % %', forObjectUuid, permitOps; IF ( forObjectUuid IS NULL ) THEN RAISE EXCEPTION 'forObjectUuid must not be null'; END IF; @@ -214,16 +259,20 @@ BEGIN FOR i IN array_lower(permitOps, 1)..array_upper(permitOps, 1) LOOP refId = (SELECT uuid FROM RbacPermission WHERE objectUuid=forObjectUuid AND op=permitOps[i]); IF (refId IS NULL) THEN + RAISE NOTICE 'createPermission: % %', forObjectUuid, permitOps[i]; INSERT INTO RbacReference ("type") VALUES ('RbacPermission') RETURNING uuid INTO refId; INSERT INTO RbacPermission (uuid, objectUuid, op) VALUES (refId, forObjectUuid, permitOps[i]); END IF; + RAISE NOTICE 'addPermission: %', refId; permissionIds = permissionIds || refId; END LOOP; + + RAISE NOTICE 'createPermissions returning: %', permissionIds; return permissionIds; END; $$; -CREATE OR REPLACE FUNCTION findPermissionId(forObjectTable varchar, forObjectUuid uuid, forOp RbacOp) +CREATE OR REPLACE FUNCTION findPermissionId(forObjectUuid uuid, forOp RbacOp) RETURNS uuid RETURNS NULL ON NULL INPUT STABLE LEAKPROOF @@ -248,6 +297,7 @@ END; $$; CREATE OR REPLACE PROCEDURE grantPermissionsToRole(roleUuid uuid, permissionIds uuid[]) LANGUAGE plpgsql AS $$ BEGIN + RAISE NOTICE 'grantPermissionsToRole: % -> %', roleUuid, permissionIds; FOR i IN array_lower(permissionIds, 1)..array_upper(permissionIds, 1) LOOP perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); perform assertReferenceType('permissionId (descendant)', permissionIds[i], 'RbacPermission'); @@ -295,21 +345,6 @@ BEGIN ON CONFLICT DO NOTHING ; -- TODO: remove? END; $$; -abort; -set local session authorization default; - -CREATE OR REPLACE FUNCTION nextLevel(level integer, maxDepth integer) - RETURNS INTEGER - LANGUAGE plpgsql AS $$ - BEGIN - IF (level > maxDepth) THEN - RAISE WARNING 'Role assignment depth exceeded %/%.', level, maxDepth; - END IF; - RETURN level+1; - END; -$$; - - abort; set local session authorization default; @@ -444,7 +479,7 @@ begin transaction; end transaction; --- - +/* CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TODO: remove? RETURNS SETOF RbacPermission RETURNS NULL ON NULL INPUT @@ -469,7 +504,7 @@ CREATE OR REPLACE FUNCTION queryAllPermissionsOfSubjectId(subjectId uuid) -- TOD FROM grants ); -$$; +$$;*/ --- @@ -643,43 +678,39 @@ CREATE OR REPLACE FUNCTION currentSubjectIds() STABLE LEAKPROOF LANGUAGE plpgsql AS $$ DECLARE - assumedRoles VARCHAR(63)[]; - currentUserId uuid; - assumedRoleIds uuid[]; - assumedRoleId uuid; + currentUserId uuid; + roleNames VARCHAR(63)[]; + roleName VARCHAR(63); + objectTableToAssume VARCHAR(63); + objectNameToAssume VARCHAR(63); + objectUuidToAssume uuid; + roleTypeToAssume RbacRoleType; + roleIdsToAssume uuid[]; + roleUuidToAssume uuid; BEGIN currentUserId := currentUserId(); - assumedRoles := assumedRoles(); - IF ( CARDINALITY(assumedRoles) = 0 ) THEN + roleNames := assumedRoles(); + IF ( CARDINALITY(roleNames) = 0 ) THEN RETURN ARRAY[currentUserId]; END IF; - RAISE NOTICE 'assuming roles: %', assumedRoles; + RAISE NOTICE 'assuming roles: %', roleNames; - SELECT ARRAY_AGG(uuid) FROM RbacRole WHERE name = ANY(assumedRoles) INTO assumedRoleIds; - IF assumedRoleIds IS NOT NULL THEN - FOREACH assumedRoleId IN ARRAY assumedRoleIds LOOP - IF ( NOT isGranted(currentUserId, assumedRoleId) ) THEN - RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), assumedRoleId; - END IF; - END LOOP; - END IF; - RETURN assumedRoleIds; -END; $$; + FOREACH roleName IN ARRAY roleNames LOOP + roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.')); + objectTableToAssume = split_part(roleName, '#', 1); + objectNameToAssume = split_part(roleName, '#', 2); + roleTypeToAssume = split_part(roleName, '#', 3); -rollback; -set session authorization default; -CREATE OR REPLACE FUNCTION maxGrantDepth() - RETURNS integer - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -DECLARE - maxGrantDepth VARCHAR(63); -BEGIN - BEGIN - maxGrantDepth := current_setting('hsadminng.maxGrantDepth'); - EXCEPTION WHEN OTHERS THEN - maxGrantDepth := NULL; - END; - RETURN coalesce(maxGrantDepth, '8')::integer; + -- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop + SELECT uuid AS roleuuidToAssume + FROM RbacRole r + WHERE r.objectUuid=objectUuidToAssume AND r.roleType=roleTypeToAssume INTO roleUuidToAssume; + IF ( NOT isGranted(currentUserId, roleUuidToAssume) ) THEN + RAISE EXCEPTION 'user % has no permission to assume role %', currentUser(), roleUuidToAssume; + END IF; + roleIdsToAssume := roleIdsToAssume || roleUuidToAssume; + END LOOP; + + RETURN roleIdsToAssume; END; $$; diff --git a/sql/12-rbac-role-builder.sql b/sql/12-rbac-role-builder.sql index fc24de10..410a97e8 100644 --- a/sql/12-rbac-role-builder.sql +++ b/sql/12-rbac-role-builder.sql @@ -4,8 +4,6 @@ -- Role-Hierarcy helper functions -- -------------------------------------------------------- -CREATE TYPE RbacRoleType AS ENUM ('owner', 'admin', 'tenant'); - -- PERMISSIONS -------------------------------------------- -- drop type RbacPermissions; @@ -29,27 +27,27 @@ CREATE TYPE RbacSuperRoles AS roleUuids uuid[] ); --- drop function beneathRoles(roleName varchar); -CREATE OR REPLACE FUNCTION beneathRoles(roleNames varchar[]) +-- drop function beneathRoles(roleDescriptors RbacRoleDescriptor[]) +CREATE OR REPLACE FUNCTION beneathRoles(roleDescriptors RbacRoleDescriptor[]) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ DECLARE - superRoleName varchar; + superRoleDescriptor RbacRoleDescriptor; superRoleUuids uuid[] := ARRAY[]::uuid[]; BEGIN - FOREACH superRoleName IN ARRAY roleNames LOOP - superRoleUuids := superRoleUuids || getRoleId(superRoleName, 'fail'); + FOREACH superRoleDescriptor IN ARRAY roleDescriptors LOOP + superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail'); END LOOP; RETURN ROW(superRoleUuids)::RbacSuperRoles; END; $$; --- drop function beneathRole(roleName varchar); -CREATE OR REPLACE FUNCTION beneathRole(roleName varchar) +-- drop function beneathRole(roleDescriptor RbacRoleDescriptor) +CREATE OR REPLACE FUNCTION beneathRole(roleDescriptor RbacRoleDescriptor) RETURNS RbacSuperRoles LANGUAGE plpgsql STRICT AS $$ BEGIN - RETURN beneathRoles(ARRAY[roleName]); + RETURN beneathRoles(ARRAY[roleDescriptor]); END; $$; -- drop function beneathRole(roleUuid uuid); @@ -83,12 +81,12 @@ BEGIN RETURN ROW(ARRAY[roleUuid]::uuid[])::RbacSubRoles; END; $$; --- drop FUNCTION beingItselfA(roleName varchar) -CREATE OR REPLACE FUNCTION beingItselfA(roleName varchar) +-- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) +CREATE OR REPLACE FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor) RETURNS RbacSubRoles LANGUAGE plpgsql STRICT AS $$ BEGIN - RETURN beingItselfA(getRoleId(roleName, 'fail')); + RETURN beingItselfA(getRoleId(roleDescriptor, 'fail')); END; $$; -- USERS -------------------------------------------------- @@ -126,20 +124,11 @@ END; $$; -- ROLE NAME BUILDER -------------------------------------- -CREATE OR REPLACE FUNCTION roleName(objectTable varchar, objectName varchar, roleType RbacRoleType ) - RETURNS varchar - RETURNS NULL ON NULL INPUT - STABLE LEAKPROOF - LANGUAGE plpgsql AS $$ -BEGIN - RETURN objectTable || '#' || objectName || '.' || roleType; -END; $$; - -- CREATE ROLE MAIN FUNCTION ------------------------------ CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, superRoles RbacSuperRoles, subRoles RbacSubRoles = null, @@ -154,8 +143,9 @@ DECLARE subRoleUuid uuid; userUuid uuid; BEGIN - RAISE NOTICE 'creating role: %', roleName; - roleUuid = createRole(roleName); + 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); @@ -181,7 +171,7 @@ BEGIN END; $$; CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, users RbacUsers = null ) @@ -189,11 +179,11 @@ CREATE OR REPLACE FUNCTION createRole( CALLED ON NULL INPUT LANGUAGE plpgsql AS $$ BEGIN - RETURN createRole(roleName, permissions, null, null, users); + RETURN createRole(roleDescriptor, permissions, null, null, users); END; $$; CREATE OR REPLACE FUNCTION createRole( - roleName varchar, + roleDescriptor RbacRoleDescriptor, permissions RbacPermissions, subRoles RbacSubRoles, users RbacUsers = null @@ -202,7 +192,7 @@ CREATE OR REPLACE FUNCTION createRole( CALLED ON NULL INPUT LANGUAGE plpgsql AS $$ BEGIN - RETURN createRole(roleName, permissions, null, subRoles, users); + RETURN createRole(roleDescriptor, permissions, null, subRoles, users); END; $$; diff --git a/sql/20-hs-base.sql b/sql/20-hs-base.sql index ff745295..dcd8a252 100644 --- a/sql/20-hs-base.sql +++ b/sql/20-hs-base.sql @@ -1,10 +1,30 @@ + + +CREATE TABLE Hostsharing +( + uuid uuid PRIMARY KEY REFERENCES RbacObject(uuid) +); +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')); + +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 do language plpgsql $$ declare admins uuid ; begin - admins = createRole('administrators'); + admins = createRole(hostsharingAdmin()); call grantRoleToUser(admins, createRbacUser('mike@hostsharing.net')); call grantRoleToUser(admins, createRbacUser('sven@hostsharing.net')); commit; diff --git a/sql/21-hs-customer.sql b/sql/21-hs-customer.sql index 83612e9b..3fcb2b1e 100644 --- a/sql/21-hs-customer.sql +++ b/sql/21-hs-customer.sql @@ -17,25 +17,25 @@ CREATE TRIGGER createRbacObjectForCustomer_Trigger BEFORE INSERT ON customer FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION customerOwner(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerOwner(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'owner'); + return roleDescriptor('customer', customer.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION customerAdmin(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerAdmin(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'admin'); + return roleDescriptor('customer', customer.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION customerTenant(customerName varchar) - RETURNS varchar +CREATE OR REPLACE FUNCTION customerTenant(customer customer) + RETURNS RbacRoleDescriptor LANGUAGE plpgsql STRICT AS $$ begin - return roleName('customer', customerName, 'tenant'); + return roleDescriptor('customer', customer.uuid, 'tenant'); end; $$; @@ -52,14 +52,14 @@ BEGIN -- the owner role with full access for Hostsharing administrators customerOwnerUuid = createRole( - customerOwner(NEW.prefix), + customerOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole('administrators') + beneathRole(hostsharingAdmin()) ); -- the admin role for the customer's admins, who can view and add products customerAdminUuid = createRole( - customerAdmin(NEW.prefix), + 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 @@ -70,7 +70,7 @@ BEGIN -- the tenant role which later can be used by owners+admins of sub-objects perform createRole( - customerTenant(NEW.prefix), + customerTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['view']) ); @@ -130,7 +130,7 @@ DO LANGUAGE plpgsql $$ BEGIN SET hsadminng.currentUser TO ''; - FOR t IN 0..6999 LOOP + FOR t IN 0..9 LOOP currentTask = 'creating RBAC test customer #' || t; SET LOCAL hsadminng.currentUser TO 'mike@hostsharing.net'; SET LOCAL hsadminng.assumedRoles = ''; diff --git a/sql/22-hs-packages.sql b/sql/22-hs-packages.sql index 3f21ae45..cd25bc53 100644 --- a/sql/22-hs-packages.sql +++ b/sql/22-hs-packages.sql @@ -11,25 +11,30 @@ CREATE TABLE IF NOT EXISTS package ( customerUuid uuid REFERENCES customer(uuid) ); -CREATE OR REPLACE FUNCTION packageOwner(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageOwner(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ +declare + roleDesc RbacRoleDescriptor; begin - return roleName('package', packageName, 'owner'); + return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageAdmin(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageAdmin(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('package', packageName, 'admin'); + return roleDescriptor('package', pac.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION packageTenant(packageName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION packageTenant(pac package) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('package', packageName, 'tenant'); + return roleDescriptor('package', pac.uuid, 'tenant'); end; $$; @@ -54,24 +59,24 @@ BEGIN -- an owner role is created and assigned to the customer's admin role packageOwnerRoleUuid = createRole( - packageOwner(NEW.name), + packageOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(customerAdmin(parentCustomer.prefix)) + beneathRole(customerAdmin(parentCustomer)) ); -- an owner role is created and assigned to the package owner role packageAdminRoleUuid = createRole( - packageAdmin(NEW.name), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-unixuser']), + packageAdmin(NEW), + 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.name), + packageTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY ['view']), beneathRole(packageAdminRoleUuid), - beingItselfA(customerTenant(parentCustomer.prefix)) + beingItselfA(customerTenant(parentCustomer)) ); RETURN NEW; diff --git a/sql/23-hs-unixuser.sql b/sql/23-hs-unixuser.sql index 44392104..52ac180c 100644 --- a/sql/23-hs-unixuser.sql +++ b/sql/23-hs-unixuser.sql @@ -12,25 +12,28 @@ CREATE TABLE IF NOT EXISTS UnixUser ( packageUuid uuid REFERENCES package(uuid) ); -CREATE OR REPLACE FUNCTION unixUserOwner(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserOwner(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'owner'); + return roleDescriptor('unixuser', uu.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION unixUserAdmin(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserAdmin(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'admin'); + return roleDescriptor('unixuser', uu.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION unixUserTenant(unixUserName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION unixUserTenant(uu UnixUser) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('unixuser', unixUserName, 'tenant'); + return roleDescriptor('unixuser', uu.uuid, 'tenant'); end; $$; CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser) @@ -38,19 +41,19 @@ CREATE OR REPLACE FUNCTION createUnixUserTenantRoleIfNotExists(unixUser UnixUser RETURNS NULL ON NULL INPUT LANGUAGE plpgsql AS $$ DECLARE - unixUserTenantRoleName varchar; + unixUserTenantRoleDesc RbacRoleDescriptor; unixUserTenantRoleUuid uuid; BEGIN - unixUserTenantRoleName = unixUserTenant(unixUser.name); - unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleName); + unixUserTenantRoleDesc = unixUserTenant(unixUser); + unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc); IF unixUserTenantRoleUuid IS NOT NULL THEN RETURN unixUserTenantRoleUuid; END IF; RETURN createRole( - unixUserTenantRoleName, - grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['edit', 'add-domain']), - beneathRole(unixUserAdmin(unixUser.name)) + unixUserTenantRoleDesc, + grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => ARRAY['view']), + beneathRole(unixUserAdmin(unixUser)) ); END; $$; @@ -76,17 +79,17 @@ BEGIN -- an owner role is created and assigned to the package's admin group unixuserOwnerRoleId = createRole( - unixUserOwner(NEW.name), + unixUserOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(packageAdmin(parentPackage.name)) + beneathRole(packageAdmin(parentPackage)) ); -- and a unixuser admin role is created and assigned to the unixuser owner as well unixuserAdminRoleId = createRole( - unixUserAdmin(NEW.name), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-domain']), + unixUserAdmin(NEW), + grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), beneathRole(unixuserOwnerRoleId), - beingItselfA(packageTenant(parentPackage.name)) + beingItselfA(packageTenant(parentPackage)) ); -- a tenent role is only created on demand diff --git a/sql/24-hs-domain.sql b/sql/24-hs-domain.sql index e9fbc142..b8588913 100644 --- a/sql/24-hs-domain.sql +++ b/sql/24-hs-domain.sql @@ -16,25 +16,28 @@ CREATE TRIGGER createRbacObjectForDomain_Trigger BEFORE INSERT ON Domain FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION domainOwner(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainOwner(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'owner'); + return roleDescriptor('domain', dom.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION domainAdmin(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainAdmin(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'admin'); + return roleDescriptor('domain', dom.uuid, 'admin'); end; $$; -CREATE OR REPLACE FUNCTION domainTenant(unixUserName varchar, domainName varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION domainTenant(dom Domain) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('domain', unixUserName || '/' || domainName, 'tenant'); + return roleDescriptor('domain', dom.uuid, 'tenant'); end; $$; @@ -42,7 +45,8 @@ CREATE OR REPLACE FUNCTION createRbacRulesForDomain() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - parentUser unixuser; + parentUser UnixUser; + parentPackage package; domainOwnerRoleUuid uuid; domainAdminRoleUuid uuid; BEGIN @@ -50,25 +54,26 @@ BEGIN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; 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; -- a domain owner role is created and assigned to the unixuser's admin role domainOwnerRoleUuid = createRole( - domainOwner(parentUser.name, NEW.name), + domainOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(unixUserAdmin(parentUser.name)) + beneathRole(packageAdmin(parentPackage)) ); -- a domain admin role is created and assigned to the domain's owner role domainAdminRoleUuid = createRole( - domainAdmin(parentUser.name, NEW.name), + domainAdmin(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit', 'add-emailaddress']), beneathRole(domainOwnerRoleUuid) ); -- and a domain tenant role is created and assigned to the domain's admiin role perform createRole( - domainTenant(parentUser.name, NEW.name), + domainTenant(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), beneathRole(domainAdminRoleUuid), beingItselfA(createUnixUserTenantRoleIfNotExists(parentUser)) diff --git a/sql/25-hs-emailaddress.sql b/sql/25-hs-emailaddress.sql index 7ff46b5a..12aba6ee 100644 --- a/sql/25-hs-emailaddress.sql +++ b/sql/25-hs-emailaddress.sql @@ -16,50 +16,51 @@ CREATE TRIGGER createRbacObjectForEMailAddress_Trigger BEFORE INSERT ON EMailAddress FOR EACH ROW EXECUTE PROCEDURE createRbacObject(); -CREATE OR REPLACE FUNCTION emailAddressOwner(emailAddress varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION emailAddressOwner(emAddr EMailAddress) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('emailaddress', emailAddress, 'owner'); + return roleDescriptor('emailaddress', emAddr.uuid, 'owner'); end; $$; -CREATE OR REPLACE FUNCTION emailAddressAdmin(emailAddress varchar) - RETURNS varchar - LANGUAGE plpgsql STRICT AS $$ +CREATE OR REPLACE FUNCTION emailAddressAdmin(emAddr EMailAddress) + RETURNS RbacRoleDescriptor + RETURNS NULL ON NULL INPUT + LANGUAGE plpgsql AS $$ begin - return roleName('emailaddress', emailAddress, 'admin'); + return roleDescriptor('emailaddress', emAddr.uuid, 'admin'); end; $$; CREATE OR REPLACE FUNCTION createRbacRulesForEMailAddress() RETURNS trigger LANGUAGE plpgsql STRICT AS $$ DECLARE - parentDomain record; - eMailAddress varchar; + parentDomain Domain; eMailAddressOwnerRoleUuid uuid; BEGIN IF TG_OP <> 'INSERT' THEN RAISE EXCEPTION 'invalid usage of TRIGGER AFTER INSERT'; END IF; - SELECT d.name as name, u.name as unixUserName FROM domain d - LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid - WHERE d.uuid=NEW.domainUuid into parentDomain; - eMailAddress = NEW.localPart || '@' || parentDomain.name; + SELECT d.* + FROM domain d + LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid + WHERE d.uuid=NEW.domainUuid INTO parentDomain; -- an owner role is created and assigned to the domains's admin group eMailAddressOwnerRoleUuid = createRole( - emailAddressOwner(eMailAddress), + emailAddressOwner(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['*']), - beneathRole(domainAdmin( parentDomain.unixUserName, parentDomain.name)) + beneathRole(domainAdmin( parentDomain)) ); -- and an admin role is created and assigned to the unixuser owner as well perform createRole( - emailAddressAdmin(eMailAddress), + emailAddressAdmin(NEW), grantingPermissions(forObjectUuid => NEW.uuid, permitOps => ARRAY['edit']), beneathRole(eMailAddressOwnerRoleUuid), - beingItselfA(domainTenant(parentDomain.unixUserName, parentDomain.name)) + beingItselfA(domainTenant(parentDomain)) ); RETURN NEW;