introduce referential integrity for role identification - part 1

This commit is contained in:
Michael Hoennig 2022-07-27 19:54:05 +02:00
parent 6f6320565c
commit 1dde6b2609
8 changed files with 246 additions and 191 deletions

View File

@ -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; $$;

View File

@ -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; $$;

View File

@ -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;

View File

@ -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 = '';

View File

@ -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;

View File

@ -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

View File

@ -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))

View File

@ -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;