introduce referential integrity for role identification - part 1
This commit is contained in:
parent
6f6320565c
commit
1dde6b2609
@ -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;
|
||||
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;
|
||||
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);
|
||||
|
||||
-- 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;
|
||||
END IF;
|
||||
RETURN assumedRoleIds;
|
||||
END; $$;
|
||||
|
||||
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;
|
||||
RETURN roleIdsToAssume;
|
||||
END; $$;
|
||||
|
@ -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; $$;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 = '';
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
SELECT d.*
|
||||
FROM domain d
|
||||
LEFT JOIN unixuser u ON u.uuid = d.unixuseruuid
|
||||
WHERE d.uuid=NEW.domainUuid into parentDomain;
|
||||
eMailAddress = NEW.localPart || '@' || parentDomain.name;
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user