add RbacUserController/-Entity/-Repository
This commit is contained in:
parent
18f3234272
commit
06996e4dc4
@ -0,0 +1,46 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class RbacUserController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RbacUserRepository rbacUserRepository;
|
||||||
|
|
||||||
|
@GetMapping(value = "/api/rbacuser")
|
||||||
|
@Transactional
|
||||||
|
public Iterable<RbacUserEntity> listUsers(
|
||||||
|
@RequestHeader(name = "current-user") String currentUserName,
|
||||||
|
@RequestHeader(name = "assumed-roles", required = false) String assumedRoles,
|
||||||
|
@RequestParam(name="name", required = false) String userName
|
||||||
|
) {
|
||||||
|
context.setCurrentUser(currentUserName);
|
||||||
|
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||||
|
context.assumeRoles(assumedRoles);
|
||||||
|
}
|
||||||
|
return rbacUserRepository.findByOptionalNameLike(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/api/rbacuser/{userName}/permissions")
|
||||||
|
@Transactional
|
||||||
|
public Iterable<RbacUserPermission> listUserPermissions(
|
||||||
|
@RequestHeader(name = "current-user") String currentUserName,
|
||||||
|
@RequestHeader(name = "assumed-roles", required = false) String assumedRoles,
|
||||||
|
@PathVariable(name= "userName") String userName
|
||||||
|
) {
|
||||||
|
context.setCurrentUser(currentUserName);
|
||||||
|
if (assumedRoles != null && !assumedRoles.isBlank()) {
|
||||||
|
context.assumeRoles(assumedRoles);
|
||||||
|
}
|
||||||
|
return rbacUserRepository.findPermissionsOfUser(userName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import org.springframework.data.annotation.Immutable;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "rbacuser_rv")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Immutable
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
//@SqlResultSetMapping(
|
||||||
|
// name = "rbacUserPermissionMapping",
|
||||||
|
// classes = {
|
||||||
|
// @ConstructorResult(
|
||||||
|
// targetClass = RbacUserPermission.class,
|
||||||
|
// columns = {
|
||||||
|
// @ColumnResult(name = "roleUuid", type = UUID.class),
|
||||||
|
// @ColumnResult(name = "oleName", type = String.class),
|
||||||
|
// @ColumnResult(name = "permissionUuid", type = UUID.class),
|
||||||
|
// @ColumnResult(name = "op", type=String.class),
|
||||||
|
// @ColumnResult(name = "objectTable", type=String.class),
|
||||||
|
// @ColumnResult(name = "objectIdName", type =String.class),
|
||||||
|
// @ColumnResult(name = "objectUuid", type = UUID.class),
|
||||||
|
// @ColumnResult(name = "campId", type = Integer.class),
|
||||||
|
// @ColumnResult(name = "userCount", type = Byte.class)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//)
|
||||||
|
//@NamedNativeQuery(
|
||||||
|
// name = "grantedPermissions",
|
||||||
|
// query = "SELECT * FROM grantedPermissions(:userName)",
|
||||||
|
// resultSetMapping = "rbacUserPermissionMapping"
|
||||||
|
//)
|
||||||
|
public class RbacUserEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private UUID uuid;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface RbacUserPermission {
|
||||||
|
|
||||||
|
UUID getRoleUuid();
|
||||||
|
String getRoleName();
|
||||||
|
UUID getPermissionUuid();
|
||||||
|
String getOp();
|
||||||
|
String getObjectTable();
|
||||||
|
String getObjectIdName();
|
||||||
|
UUID getObjectUuid();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.Repository;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface RbacUserRepository extends Repository<RbacUserEntity, UUID> {
|
||||||
|
|
||||||
|
@Query("SELECT u FROM RbacUserEntity u WHERE :userName is null or u.name like concat(:userName, '%')")
|
||||||
|
List<RbacUserEntity> findByOptionalNameLike(final String userName);
|
||||||
|
|
||||||
|
@Query(value = "SELECT * FROM grantedPermissions(:userName)", nativeQuery = true)
|
||||||
|
Iterable<RbacUserPermission> findPermissionsOfUser(@Param("userName") String userName);
|
||||||
|
}
|
@ -501,36 +501,37 @@ $$;
|
|||||||
--changeset rbac-base-QUERY-GRANTED-PERMISSIONS:1 endDelimiter:--//
|
--changeset rbac-base-QUERY-GRANTED-PERMISSIONS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
|
Returns all permissions accessible to the given subject UUID (user or role).
|
||||||
*/
|
*/
|
||||||
create or replace function queryGrantedPermissionsOfSubjectIds(requiredOp RbacOp, subjectIds uuid[])
|
create or replace function queryPermissionsGrantedToSubjectId(subjectId uuid)
|
||||||
returns setof RbacPermission
|
returns setof RbacPermission
|
||||||
strict
|
strict
|
||||||
language sql as $$
|
language sql as $$
|
||||||
select distinct *
|
-- @formatter:off
|
||||||
|
select *
|
||||||
from RbacPermission
|
from RbacPermission
|
||||||
where op = '*'
|
where uuid in (
|
||||||
or op = requiredOp
|
with recursive grants as (
|
||||||
and uuid in (with recursive grants as (select distinct descendantUuid,
|
select distinct descendantUuid, ascendantUuid
|
||||||
ascendantUuid
|
|
||||||
from RbacGrants
|
from RbacGrants
|
||||||
where ascendantUuid = any (subjectIds)
|
where ascendantUuid = subjectId
|
||||||
union all
|
union all
|
||||||
select "grant".descendantUuid,
|
select "grant".descendantUuid, "grant".ascendantUuid
|
||||||
"grant".ascendantUuid
|
|
||||||
from RbacGrants "grant"
|
from RbacGrants "grant"
|
||||||
inner join grants recur on recur.descendantUuid = "grant".ascendantUuid)
|
inner join grants recur on recur.descendantUuid = "grant".ascendantUuid
|
||||||
|
)
|
||||||
select descendantUuid
|
select descendantUuid
|
||||||
from grants);
|
from grants
|
||||||
|
);
|
||||||
|
-- @formatter:on
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset rbac-base-QUERY-USERS-WITH-PERMISSION-FOR-OBJECT:1 endDelimiter:--//
|
--changeset rbac-base-QUERY-USERS-WITH-PERMISSION-FOR-OBJECT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
|
Returns all user UUIDs which have any permission for the given object UUID.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace function queryAllRbacUsersWithPermissionsFor(objectId uuid)
|
create or replace function queryAllRbacUsersWithPermissionsFor(objectId uuid)
|
||||||
@ -554,190 +555,6 @@ $$;
|
|||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset rbac-CURRENT-USER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
|
|
||||||
*/
|
|
||||||
create or replace function currentUser()
|
|
||||||
returns varchar(63)
|
|
||||||
stable leakproof
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
currentUser varchar(63);
|
|
||||||
begin
|
|
||||||
begin
|
|
||||||
currentUser := current_setting('hsadminng.currentUser');
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
currentUser := null;
|
|
||||||
end;
|
|
||||||
if (currentUser is null or currentUser = '') then
|
|
||||||
raise exception 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"';
|
|
||||||
end if;
|
|
||||||
return currentUser;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create or replace function currentUserId()
|
|
||||||
returns uuid
|
|
||||||
stable leakproof
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
currentUser varchar(63);
|
|
||||||
currentUserId uuid;
|
|
||||||
begin
|
|
||||||
currentUser := currentUser();
|
|
||||||
currentUserId = (select uuid from RbacUser where name = currentUser);
|
|
||||||
if currentUserId is null then
|
|
||||||
raise exception 'hsadminng.currentUser defined as %, but does not exists', currentUser;
|
|
||||||
end if;
|
|
||||||
return currentUserId;
|
|
||||||
end; $$;
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset rbac-ASSUMED-ROLES:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
|
|
||||||
*/
|
|
||||||
create or replace function assumedRoles()
|
|
||||||
returns varchar(63)[]
|
|
||||||
stable leakproof
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
currentSubject varchar(63);
|
|
||||||
begin
|
|
||||||
begin
|
|
||||||
currentSubject := current_setting('hsadminng.assumedRoles');
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
return array []::varchar[];
|
|
||||||
end;
|
|
||||||
if (currentSubject = '') then
|
|
||||||
return array []::varchar[];
|
|
||||||
end if;
|
|
||||||
return string_to_array(currentSubject, ';');
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create or replace function pureIdentifier(rawIdentifier varchar)
|
|
||||||
returns varchar
|
|
||||||
returns null on null input
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
return regexp_replace(rawIdentifier, '\W+', '');
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
-- TODO: rename to findObjectUuidByIdName
|
|
||||||
create or replace function findUuidByIdName(objectTable varchar, objectIdName varchar)
|
|
||||||
returns uuid
|
|
||||||
returns null on null input
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
sql varchar;
|
|
||||||
uuid uuid;
|
|
||||||
begin
|
|
||||||
objectTable := pureIdentifier(objectTable);
|
|
||||||
objectIdName := pureIdentifier(objectIdName);
|
|
||||||
sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName);
|
|
||||||
begin
|
|
||||||
raise notice 'sql: %', sql;
|
|
||||||
execute sql into uuid;
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable;
|
|
||||||
end;
|
|
||||||
return uuid;
|
|
||||||
end ; $$;
|
|
||||||
|
|
||||||
create or replace function findIdNameByObjectUuid(objectTable varchar, objectUuid uuid)
|
|
||||||
returns varchar
|
|
||||||
returns null on null input
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
sql varchar;
|
|
||||||
idName varchar;
|
|
||||||
begin
|
|
||||||
objectTable := pureIdentifier(objectTable);
|
|
||||||
sql := format('select * from %sIdNameByUuid(%L::uuid);', objectTable, objectUuid);
|
|
||||||
begin
|
|
||||||
raise notice 'sql: %', sql;
|
|
||||||
execute sql into idName;
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
raise exception 'function %IdNameByUuid(...) not found, add identity view support for table %', objectTable, objectTable;
|
|
||||||
end;
|
|
||||||
return idName;
|
|
||||||
end ; $$;
|
|
||||||
|
|
||||||
create or replace function currentSubjects()
|
|
||||||
returns varchar(63)[]
|
|
||||||
stable leakproof
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
assumedRoles varchar(63)[];
|
|
||||||
begin
|
|
||||||
assumedRoles := assumedRoles();
|
|
||||||
if array_length(assumedRoles(), 1) > 0 then
|
|
||||||
return assumedRoles();
|
|
||||||
else
|
|
||||||
return array [currentUser()]::varchar(63)[];
|
|
||||||
end if;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create or replace function currentSubjectIds()
|
|
||||||
returns uuid[]
|
|
||||||
stable leakproof
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
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();
|
|
||||||
if currentUserId is null then
|
|
||||||
raise exception 'user % does not exist', currentUser();
|
|
||||||
end if;
|
|
||||||
|
|
||||||
roleNames := assumedRoles();
|
|
||||||
if cardinality(roleNames) = 0 then
|
|
||||||
return array [currentUserId];
|
|
||||||
end if;
|
|
||||||
|
|
||||||
raise notice 'assuming roles: %', roleNames;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
objectUuidToAssume = findUuidByIdName(objectTableToAssume, objectNameToAssume);
|
|
||||||
|
|
||||||
-- 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(), currentUserId, roleName, roleUuidToAssume;
|
|
||||||
end if;
|
|
||||||
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
|
|
||||||
end loop;
|
|
||||||
|
|
||||||
return roleIdsToAssume;
|
|
||||||
end; $$;
|
|
||||||
--//
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset rbac-base-PGSQL-ROLES:1 endDelimiter:--//
|
--changeset rbac-base-PGSQL-ROLES:1 endDelimiter:--//
|
||||||
@ -750,21 +567,3 @@ create role restricted;
|
|||||||
grant all privileges on all tables in schema public to restricted;
|
grant all privileges on all tables in schema public to restricted;
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset rbac-base-ROLE-RESTRICTED-VIEW:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
Creates a view to the role table with row-level limitation
|
|
||||||
based on the grants of the current user or assumed roles.
|
|
||||||
*/
|
|
||||||
drop view if exists rbacrole_rv;
|
|
||||||
create or replace view rbacrole_rv as
|
|
||||||
select DISTINCT r.*, o.objectTable,
|
|
||||||
findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName
|
|
||||||
from rbacrole as r
|
|
||||||
join rbacobject as o on o.uuid=r.objectuuid
|
|
||||||
where isGranted(currentSubjectIds(), r.uuid);
|
|
||||||
grant all privileges on rbacrole_rv to restricted;
|
|
||||||
--//
|
|
||||||
|
187
src/main/resources/db/changelog/2022-07-28-006-rbac-current.sql
Normal file
187
src/main/resources/db/changelog/2022-07-28-006-rbac-current.sql
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-current-CURRENT-USER:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Returns the current user as set by `hsadminng.currentUser`.
|
||||||
|
Raises exception if not set.
|
||||||
|
*/
|
||||||
|
create or replace function currentUser()
|
||||||
|
returns varchar(63)
|
||||||
|
stable leakproof
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
currentUser varchar(63);
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
currentUser := current_setting('hsadminng.currentUser');
|
||||||
|
exception
|
||||||
|
when others then
|
||||||
|
currentUser := null;
|
||||||
|
end;
|
||||||
|
if (currentUser is null or currentUser = '') then
|
||||||
|
raise exception 'hsadminng.currentUser must be defined, please use "SET LOCAL ...;"';
|
||||||
|
end if;
|
||||||
|
return currentUser;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create or replace function currentUserId()
|
||||||
|
returns uuid
|
||||||
|
stable leakproof
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
currentUser varchar(63);
|
||||||
|
currentUserId uuid;
|
||||||
|
begin
|
||||||
|
currentUser := currentUser();
|
||||||
|
currentUserId = (select uuid from RbacUser where name = currentUser);
|
||||||
|
if currentUserId is null then
|
||||||
|
raise exception 'hsadminng.currentUser defined as %, but does not exists', currentUser;
|
||||||
|
end if;
|
||||||
|
return currentUserId;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-current-ASSUMED-ROLES:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Returns assumed role names as set in `hsadminng.assumedRoles`
|
||||||
|
or empty array, if not set.
|
||||||
|
*/
|
||||||
|
create or replace function assumedRoles()
|
||||||
|
returns varchar(63)[]
|
||||||
|
stable leakproof
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
currentSubject varchar(63);
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
currentSubject := current_setting('hsadminng.assumedRoles');
|
||||||
|
exception
|
||||||
|
when others then
|
||||||
|
return array []::varchar[];
|
||||||
|
end;
|
||||||
|
if (currentSubject = '') then
|
||||||
|
return array []::varchar[];
|
||||||
|
end if;
|
||||||
|
return string_to_array(currentSubject, ';');
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create or replace function pureIdentifier(rawIdentifier varchar)
|
||||||
|
returns varchar
|
||||||
|
returns null on null input
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
return regexp_replace(rawIdentifier, '\W+', '');
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create or replace function findObjectUuidByIdName(objectTable varchar, objectIdName varchar)
|
||||||
|
returns uuid
|
||||||
|
returns null on null input
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
sql varchar;
|
||||||
|
uuid uuid;
|
||||||
|
begin
|
||||||
|
objectTable := pureIdentifier(objectTable);
|
||||||
|
objectIdName := pureIdentifier(objectIdName);
|
||||||
|
sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName);
|
||||||
|
begin
|
||||||
|
raise notice 'sql: %', sql;
|
||||||
|
execute sql into uuid;
|
||||||
|
exception
|
||||||
|
when others then
|
||||||
|
raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable;
|
||||||
|
end;
|
||||||
|
return uuid;
|
||||||
|
end ; $$;
|
||||||
|
|
||||||
|
create or replace function findIdNameByObjectUuid(objectTable varchar, objectUuid uuid)
|
||||||
|
returns varchar
|
||||||
|
returns null on null input
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
sql varchar;
|
||||||
|
idName varchar;
|
||||||
|
begin
|
||||||
|
objectTable := pureIdentifier(objectTable);
|
||||||
|
sql := format('select * from %sIdNameByUuid(%L::uuid);', objectTable, objectUuid);
|
||||||
|
begin
|
||||||
|
raise notice 'sql: %', sql;
|
||||||
|
execute sql into idName;
|
||||||
|
exception
|
||||||
|
when others then
|
||||||
|
raise exception 'function %IdNameByUuid(...) not found, add identity view support for table %', objectTable, objectTable;
|
||||||
|
end;
|
||||||
|
return idName;
|
||||||
|
end ; $$;
|
||||||
|
|
||||||
|
create or replace function currentSubjects()
|
||||||
|
returns varchar(63)[]
|
||||||
|
stable leakproof
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
assumedRoles varchar(63)[];
|
||||||
|
begin
|
||||||
|
assumedRoles := assumedRoles();
|
||||||
|
if array_length(assumedRoles(), 1) > 0 then
|
||||||
|
return assumedRoles();
|
||||||
|
else
|
||||||
|
return array [currentUser()]::varchar(63)[];
|
||||||
|
end if;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create or replace function currentSubjectIds()
|
||||||
|
returns uuid[]
|
||||||
|
stable leakproof
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
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();
|
||||||
|
if currentUserId is null then
|
||||||
|
raise exception 'user % does not exist', currentUser();
|
||||||
|
end if;
|
||||||
|
|
||||||
|
roleNames := assumedRoles();
|
||||||
|
if cardinality(roleNames) = 0 then
|
||||||
|
return array [currentUserId];
|
||||||
|
end if;
|
||||||
|
|
||||||
|
raise notice 'assuming roles: %', roleNames;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
||||||
|
|
||||||
|
-- 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(), currentUserId, roleName, roleUuidToAssume;
|
||||||
|
end if;
|
||||||
|
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
return roleIdsToAssume;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
@ -0,0 +1,91 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-views-ROLE-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Creates a view to the role table with row-level limitation
|
||||||
|
based on the grants of the current user or assumed roles.
|
||||||
|
*/
|
||||||
|
drop view if exists rbacrole_rv;
|
||||||
|
create or replace view rbacrole_rv as
|
||||||
|
select DISTINCT r.*, o.objectTable,
|
||||||
|
findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName
|
||||||
|
from rbacrole as r
|
||||||
|
join rbacobject as o on o.uuid=r.objectuuid
|
||||||
|
where isGranted(currentSubjectIds(), r.uuid);
|
||||||
|
grant all privileges on rbacrole_rv to restricted;
|
||||||
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-views-USER-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Creates a view to the users table with row-level limitation
|
||||||
|
based on the grants of the current user or assumed roles.
|
||||||
|
*/
|
||||||
|
drop view if exists RbacUser_rv;
|
||||||
|
create or replace view RbacUser_rv as
|
||||||
|
select u.*
|
||||||
|
from RbacUser as u
|
||||||
|
join RbacGrants as g on g.ascendantuuid = u.uuid
|
||||||
|
join rbacrole_rv as r on r.uuid = g.descendantuuid;
|
||||||
|
grant all privileges on RbacUser_rv to restricted;
|
||||||
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-views-OWN-GRANTED-PERMISSIONS-VIEW:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Creates a view to all permissions granted to the current user or
|
||||||
|
based on the grants of the current user or assumed roles.
|
||||||
|
*/
|
||||||
|
-- @formatter:off
|
||||||
|
drop view if exists RbacOwnGrantedPermissions_rv;
|
||||||
|
create or replace view RbacOwnGrantedPermissions_rv as
|
||||||
|
select r.uuid as roleuuid, p.uuid as permissionUuid,
|
||||||
|
(r.objecttable || '#' || r.objectidname || '.' || r.roletype) as roleName, p.op,
|
||||||
|
o.objecttable, r.objectidname, o.uuid as objectuuid
|
||||||
|
from rbacrole_rv r
|
||||||
|
join rbacgrants g on g.ascendantuuid = r.uuid
|
||||||
|
join rbacpermission p on p.uuid = g.descendantuuid
|
||||||
|
join rbacobject o on o.uuid = p.objectuuid;
|
||||||
|
grant all privileges on RbacOwnGrantedPermissions_rv to restricted;
|
||||||
|
-- @formatter:om
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-views-GRANTED-PERMISSIONS:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Returns all permissions granted to the given user,
|
||||||
|
which are also visible to the current user or assumed roles.
|
||||||
|
*/
|
||||||
|
create or replace function grantedPermissions(userName varchar)
|
||||||
|
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, objectTable varchar, objectIdName varchar, objectUuid uuid)
|
||||||
|
returns null on null input
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
-- @formatter:off
|
||||||
|
if cardinality(assumedRoles()) > 0 then
|
||||||
|
raise exception 'grantedPermissions(...) does not support assumed roles';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
return query select
|
||||||
|
xp.roleUuid,
|
||||||
|
(xp.objecttable || '#' || xp.objectidname || '.' || xp.roletype) as roleName,
|
||||||
|
xp.permissionUuid, xp.op, xp.objecttable, xp.objectIdName, xp.objectuuid
|
||||||
|
from (select
|
||||||
|
r.uuid as roleUuid, r.roletype,
|
||||||
|
p.uuid as permissionUuid, p.op, o.objecttable,
|
||||||
|
findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName,
|
||||||
|
o.uuid as objectuuid
|
||||||
|
from queryPermissionsGrantedToSubjectId( findRbacUserId(userName)) p
|
||||||
|
join rbacgrants g on g.descendantuuid = p.uuid
|
||||||
|
join rbacobject o on o.uuid = p.objectuuid
|
||||||
|
join rbacrole r on r.uuid = g.ascendantuuid
|
||||||
|
where isGranted(currentUserId(), r.uuid)
|
||||||
|
) xp;
|
||||||
|
-- @formatter:on
|
||||||
|
end; $$;
|
@ -168,6 +168,7 @@ $$;
|
|||||||
*/
|
*/
|
||||||
create or replace function packageIdNameByUuid(uuid uuid)
|
create or replace function packageIdNameByUuid(uuid uuid)
|
||||||
returns varchar
|
returns varchar
|
||||||
|
stable leakproof
|
||||||
language sql
|
language sql
|
||||||
strict as $$
|
strict as $$
|
||||||
select idName from package_iv iv where iv.uuid = packageIdNameByUuid.uuid;
|
select idName from package_iv iv where iv.uuid = packageIdNameByUuid.uuid;
|
||||||
|
@ -9,6 +9,10 @@ databaseChangeLog:
|
|||||||
file: db/changelog/2022-07-28-004-uuid-ossp-extension.sql
|
file: db/changelog/2022-07-28-004-uuid-ossp-extension.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/2022-07-28-005-rbac-base.sql
|
file: db/changelog/2022-07-28-005-rbac-base.sql
|
||||||
|
- include:
|
||||||
|
file: db/changelog/2022-07-28-006-rbac-current.sql
|
||||||
|
- include:
|
||||||
|
file: db/changelog/2022-07-28-007-rbac-views.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/2022-07-28-020-rbac-role-builder.sql
|
file: db/changelog/2022-07-28-020-rbac-role-builder.sql
|
||||||
- include:
|
- include:
|
||||||
|
Loading…
Reference in New Issue
Block a user