assuming-long-roleidnames + object-uuid-based-rolenames (#139)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/139 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
@ -58,7 +58,7 @@ public class Context {
|
||||
cast(:currentTask as varchar(127)),
|
||||
cast(:currentRequest as text),
|
||||
cast(:currentSubject as varchar(63)),
|
||||
cast(:assumedRoles as varchar(1023)));
|
||||
cast(:assumedRoles as text));
|
||||
""");
|
||||
query.setParameter("currentTask", shortenToMaxLength(currentTask, 127));
|
||||
query.setParameter("currentRequest", currentRequest);
|
||||
|
@ -42,22 +42,22 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
|
||||
toSqlLikeOperand(mark), toSqlLikeOperand(personData), toSqlLikeOperand(contactData));
|
||||
}
|
||||
|
||||
// TODO: use ELIKE instead of lower(...) LIKE ...? Or use jsonb_path with RegEx like emailAddressRegEx in ContactRepo?
|
||||
// TODO: Or use jsonb_path with RegEx like emailAddressRegEx in ContactRepo?
|
||||
@Query(value = """
|
||||
SELECT rel FROM HsOfficeRelationRbacEntity AS rel
|
||||
WHERE (:relationType IS NULL OR CAST(rel.type AS String) = :relationType)
|
||||
AND ( :personUuid IS NULL
|
||||
OR rel.anchor.uuid = :personUuid OR rel.holder.uuid = :personUuid )
|
||||
AND ( :mark IS NULL OR lower(rel.mark) LIKE :mark )
|
||||
AND ( :mark IS NULL OR rel.mark ILIKE :mark )
|
||||
AND ( :personData IS NULL
|
||||
OR lower(rel.anchor.tradeName) LIKE :personData OR lower(rel.holder.tradeName) LIKE :personData
|
||||
OR lower(rel.anchor.familyName) LIKE :personData OR lower(rel.holder.familyName) LIKE :personData
|
||||
OR lower(rel.anchor.givenName) LIKE :personData OR lower(rel.holder.givenName) LIKE :personData )
|
||||
OR rel.anchor.tradeName ILIKE :personData OR rel.holder.tradeName ILIKE :personData
|
||||
OR rel.anchor.familyName ILIKE :personData OR rel.holder.familyName ILIKE :personData
|
||||
OR rel.anchor.givenName ILIKE :personData OR rel.holder.givenName ILIKE :personData )
|
||||
AND ( :contactData IS NULL
|
||||
OR lower(rel.contact.caption) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.postalAddress AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.emailAddresses AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
|
||||
OR rel.contact.caption ILIKE :contactData
|
||||
OR CAST(rel.contact.postalAddress AS String) ILIKE :contactData
|
||||
OR CAST(rel.contact.emailAddresses AS String) ILIKE :contactData
|
||||
OR CAST(rel.contact.phoneNumbers AS String) ILIKE :contactData )
|
||||
""")
|
||||
@Timed("app.office.relations.repo.findRelationRelatedToPersonUuidRelationByTypeMarkPersonAndContactDataImpl.rbac")
|
||||
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidRelationByTypeMarkPersonAndContactDataImpl(
|
||||
|
@ -14,24 +14,58 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
|
||||
@Timed("app.repo.relations.findByUuid.real")
|
||||
Optional<HsOfficeRelationRealEntity> findByUuid(UUID id);
|
||||
|
||||
default List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
|
||||
return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType == null ? null : relationType.toString());
|
||||
}
|
||||
|
||||
@Query(value = """
|
||||
SELECT p.* FROM hs_office.relation AS p
|
||||
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
|
||||
""", nativeQuery = true)
|
||||
""", nativeQuery = true)
|
||||
@Timed("app.repo.relations.findRelationRelatedToPersonUuid.real")
|
||||
List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
|
||||
|
||||
/**
|
||||
* Finds relations by a conjunction of optional criteria, including anchorPerson, holderPerson and contact data.
|
||||
* *
|
||||
* @param personUuid the optional UUID of the anchorPerson or holderPerson
|
||||
* @param relationType the type of the relation
|
||||
* @param mark the mark (use '%' for wildcard), case ignored
|
||||
* @param personData a string to match the persons tradeName, familyName or givenName (use '%' for wildcard), case ignored
|
||||
* @param contactData a string to match the contacts caption, postalAddress, emailAddresses or phoneNumbers (use '%' for wildcard), case ignored
|
||||
* @return a list of (accessible) relations which match all given criteria
|
||||
*/
|
||||
default List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidRelationTypeMarkPersonAndContactData(
|
||||
final UUID personUuid,
|
||||
final HsOfficeRelationType relationType,
|
||||
final String mark,
|
||||
final String personData,
|
||||
final String contactData) {
|
||||
return findRelationRelatedToPersonUuidRelationByTypeMarkPersonAndContactDataImpl(
|
||||
personUuid, toStringOrNull(relationType),
|
||||
toSqlLikeOperand(mark), toSqlLikeOperand(personData), toSqlLikeOperand(contactData));
|
||||
}
|
||||
|
||||
// TODO: Or use jsonb_path with RegEx like emailAddressRegEx in ContactRepo?
|
||||
@Query(value = """
|
||||
SELECT p.* FROM hs_office.relation AS p
|
||||
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS hs_office.RelationType))
|
||||
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
|
||||
""", nativeQuery = true)
|
||||
@Timed("app.repo.relations.findRelationRelatedToPersonUuidAndRelationTypeString.real")
|
||||
List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
|
||||
SELECT rel FROM HsOfficeRelationRealEntity AS rel
|
||||
WHERE (:relationType IS NULL OR CAST(rel.type AS String) = :relationType)
|
||||
AND ( :personUuid IS NULL
|
||||
OR rel.anchor.uuid = :personUuid OR rel.holder.uuid = :personUuid )
|
||||
AND ( :mark IS NULL OR rel.mark ILIKE :mark )
|
||||
AND ( :personData IS NULL
|
||||
OR rel.anchor.tradeName ILIKE :personData OR rel.holder.tradeName ILIKE :personData
|
||||
OR rel.anchor.familyName ILIKE :personData OR rel.holder.familyName ILIKE :personData
|
||||
OR rel.anchor.givenName ILIKE :personData OR rel.holder.givenName ILIKE :personData )
|
||||
AND ( :contactData IS NULL
|
||||
OR rel.contact.caption ILIKE :contactData
|
||||
OR CAST(rel.contact.postalAddress AS String) ILIKE :contactData
|
||||
OR CAST(rel.contact.emailAddresses AS String) ILIKE :contactData
|
||||
OR CAST(rel.contact.phoneNumbers AS String) ILIKE :contactData )
|
||||
""")
|
||||
@Timed("app.office.relations.repo.findRelationRelatedToPersonUuidRelationByTypeMarkPersonAndContactDataImpl.real")
|
||||
List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidRelationByTypeMarkPersonAndContactDataImpl(
|
||||
final UUID personUuid,
|
||||
final String relationType,
|
||||
final String mark,
|
||||
final String personData,
|
||||
final String contactData);
|
||||
|
||||
@Timed("app.repo.relations.save.real")
|
||||
HsOfficeRelationRealEntity save(final HsOfficeRelationRealEntity entity);
|
||||
@ -41,4 +75,11 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
|
||||
|
||||
@Timed("app.repo.relations.deleteByUuid.real")
|
||||
int deleteByUuid(UUID uuid);
|
||||
private static String toSqlLikeOperand(final String text) {
|
||||
return text == null ? null : ("%" + text.toLowerCase() + "%");
|
||||
}
|
||||
|
||||
private static String toStringOrNull(final HsOfficeRelationType relationType) {
|
||||
return relationType == null ? null : relationType.name();
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ create procedure base.contextDefined(
|
||||
currentTask varchar(127),
|
||||
currentRequest text,
|
||||
currentSubject varchar(63),
|
||||
assumedRoles varchar(1023)
|
||||
assumedRoles varchar(4096)
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
@ -26,7 +26,7 @@ create or replace procedure base.defineContext(
|
||||
currentTask varchar(127),
|
||||
currentRequest text = null,
|
||||
currentSubject varchar(63) = null,
|
||||
assumedRoles varchar(1023) = null
|
||||
assumedRoles text = null
|
||||
)
|
||||
language plpgsql as $$
|
||||
begin
|
||||
@ -43,7 +43,7 @@ begin
|
||||
execute format('set local hsadminng.currentSubject to %L', currentSubject);
|
||||
|
||||
assumedRoles := coalesce(assumedRoles, '');
|
||||
assert length(assumedRoles) <= 1023, FORMAT('assumedRoles must not be longer than 1023 characters: "%s"', assumedRoles);
|
||||
assert length(assumedRoles) <= 4096, FORMAT('assumedRoles must not be longer than 4096 characters: "%s"', assumedRoles);
|
||||
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
|
||||
|
||||
call base.contextDefined(currentTask, currentRequest, currentSubject, assumedRoles);
|
||||
|
@ -251,9 +251,14 @@ begin
|
||||
execute sql into uuid;
|
||||
exception
|
||||
when others then
|
||||
raise exception 'function %_uuid_by_id_name(...) not found, add identity view support for table %', objectTable, objectTable;
|
||||
raise exception 'function %_uuid_by_id_name(''%'') failed: %, SQLSTATE: %. If it could not be found, add identity view support to %\nSQL:%',
|
||||
objectTable, objectIdName, SQLERRM, SQLSTATE, objectTable, sql;
|
||||
end;
|
||||
return uuid;
|
||||
if uuid is null then
|
||||
raise exception 'SQL returned null: %', sql;
|
||||
else
|
||||
return uuid;
|
||||
end if;
|
||||
end ; $$;
|
||||
|
||||
create or replace function rbac.findIdNameByObjectUuid(objectTable varchar, objectUuid uuid)
|
||||
@ -270,7 +275,8 @@ begin
|
||||
execute sql into idName;
|
||||
exception
|
||||
when others then
|
||||
raise exception 'function %_id_name_by_uuid(...) not found, add identity view support for table %', objectTable, objectTable;
|
||||
raise exception 'function %_id_name_by_uuid(''%'') failed: %, SQLSTATE: %. If it could not be found, add identity view support to %',
|
||||
objectTable, objectUuid, SQLERRM, SQLSTATE, objectTable;
|
||||
end;
|
||||
return idName;
|
||||
end ; $$;
|
||||
|
@ -23,7 +23,7 @@ begin
|
||||
return currentSubjectUuid;
|
||||
end; $$;
|
||||
|
||||
create or replace function rbac.determinecurrentsubjectorassumedrolesuuids(currentSubjectOrAssumedRolesUuids uuid, assumedRoles varchar)
|
||||
create or replace function rbac.determinecurrentsubjectorassumedrolesuuids(currentSubjectOrAssumedRolesUuids uuid, assumedRoles text)
|
||||
returns uuid[]
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
@ -31,7 +31,7 @@ declare
|
||||
roleName text;
|
||||
roleNameParts text;
|
||||
objectTableToAssume varchar(63);
|
||||
objectNameToAssume varchar(63);
|
||||
objectNameToAssume varchar(1024); -- e.g. for relation: 2*(96+48+48)+length('-with-REPRESENTATIVE-') = 405
|
||||
objectUuidToAssume uuid;
|
||||
roleTypeToAssume rbac.RoleType;
|
||||
roleIdsToAssume uuid[];
|
||||
@ -55,7 +55,12 @@ begin
|
||||
objectNameToAssume = split_part(roleNameParts, '#', 2);
|
||||
roleTypeToAssume = split_part(roleNameParts, '#', 3);
|
||||
|
||||
objectUuidToAssume = rbac.findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
||||
begin
|
||||
objectUuidToAssume = objectNameToAssume::uuid;
|
||||
exception when invalid_text_representation then
|
||||
objectUuidToAssume = rbac.findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
|
||||
end;
|
||||
|
||||
if objectUuidToAssume is null then
|
||||
raise exception '[401] object % cannot be found in table % (from roleNameParts=%)', objectNameToAssume, objectTableToAssume, roleNameParts;
|
||||
end if;
|
||||
@ -88,7 +93,7 @@ create or replace procedure base.contextDefined(
|
||||
currentTask varchar(127),
|
||||
currentRequest text,
|
||||
currentSubject varchar(63),
|
||||
assumedRoles varchar(1023)
|
||||
assumedRoles varchar(4096)
|
||||
)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
@ -104,7 +109,7 @@ begin
|
||||
|
||||
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
|
||||
execute format('set local hsadminng.currentSubjectOrAssumedRolesUuids to %L',
|
||||
(select array_to_string(rbac.determinecurrentsubjectorassumedrolesuuids(currentSubjectUuid, assumedRoles), ';')));
|
||||
(select array_to_string(rbac.determineCurrentSubjectOrAssumedRolesUuids(currentSubjectUuid, assumedRoles), ';')));
|
||||
|
||||
raise notice 'Context defined as: %, %, %, [%]', currentTask, currentRequest, currentSubject, assumedRoles;
|
||||
end; $$;
|
||||
|
@ -45,7 +45,8 @@ do language plpgsql $$
|
||||
call hs_office.person_create_test_data('NP', null, 'Smith', 'Peter');
|
||||
call hs_office.person_create_test_data('NP', null, 'Tucker', 'Jack');
|
||||
call hs_office.person_create_test_data('NP', null, 'Fouler', 'Ellie');
|
||||
call hs_office.person_create_test_data('LP', 'Second e.K.', 'Smith', 'Peter');
|
||||
-- the next tradeName is deliberately 63 chars in length, the max length for that field, also to test long rbac-role names
|
||||
call hs_office.person_create_test_data('LP', 'Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'Smith', 'Peter');
|
||||
call hs_office.person_create_test_data('IF', 'Third OHG');
|
||||
call hs_office.person_create_test_data('LP', 'Fourth eG');
|
||||
call hs_office.person_create_test_data('UF', 'Erben Bessler', 'Mel', 'Bessler');
|
||||
|
@ -91,9 +91,9 @@ do language plpgsql $$
|
||||
call hs_office.relation_create_test_data('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
|
||||
call hs_office.relation_create_test_data('First GmbH', 'DEBITOR', 'First GmbH', 'first contact');
|
||||
|
||||
call hs_office.relation_create_test_data('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
|
||||
call hs_office.relation_create_test_data('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
|
||||
call hs_office.relation_create_test_data('Second e.K.', 'DEBITOR', 'Second e.K.', 'second contact');
|
||||
call hs_office.relation_create_test_data('Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
|
||||
call hs_office.relation_create_test_data('Smith', 'REPRESENTATIVE', 'Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'second contact');
|
||||
call hs_office.relation_create_test_data('Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'DEBITOR', 'Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'second contact');
|
||||
|
||||
call hs_office.relation_create_test_data('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
|
||||
call hs_office.relation_create_test_data('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
||||
|
@ -74,7 +74,7 @@ do language plpgsql $$
|
||||
call base.defineContext('creating partner test-data ', null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10001, 'First GmbH', 'first contact');
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10002, 'Second e.K.', 'second contact');
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10002, 'Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'second contact');
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10003, 'Third OHG', 'third contact');
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10004, 'Fourth eG', 'fourth contact');
|
||||
call hs_office.partner_create_test_data('Hostsharing eG', 10010, 'Smith', 'fifth contact');
|
||||
|
@ -13,7 +13,7 @@ create or replace procedure hs_office.bankaccount_create_test_data(givenHolder v
|
||||
declare
|
||||
emailAddr varchar;
|
||||
begin
|
||||
emailAddr = 'bankaccount-admin@' || base.cleanIdentifier(givenHolder) || '.example.com';
|
||||
emailAddr = 'bankaccount-admin@' || TRIM(SUBSTRING(base.cleanIdentifier(givenHolder) FOR 32)) || '.example.com';
|
||||
perform rbac.create_subject(emailAddr);
|
||||
call base.defineContext('creating bankaccount test-data', null, emailAddr);
|
||||
|
||||
@ -36,7 +36,7 @@ do language plpgsql $$
|
||||
-- IBANs+BICs taken from https://ibanvalidieren.de/beispiele.html
|
||||
call hs_office.bankaccount_create_test_data('First GmbH', 'DE02120300000000202051', 'BYLADEM1001');
|
||||
call hs_office.bankaccount_create_test_data('Peter Smith', 'DE02500105170137075030', 'INGDDEFF');
|
||||
call hs_office.bankaccount_create_test_data('Second e.K.', 'DE02100500000054540402', 'BELADEBE');
|
||||
call hs_office.bankaccount_create_test_data('Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'DE02100500000054540402', 'BELADEBE');
|
||||
call hs_office.bankaccount_create_test_data('Third OHG', 'DE02300209000106531065', 'CMCIDEDD');
|
||||
call hs_office.bankaccount_create_test_data('Fourth eG', 'DE02200505501015871393', 'HASPDEHH');
|
||||
call hs_office.bankaccount_create_test_data('Mel Bessler', 'DE02100100100006820101', 'PBNKDEFF');
|
||||
|
@ -53,7 +53,7 @@ do language plpgsql $$
|
||||
call base.defineContext('creating debitor test-data', null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||
|
||||
call hs_office.debitor_create_test_data(11, 'First GmbH', 'first contact', 'fir');
|
||||
call hs_office.debitor_create_test_data(12, 'Second e.K.', 'second contact', 'sec');
|
||||
call hs_office.debitor_create_test_data(12, 'Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.', 'second contact', 'sec');
|
||||
call hs_office.debitor_create_test_data(13, 'Third OHG', 'third contact', 'thi');
|
||||
end;
|
||||
$$;
|
||||
|
Reference in New Issue
Block a user