diff --git a/common/taskana-common-data/src/main/resources/sql/clear/clear-db.sql b/common/taskana-common-data/src/main/resources/sql/clear/clear-db.sql index 3221bb90a2..1c9ad0127b 100644 --- a/common/taskana-common-data/src/main/resources/sql/clear/clear-db.sql +++ b/common/taskana-common-data/src/main/resources/sql/clear/clear-db.sql @@ -14,5 +14,6 @@ DELETE FROM OBJECT_REFERENCE; DELETE FROM SCHEDULED_JOB; DELETE FROM USER_INFO; DELETE FROM GROUP_INFO; +DELETE FROM PERMISSION_INFO; INSERT INTO CONFIGURATION (NAME) VALUES ('MASTER'); COMMIT; diff --git a/common/taskana-common-data/src/main/resources/sql/clear/drop-tables.sql b/common/taskana-common-data/src/main/resources/sql/clear/drop-tables.sql index 4c95dd9c7f..447674060a 100644 --- a/common/taskana-common-data/src/main/resources/sql/clear/drop-tables.sql +++ b/common/taskana-common-data/src/main/resources/sql/clear/drop-tables.sql @@ -14,6 +14,7 @@ DROP TABLE OBJECT_REFERENCE; DROP TABLE SCHEDULED_JOB; DROP TABLE USER_INFO; DROP TABLE GROUP_INFO; +DROP TABLE PERMISSION_INFO; DROP SEQUENCE SCHEDULED_JOB_SEQ; DROP SEQUENCE TASKANA_SCHEMA_VERSION_ID_SEQ; COMMIT; diff --git a/common/taskana-common/src/main/resources/sql/db2/taskana-schema-db2.sql b/common/taskana-common/src/main/resources/sql/db2/taskana-schema-db2.sql index 61b2069f37..8e5d7b27a8 100644 --- a/common/taskana-common/src/main/resources/sql/db2/taskana-schema-db2.sql +++ b/common/taskana-common/src/main/resources/sql/db2/taskana-schema-db2.sql @@ -357,6 +357,13 @@ CREATE TABLE GROUP_INFO PRIMARY KEY (USER_ID, GROUP_ID) ); +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NUll, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); + CREATE SEQUENCE SCHEDULED_JOB_SEQ MINVALUE 1 START WITH 1 diff --git a/common/taskana-common/src/main/resources/sql/db2/taskana_schema_update_6.2.0_to_6.4.0_db2.sql b/common/taskana-common/src/main/resources/sql/db2/taskana_schema_update_6.2.0_to_6.4.0_db2.sql new file mode 100644 index 0000000000..2186dafacf --- /dev/null +++ b/common/taskana-common/src/main/resources/sql/db2/taskana_schema_update_6.2.0_to_6.4.0_db2.sql @@ -0,0 +1,12 @@ +-- this script updates the TASKANA database schema from version 6.2.0 to version 6.4.0. +SET SCHEMA %schemaName%; + +INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED) +VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.4.0', CURRENT_TIMESTAMP); + +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NULL, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); diff --git a/common/taskana-common/src/main/resources/sql/h2/taskana-schema-h2.sql b/common/taskana-common/src/main/resources/sql/h2/taskana-schema-h2.sql index 0402ec4a34..a3a8b83626 100644 --- a/common/taskana-common/src/main/resources/sql/h2/taskana-schema-h2.sql +++ b/common/taskana-common/src/main/resources/sql/h2/taskana-schema-h2.sql @@ -363,6 +363,13 @@ CREATE TABLE GROUP_INFO PRIMARY KEY (USER_ID, GROUP_ID) ); +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NULL, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); + CREATE SEQUENCE SCHEDULED_JOB_SEQ MINVALUE 1 START WITH 1 diff --git a/common/taskana-common/src/main/resources/sql/h2/taskana_schema_update_6.2.0_to_6.4.0_h2.sql b/common/taskana-common/src/main/resources/sql/h2/taskana_schema_update_6.2.0_to_6.4.0_h2.sql new file mode 100644 index 0000000000..d6df288dc5 --- /dev/null +++ b/common/taskana-common/src/main/resources/sql/h2/taskana_schema_update_6.2.0_to_6.4.0_h2.sql @@ -0,0 +1,11 @@ +-- this script updates the TASKANA database schema from version 6.2.0 to version 6.4.0. + +INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED) +VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.4.0', CURRENT_TIMESTAMP); + +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NULL, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); diff --git a/common/taskana-common/src/main/resources/sql/oracle/taskana-schema-oracle.sql b/common/taskana-common/src/main/resources/sql/oracle/taskana-schema-oracle.sql index 4248be6872..47a1a582f0 100644 --- a/common/taskana-common/src/main/resources/sql/oracle/taskana-schema-oracle.sql +++ b/common/taskana-common/src/main/resources/sql/oracle/taskana-schema-oracle.sql @@ -356,6 +356,13 @@ CREATE TABLE GROUP_INFO CONSTRAINT GROUP_INFO_PKEY PRIMARY KEY (USER_ID, GROUP_ID) ); +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR2(32) NOT NULL, + PERMISSION_ID VARCHAR2(256) NOT NULL, + CONSTRAINT PERMISSION_INFO_PKEY PRIMARY KEY (USER_ID, PERMISSION_ID) +); + CREATE SEQUENCE SCHEDULED_JOB_SEQ START WITH 1 INCREMENT BY 1 diff --git a/common/taskana-common/src/main/resources/sql/oracle/taskana_schema_update_6.2.0_to_6.4.0_oracle.sql b/common/taskana-common/src/main/resources/sql/oracle/taskana_schema_update_6.2.0_to_6.4.0_oracle.sql new file mode 100644 index 0000000000..09aed9fa2d --- /dev/null +++ b/common/taskana-common/src/main/resources/sql/oracle/taskana_schema_update_6.2.0_to_6.4.0_oracle.sql @@ -0,0 +1,12 @@ +-- this script updates the TASKANA database schema from version 6.2.0 to version 6.4.0. +ALTER SESSION SET CURRENT_SCHEMA = %schemaName%; + +INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED) +VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.4.0', CURRENT_TIMESTAMP); + +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR2(32) NOT NULL, + PERMISSION_ID VARCHAR2(256) NOT NULL, + CONSTRAINT PERMISSION_INFO_PKEY PRIMARY KEY (USER_ID, PERMISSION_ID) +); diff --git a/common/taskana-common/src/main/resources/sql/postgres/taskana-schema-postgres.sql b/common/taskana-common/src/main/resources/sql/postgres/taskana-schema-postgres.sql index de19e76c5c..a58e4c26ce 100644 --- a/common/taskana-common/src/main/resources/sql/postgres/taskana-schema-postgres.sql +++ b/common/taskana-common/src/main/resources/sql/postgres/taskana-schema-postgres.sql @@ -360,6 +360,13 @@ CREATE TABLE GROUP_INFO PRIMARY KEY (USER_ID, GROUP_ID) ); +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NULL, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); + CREATE SEQUENCE SCHEDULED_JOB_SEQ MINVALUE 1 START WITH 1 diff --git a/common/taskana-common/src/main/resources/sql/postgres/taskana_schema_update_6.2.0_to_6.4.0_postgres.sql b/common/taskana-common/src/main/resources/sql/postgres/taskana_schema_update_6.2.0_to_6.4.0_postgres.sql new file mode 100644 index 0000000000..0612211661 --- /dev/null +++ b/common/taskana-common/src/main/resources/sql/postgres/taskana_schema_update_6.2.0_to_6.4.0_postgres.sql @@ -0,0 +1,13 @@ +-- this script updates the TASKANA database schema from version 6.2.0 to version 6.4.0. + +SET search_path = %schemaName%; + +INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED) +VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.4.0', CURRENT_TIMESTAMP); + +CREATE TABLE PERMISSION_INFO +( + USER_ID VARCHAR(32) NOT NULL, + PERMISSION_ID VARCHAR(256) NOT NULL, + PRIMARY KEY (USER_ID, PERMISSION_ID) +); diff --git a/history/taskana-simplehistory-rest-spring/src/test/resources/application.properties b/history/taskana-simplehistory-rest-spring/src/test/resources/application.properties index 5340f95115..7866939c93 100644 --- a/history/taskana-simplehistory-rest-spring/src/test/resources/application.properties +++ b/history/taskana-simplehistory-rest-spring/src/test/resources/application.properties @@ -26,6 +26,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/lib/taskana-core-test/src/test/java/acceptance/user/UserServiceAccTest.java b/lib/taskana-core-test/src/test/java/acceptance/user/UserServiceAccTest.java index 0aab0b5b61..610eee2255 100644 --- a/lib/taskana-core-test/src/test/java/acceptance/user/UserServiceAccTest.java +++ b/lib/taskana-core-test/src/test/java/acceptance/user/UserServiceAccTest.java @@ -202,6 +202,59 @@ void should_DetermineDomains_When_WorkbasketPermissionsExistForUsers() throws Ex domains -> Set.of(workbasketDomainB.getDomain()).equals(domains), "DOMAIN_B")); } + @WithAccessId(user = "user-1-1") + @Test + void should_DetermineDomains_When_WorkbasketPermissionsExistForUsersWithPermissions() + throws Exception { + Workbasket workbasketDomainA = + defaultTestWorkbasket().buildAndStore(workbasketService, "businessadmin"); + createAccessItem( + "permissions-domaina", + workbasketDomainA, + WorkbasketPermission.READ, + WorkbasketPermission.OPEN); + Workbasket workbasketDomainB = + defaultTestWorkbasket() + .domain("DOMAIN_B") + .buildAndStore(workbasketService, "businessadmin"); + createAccessItem( + "permissions-domainb", + workbasketDomainB, + WorkbasketPermission.READ, + WorkbasketPermission.OPEN); + Set users = new HashSet<>(); + for (int i = 0; i < 6; i++) { + users.add( + randomTestUser() + .permissions(Set.of("test1", "test2", "permissions-domaina")) + .buildAndStore(userService, "businessadmin")); + } + for (int i = 0; i < 4; i++) { + users.add( + randomTestUser() + .permissions(Set.of("test1", "test2", "permissions-domainb")) + .buildAndStore(userService, "businessadmin")); + } + Set userIds = users.stream().map(User::getId).collect(Collectors.toSet()); + + List returnedUsers = userService.getUsers(userIds); + + assertThat(returnedUsers) + .extracting(User::getDomains) + .areExactly( + 6, + new Condition<>( + domains -> + Set.of(workbasketDomainA.getDomain()) + .equals(domains), "DOMAIN_A")) + .areExactly( + 4, + new Condition<>( + domains -> + Set.of(workbasketDomainB.getDomain()) + .equals(domains), "DOMAIN_B")); + } + @WithAccessId(user = "user-1-1") @Test void should_ReturnAllUsers_When_TryingToGetUsersWithIdsExistingInCaps() throws Exception { @@ -312,6 +365,20 @@ void should_InsertUserInDatabase_When_CreatingUserWithGroups() throws Exception assertThat(userToCreate).isNotSameAs(userInDatabase).isEqualTo(userInDatabase); } + @WithAccessId(user = "businessadmin") + @Test + void should_InsertUserInDatabase_When_CreatingUserWithPermissions() throws Exception { + User userToCreate = userService.newUser(); + userToCreate.setId("anton4"); + userToCreate.setFirstName("Anton"); + userToCreate.setLastName("Miller"); + userToCreate.setPermissions(Set.of("perm1", "perm2")); + userService.createUser(userToCreate); + + User userInDatabase = userService.getUser(userToCreate.getId()); + assertThat(userToCreate).isNotSameAs(userInDatabase).isEqualTo(userInDatabase); + } + @WithAccessId(user = "businessadmin") @TestFactory Stream @@ -483,12 +550,15 @@ void should_MakeAccessIdsLowerCase_When_ConfigurationPropertyIsSet() throws Exce userToCreate.setFirstName("firstName"); userToCreate.setLastName("lastName"); userToCreate.setGroups(Set.of("GROUP1-ID-WITH-CAPS", "Group2-Id-With-Caps")); + userToCreate.setPermissions(Set.of("PERMISSION1-ID-WITH-CAPS", "Permission2-Id-With-Caps")); User userInDatabase = userService.createUser(userToCreate); assertThat(userInDatabase.getId()).isEqualTo("user-id-with-caps"); assertThat(userInDatabase.getGroups()) .containsExactlyInAnyOrder("group1-id-with-caps", "group2-id-with-caps"); + assertThat(userInDatabase.getPermissions()) + .containsExactlyInAnyOrder("permission1-id-with-caps", "permission2-id-with-caps"); } } @@ -549,6 +619,36 @@ Stream should_UpdateGroups() { return DynamicTest.stream(testCases, Triplet::getLeft, test); } + @WithAccessId(user = "businessadmin") + @TestFactory + Stream should_UpdatePermissions() { + Stream, Set>> testCases = + Stream.of( + Triplet.of( + "User has no permissions before updating", Set.of(), Set.of("perm1", "perm2")), + Triplet.of("new permissions differ all", Set.of("perm1"), Set.of("perm2", "perm3")), + Triplet.of("some new permissions differ", Set.of("perm1"), Set.of("perm1", "perm2")), + Triplet.of("new permissions are all the same", Set.of("perm1"), Set.of("perm1"))); + + ThrowingConsumer, Set>> test = + t -> { + Set existingPerms = t.getMiddle(); + Set newPerms = t.getMiddle(); + User userToUpdate = randomTestUser() + .permissions(existingPerms) + .buildAndStore(userService); + + userToUpdate.setPermissions(newPerms); + userService.updateUser(userToUpdate); + + User userInDatabase = userService.getUser(userToUpdate.getId()); + assertThat(userInDatabase.getPermissions()) + .containsExactlyInAnyOrderElementsOf(newPerms); + }; + + return DynamicTest.stream(testCases, Triplet::getLeft, test); + } + @WithAccessId(user = "user-1-1") @Test void should_ThrowNotAuthorizedException_When_TryingToUpdateUserWithNoAdminRole() @@ -722,6 +822,19 @@ void should_MakeGroupIdsLowerCase_When_UpdatingUserWithUpperCaseGroups() throws .containsExactlyInAnyOrder("group1-id-with-caps", "group2-id-with-caps"); } + @WithAccessId(user = "businessadmin") + @Test + void should_MakePermissionsIdsLowerCase_When_UpdatingUserWithUpperCasePermissions() + throws Exception { + User userToUpdate = randomTestUser().buildAndStore(userService); + userToUpdate.setPermissions(Set.of("PERM1-ID-WITH-CAPS", "Permission2-Id-With-Caps")); + + User updatedUser = userService.updateUser(userToUpdate); + + assertThat(updatedUser.getPermissions()) + .containsExactlyInAnyOrder("perm1-id-with-caps", "permission2-id-with-caps"); + } + @WithAccessId(user = "businessadmin") @Test void should_ThrowInvalidArgumentException_When_TryingToUpdateUserWithFirstNameNull() { @@ -826,6 +939,22 @@ void should_DeleteGroupsFromDatabase_When_UserHadGroups() throws Exception { assertThat(userInDatabase.getGroups()).isEmpty(); } + @WithAccessId(user = "businessadmin") + @Test + void should_DeletePermissionsFromDatabase_When_UserHadPermissions() throws Exception { + User userToDelete = + randomTestUser() + .permissions(Set.of("permission1", "permission2")) + .buildAndStore(userService); + + userService.deleteUser(userToDelete.getId()); + + // verify that groups are deleted by creating a new user with the same id and check its groups + User newUserWithSameId = randomTestUser().id(userToDelete.getId()).buildAndStore(userService); + User userInDatabase = userService.getUser(newUserWithSameId.getId()); + assertThat(userInDatabase.getPermissions()).isEmpty(); + } + @WithAccessId(user = "businessadmin") @Test void should_ThrowUserNotFoundException_When_TryingToDeleteUserWithNonExistingId() { @@ -913,6 +1042,25 @@ void should_ReturnOneDomain_When_GroupHasSufficientMinimalPermissionsToAssignDom assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); } + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnOneDomain_When_PermissionHasSufficientMinimalPermissionsToAssignDomains() + throws Exception { + String permissionsId = UUID.randomUUID().toString(); + User user = + randomTestUser() + .permissions(Set.of(permissionsId)) + .buildAndStore(userService, "businessadmin"); + Workbasket workbasket = + defaultTestWorkbasket().buildAndStore(workbasketService, "businessadmin"); + createAccessItem(permissionsId, + workbasket, WorkbasketPermission.OPEN, WorkbasketPermission.READ); + + User userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); + } + @WithAccessId(user = "user-1-1") @Test void should_ReturnEmptyDomains_When_UserHasSufficientPermissionsWhichThenGetRevoked() @@ -961,6 +1109,29 @@ void should_ReturnEmptyDomains_When_GroupHasSufficientPermissionsAndThenGroupIsU assertThat(userInDatabase.getDomains()).isEmpty(); } + @WithAccessId(user = "businessadmin") + @Test + void should_ReturnEmptyDomains_When_GroupHasSufficientPermissionsAndThenPermissionIsUpdated() + throws Exception { + String groupId = UUID.randomUUID().toString(); + User user = randomTestUser().permissions(Set.of(groupId)).buildAndStore(userService); + Workbasket workbasket = defaultTestWorkbasket().buildAndStore(workbasketService); + createAccessItem(groupId, workbasket, WorkbasketPermission.OPEN, WorkbasketPermission.READ); + + User userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); + + // then user is updated and other group is assigned + + user.setPermissions(Set.of("new group")); + userService.updateUser(user); + + userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()).isEmpty(); + } + @WithAccessId(user = "user-1-1") @Test void should_ReturnMultipleDomains_When_UserHasSufficientMinimalPermissionsForMultipleDomains() @@ -1007,6 +1178,32 @@ void should_ReturnMultipleDomains_When_UserHasSufficientMinimalPermissionsForMul .containsExactlyInAnyOrder(workbasket1.getDomain(), workbasket2.getDomain()); } + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnMultipleDomains_When_UserAndPermHaveSufficientMinimalPermsForMultipleDomains() + throws Exception { + String permissionId = UUID.randomUUID().toString(); + User user = + randomTestUser() + .permissions(Set.of(permissionId)) + .buildAndStore(userService, "businessadmin"); + Workbasket workbasket1 = + defaultTestWorkbasket().buildAndStore(workbasketService, "businessadmin"); + Workbasket workbasket2 = + defaultTestWorkbasket() + .domain("DOMAIN_B") + .buildAndStore(workbasketService, "businessadmin"); + createAccessItem( + user.getId(), workbasket1, WorkbasketPermission.OPEN, WorkbasketPermission.READ); + createAccessItem( + permissionId, workbasket2, WorkbasketPermission.OPEN, WorkbasketPermission.READ); + + User userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()) + .containsExactlyInAnyOrder(workbasket1.getDomain(), workbasket2.getDomain()); + } + @Nested @TestInstance(Lifecycle.PER_CLASS) class DifferentMinimalPermissionsToAssignDomains implements TaskanaConfigurationModifier { @@ -1050,6 +1247,27 @@ void should_ReturnEmptyDomains_When_GroupHasInsufficientMinimalPermissionsToAssi assertThat(userInDatabase.getDomains()).isEmpty(); } + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnEmptyDomains_When_PermHasInsufficientMinimalPermissionsToAssignDomains() + throws Exception { + String permissionId = UUID.randomUUID().toString(); + User user = + randomTestUser() + .permissions(Set.of(permissionId)) + .buildAndStore(userService, "businessadmin"); + Workbasket workbasket = + defaultTestWorkbasket().buildAndStore(workbasketService, "businessadmin"); + createAccessItem(permissionId, + workbasket, + WorkbasketPermission.OPEN, + WorkbasketPermission.READ); + + User userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()).isEmpty(); + } + @WithAccessId(user = "user-1-1") @Test void should_ReturnOneDomain_When_UserHasSufficientMinimalPermissionsToAssignDomains() @@ -1081,6 +1299,7 @@ void should_ReturnOneDomain_When_GroupHasSufficientMinimalPermissionsToAssignDom assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); } + } @Nested @@ -1124,6 +1343,27 @@ void should_ReturnEmptyDomains_When_PropertyIsNotSetAndGroupHasPermission() thro assertThat(userInDatabase.getDomains()).isEmpty(); } + + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnEmptyDomains_When_PropertyIsNotSetAndPermission() + throws Exception { + String permissionId = UUID.randomUUID().toString(); + User user = + randomTestUser() + .permissions(Set.of(permissionId)) + .buildAndStore(userService, "businessadmin"); + Workbasket workbasket = + defaultTestWorkbasket().buildAndStore(workbasketService, "businessadmin"); + createAccessItem(permissionId, + workbasket, + WorkbasketPermission.OPEN, + WorkbasketPermission.READ); + + User userInDatabase = userService.getUser(user.getId()); + + assertThat(userInDatabase.getDomains()).isEmpty(); + } } } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/user/api/models/User.java b/lib/taskana-core/src/main/java/pro/taskana/user/api/models/User.java index 7e820efc78..f415d56ea0 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/user/api/models/User.java +++ b/lib/taskana-core/src/main/java/pro/taskana/user/api/models/User.java @@ -34,6 +34,20 @@ public interface User { */ void setGroups(Set groups); + /** + * Returns the permissions of the User. + * + * @return permissions + */ + Set getPermissions(); + + /** + * Sets the permissions of the User. + * + * @param permissions the permissions of the User + */ + void setPermissions(Set permissions); + /** * Returns the first name of the User. * diff --git a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapper.java b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapper.java index 08de1c9fad..c3115ffb3f 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapper.java +++ b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapper.java @@ -16,6 +16,8 @@ public interface UserMapper { @SelectProvider(type = UserMapperSqlProvider.class, method = "findById") @Result(property = "id", column = "USER_ID") @Result(property = "groups", column = "USER_ID", many = @Many(select = "findGroupsById")) + @Result(property = "permissions", column = "USER_ID", + many = @Many(select = "findPermissionsById")) @Result(property = "firstName", column = "FIRST_NAME") @Result(property = "lastName", column = "LASTNAME") @Result(property = "fullName", column = "FULL_NAME") @@ -32,6 +34,8 @@ public interface UserMapper { @Result(property = "id", column = "USER_ID") @Result(property = "groups", column = "USER_ID", many = @Many(select = "findGroupsById")) + @Result(property = "permissions", column = "USER_ID", + many = @Many(select = "findPermissionsById")) @Result(property = "firstName", column = "FIRST_NAME") @Result(property = "lastName", column = "LASTNAME") @Result(property = "fullName", column = "FULL_NAME") @@ -50,6 +54,9 @@ public interface UserMapper { @SelectProvider(type = UserMapperSqlProvider.class, method = "findGroupsById") Set findGroupsById(String id); + @SelectProvider(type = UserMapperSqlProvider.class, method = "findPermissionsById") + Set findPermissionsById(String id); + @InsertProvider(type = UserMapperSqlProvider.class, method = "insert") void insert(User user); @@ -60,6 +67,13 @@ public interface UserMapper { @InsertProvider(type = UserMapperSqlProvider.class, method = "insertGroups") void insertGroups(User user); + @InsertProvider( + type = UserMapperSqlProvider.class, + method = "insertPermissionsOracle", + databaseId = "oracle") + @InsertProvider(type = UserMapperSqlProvider.class, method = "insertPermissions") + void insertPermissions(User user); + @UpdateProvider(type = UserMapperSqlProvider.class, method = "update") void update(User user); @@ -68,4 +82,7 @@ public interface UserMapper { @DeleteProvider(type = UserMapperSqlProvider.class, method = "deleteGroups") void deleteGroups(String id); + + @DeleteProvider(type = UserMapperSqlProvider.class, method = "deletePermissions") + void deletePermissions(String id); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapperSqlProvider.java b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapperSqlProvider.java index 94b03c0e43..a25fe73f9a 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapperSqlProvider.java +++ b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserMapperSqlProvider.java @@ -42,6 +42,13 @@ public static String findGroupsById() { + CLOSING_SCRIPT_TAG; } + public static String findPermissionsById() { + return OPENING_SCRIPT_TAG + + "SELECT PERMISSION_ID FROM PERMISSION_INFO WHERE USER_ID = #{id} " + + DB2_WITH_UR + + CLOSING_SCRIPT_TAG; + } + public static String insert() { return "INSERT INTO USER_INFO ( " + USER_INFO_COLUMNS + ") VALUES(" + USER_INFO_VALUES + ")"; } @@ -65,6 +72,26 @@ public static String insertGroupsOracle() { + CLOSING_SCRIPT_TAG; } + public static String insertPermissions() { + return OPENING_SCRIPT_TAG + + "INSERT INTO PERMISSION_INFO (USER_ID, PERMISSION_ID) VALUES " + + "" + + "#{id}, #{permission}" + + " " + + CLOSING_SCRIPT_TAG; + } + + public static String insertPermissionsOracle() { + return OPENING_SCRIPT_TAG + + "INSERT ALL " + + "" + + "INTO PERMISSION_INFO (USER_ID, PERMISSION_ID) VALUES ( #{id}, #{permission} )" + + " " + + "SELECT 1 FROM DUAL" + + CLOSING_SCRIPT_TAG; + } + public static String update() { return "UPDATE USER_INFO " + "SET FIRST_NAME = #{firstName}, " @@ -82,4 +109,8 @@ public static String delete() { public static String deleteGroups() { return "DELETE FROM GROUP_INFO WHERE USER_ID = #{id} "; } + + public static String deletePermissions() { + return "DELETE FROM PERMISSION_INFO WHERE USER_ID = #{id} "; + } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserServiceImpl.java index 89cc6f0583..5455e20a7b 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/user/internal/UserServiceImpl.java @@ -122,11 +122,18 @@ public User updateUser(User userToUpdate) internalTaskanaEngine.executeInDatabaseConnection(() -> userMapper.update(userToUpdate)); internalTaskanaEngine.executeInDatabaseConnection( - () -> userMapper.deleteGroups(userToUpdate.getId())); + () -> { + userMapper.deleteGroups(userToUpdate.getId()); + userMapper.deletePermissions(userToUpdate.getId()); + }); if (userToUpdate.getGroups() != null && !userToUpdate.getGroups().isEmpty()) { internalTaskanaEngine.executeInDatabaseConnection( () -> userMapper.insertGroups(userToUpdate)); } + if (userToUpdate.getPermissions() != null && !userToUpdate.getPermissions().isEmpty()) { + internalTaskanaEngine.executeInDatabaseConnection( + () -> userMapper.insertPermissions(userToUpdate)); + } ((UserImpl) userToUpdate).setDomains(determineDomains(userToUpdate)); if (LOGGER.isDebugEnabled()) { @@ -150,6 +157,7 @@ public void deleteUser(String id) () -> { userMapper.delete(id); userMapper.deleteGroups(id); + userMapper.deletePermissions(id); }); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Method deleteUser() deleted User with id '{}'.", id); @@ -158,6 +166,7 @@ public void deleteUser(String id) private Set determineDomains(User user) { Set accessIds = new HashSet<>(user.getGroups()); + accessIds.addAll(user.getPermissions()); accessIds.add(user.getId()); if (minimalWorkbasketPermissions != null && !minimalWorkbasketPermissions.isEmpty()) { // since WorkbasketService#accessIdsHavePermissions requires some role permissions we have to @@ -186,6 +195,9 @@ private void insertIntoDatabase(User userToCreate) throws UserAlreadyExistExcept if (userToCreate.getGroups() != null && !userToCreate.getGroups().isEmpty()) { userMapper.insertGroups(userToCreate); } + if (userToCreate.getPermissions() != null && !userToCreate.getPermissions().isEmpty()) { + userMapper.insertPermissions(userToCreate); + } } catch (PersistenceException e) { throw new UserAlreadyExistException(userToCreate.getId(), e); } finally { @@ -214,6 +226,8 @@ private void standardCreateActions(User user) { user.setId(user.getId().toLowerCase()); user.setGroups( user.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet())); + user.setPermissions( + user.getPermissions().stream().map((String::toLowerCase)).collect(Collectors.toSet())); } } @@ -235,6 +249,8 @@ private void standardUpdateActions(User oldUser, User newUser) { newUser.setId(newUser.getId().toLowerCase()); newUser.setGroups( newUser.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet())); + newUser.setPermissions( + newUser.getPermissions().stream().map((String::toLowerCase)).collect(Collectors.toSet())); } } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/user/internal/models/UserImpl.java b/lib/taskana-core/src/main/java/pro/taskana/user/internal/models/UserImpl.java index fbb6997688..0ef53abd44 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/user/internal/models/UserImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/user/internal/models/UserImpl.java @@ -8,6 +8,7 @@ public class UserImpl implements User { private String id; private Set groups = Collections.emptySet(); + private Set permissions = Collections.emptySet(); private String firstName; private String lastName; private String fullName; @@ -27,6 +28,7 @@ public UserImpl() {} protected UserImpl(UserImpl copyFrom) { this.id = copyFrom.id; this.groups = copyFrom.groups; + this.permissions = copyFrom.permissions; this.firstName = copyFrom.firstName; this.lastName = copyFrom.lastName; this.fullName = copyFrom.fullName; @@ -62,6 +64,16 @@ public void setGroups(Set groups) { this.groups = groups; } + @Override + public Set getPermissions() { + return permissions; + } + + @Override + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + @Override public String getFirstName() { return firstName; @@ -201,6 +213,7 @@ public int hashCode() { return Objects.hash( id, groups, + permissions, firstName, lastName, fullName, @@ -230,6 +243,7 @@ public boolean equals(Object obj) { UserImpl other = (UserImpl) obj; return Objects.equals(id, other.id) && Objects.equals(groups, other.groups) + && Objects.equals(permissions, other.permissions) && Objects.equals(firstName, other.firstName) && Objects.equals(lastName, other.lastName) && Objects.equals(fullName, other.fullName) @@ -251,6 +265,8 @@ public String toString() { + id + ", groups=" + groups + + ", permissions=" + + permissions + ", firstName=" + firstName + ", lastName=" diff --git a/lib/taskana-test-api/src/main/java/pro/taskana/testapi/builder/UserBuilder.java b/lib/taskana-test-api/src/main/java/pro/taskana/testapi/builder/UserBuilder.java index bc8676c5be..48abcdd7d6 100644 --- a/lib/taskana-test-api/src/main/java/pro/taskana/testapi/builder/UserBuilder.java +++ b/lib/taskana-test-api/src/main/java/pro/taskana/testapi/builder/UserBuilder.java @@ -28,6 +28,11 @@ public UserBuilder groups(Set groups) { return this; } + public UserBuilder permissions(Set permissions) { + testUser.setPermissions(permissions); + return this; + } + public UserBuilder firstName(String firstName) { testUser.setFirstName(firstName); return this; diff --git a/rest/taskana-rest-spring-example-boot/src/main/resources/application-db2.properties b/rest/taskana-rest-spring-example-boot/src/main/resources/application-db2.properties index 97d67e7523..f212e4a3d7 100644 --- a/rest/taskana-rest-spring-example-boot/src/main/resources/application-db2.properties +++ b/rest/taskana-rest-spring-example-boot/src/main/resources/application-db2.properties @@ -75,6 +75,8 @@ taskana.ldap.userOrglevel2Attribute=orgLevel2 taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid +taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/rest/taskana-rest-spring-example-boot/src/main/resources/application-postgres.properties b/rest/taskana-rest-spring-example-boot/src/main/resources/application-postgres.properties index 4034f11751..1b87ecb3aa 100644 --- a/rest/taskana-rest-spring-example-boot/src/main/resources/application-postgres.properties +++ b/rest/taskana-rest-spring-example-boot/src/main/resources/application-postgres.properties @@ -53,6 +53,8 @@ taskana.ldap.userOrglevel2Attribute=orgLevel2 taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid +taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase=ou=groups taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/rest/taskana-rest-spring-example-boot/src/main/resources/application.properties b/rest/taskana-rest-spring-example-boot/src/main/resources/application.properties index 8719a123f6..10705fa613 100644 --- a/rest/taskana-rest-spring-example-boot/src/main/resources/application.properties +++ b/rest/taskana-rest-spring-example-boot/src/main/resources/application.properties @@ -71,6 +71,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupofuniquenames diff --git a/rest/taskana-rest-spring-example-common/src/main/resources/taskana-example.ldif b/rest/taskana-rest-spring-example-common/src/main/resources/taskana-example.ldif index dad402a477..cf8b00e506 100644 --- a/rest/taskana-rest-spring-example-common/src/main/resources/taskana-example.ldif +++ b/rest/taskana-rest-spring-example-common/src/main/resources/taskana-example.ldif @@ -16,6 +16,11 @@ cn: users objectclass: top objectclass: container +dn: cn=other-users,OU=Test,O=TASKANA +cn: users +objectclass: top +objectclass: container + dn: cn=organisation,OU=Test,O=TASKANA cn: organisation objectclass: top @@ -91,7 +96,7 @@ description: desc phoneNumber: 012345678 mobileNumber: 09876554321 email: Titus.Toll@taskana.de -orgLevel1: QWERT +orgLevel1: ABC orgLevel2: DEF/GHI someDepartement: JKL orgLevel4: MNO/PQR @@ -104,6 +109,8 @@ sn: Toll ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Titus Toll userPassword: teamlead-1 +permission: organize +permission: inet dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -119,6 +126,8 @@ sn: Mustermann ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Max Mustermann userPassword: user-1-1 +permission: organize +permission: inet dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -134,6 +143,9 @@ sn: Eifrig ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Elena Eifrig userPassword: user-1-2 +permission: organize +permission: inet +permission: program dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -211,6 +223,9 @@ sn: Bach ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 cn: Thomas Bach userPassword: user-2-3 +permission: organize +permission: inet +permission: program dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -267,6 +282,9 @@ sn: Meyer ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 cn: Wiebke Meyer userPassword: user-2-7 +permission: organize +permission: inet +permission: manage dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -352,6 +370,26 @@ sn: Bio ou: Organisationseinheit/Organisationseinheit B cn: Brunhilde Bio userPassword: user-b-2 +permission: organize +permission: inet +permission: siegen +permission: frieden + +######################## +# Users in other cn +######################## +dn: uid=otheruser,cn=other-users,OU=Test,O=TASKANA +objectclass: inetorgperson +objectclass: organizationalperson +objectclass: person +objectclass: top +givenName: Other +description: User in other cn than search root +uid: otheruser +sn: User +ou: Other +cn: Other User +userPassword: otheruser ######################## diff --git a/rest/taskana-rest-spring-example-common/src/test/resources/application.properties b/rest/taskana-rest-spring-example-common/src/test/resources/application.properties index 801a9a1df2..133810e00a 100644 --- a/rest/taskana-rest-spring-example-common/src/test/resources/application.properties +++ b/rest/taskana-rest-spring-example-common/src/test/resources/application.properties @@ -30,6 +30,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/rest/taskana-rest-spring-example-wildfly/src/main/resources/application-postgres.properties b/rest/taskana-rest-spring-example-wildfly/src/main/resources/application-postgres.properties index a41fbe88a4..868059120a 100644 --- a/rest/taskana-rest-spring-example-wildfly/src/main/resources/application-postgres.properties +++ b/rest/taskana-rest-spring-example-wildfly/src/main/resources/application-postgres.properties @@ -22,6 +22,7 @@ taskana.ldap.groupNameAttribute=cn taskana.ldap.minSearchForLength=3 taskana.ldap.maxNumberOfReturnedAccessIds=50 taskana.ldap.groupsOfUser=memberUid +taskana.ldap.userPermissionsAttribute=permission ####### JobScheduler cron expression that specifies when the JobSchedler runs taskana.jobscheduler.async.cron=0 * * * * * diff --git a/rest/taskana-rest-spring-example-wildfly/src/main/resources/application.properties b/rest/taskana-rest-spring-example-wildfly/src/main/resources/application.properties index 72aeaa7331..931f0a032d 100644 --- a/rest/taskana-rest-spring-example-wildfly/src/main/resources/application.properties +++ b/rest/taskana-rest-spring-example-wildfly/src/main/resources/application.properties @@ -16,6 +16,7 @@ taskana.ldap.userLastnameAttribute=sn taskana.ldap.userFullnameAttribute=cn taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupofuniquenames diff --git a/rest/taskana-rest-spring-example-wildfly/src/test/resources/application-with-additional-user-config.properties b/rest/taskana-rest-spring-example-wildfly/src/test/resources/application-with-additional-user-config.properties index 62be0b2249..9763fd7b61 100644 --- a/rest/taskana-rest-spring-example-wildfly/src/test/resources/application-with-additional-user-config.properties +++ b/rest/taskana-rest-spring-example-wildfly/src/test/resources/application-with-additional-user-config.properties @@ -23,6 +23,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/rest/taskana-rest-spring-example-wildfly/src/test/resources/application.properties b/rest/taskana-rest-spring-example-wildfly/src/test/resources/application.properties index d94aa311ca..bd5ffd3d05 100644 --- a/rest/taskana-rest-spring-example-wildfly/src/test/resources/application.properties +++ b/rest/taskana-rest-spring-example-wildfly/src/test/resources/application.properties @@ -21,6 +21,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/rest/taskana-rest-spring-test-lib/src/main/resources/taskana-test.ldif b/rest/taskana-rest-spring-test-lib/src/main/resources/taskana-test.ldif index 2c05e7969d..cf8b00e506 100644 --- a/rest/taskana-rest-spring-test-lib/src/main/resources/taskana-test.ldif +++ b/rest/taskana-rest-spring-test-lib/src/main/resources/taskana-test.ldif @@ -109,6 +109,8 @@ sn: Toll ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Titus Toll userPassword: teamlead-1 +permission: organize +permission: inet dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -124,6 +126,8 @@ sn: Mustermann ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Max Mustermann userPassword: user-1-1 +permission: organize +permission: inet dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -139,6 +143,9 @@ sn: Eifrig ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 cn: Elena Eifrig userPassword: user-1-2 +permission: organize +permission: inet +permission: program dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -216,6 +223,9 @@ sn: Bach ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 cn: Thomas Bach userPassword: user-2-3 +permission: organize +permission: inet +permission: program dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -272,6 +282,9 @@ sn: Meyer ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 cn: Wiebke Meyer userPassword: user-2-7 +permission: organize +permission: inet +permission: manage dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA objectclass: inetorgperson @@ -357,6 +370,10 @@ sn: Bio ou: Organisationseinheit/Organisationseinheit B cn: Brunhilde Bio userPassword: user-b-2 +permission: organize +permission: inet +permission: siegen +permission: frieden ######################## # Users in other cn diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapClient.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapClient.java index 1c04b9e7e2..12bd28c614 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapClient.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapClient.java @@ -413,6 +413,10 @@ public String getUserMemberOfGroupAttribute() { return LdapSettings.TASKANA_LDAP_USER_MEMBER_OF_GROUP_ATTRIBUTE.getValueFromEnv(env); } + public String getUserPermissionsAttribute() { + return LdapSettings.TASKANA_LDAP_USER_PERMISSIONS_ATTRIBUTE.getValueFromEnv(env); + } + public String getGroupSearchBase() { return LdapSettings.TASKANA_LDAP_GROUP_SEARCH_BASE.getValueFromEnv(env); } @@ -538,6 +542,7 @@ String[] getLookUpUserInfoAttributesToReturn() { return new String[] { getUserIdAttribute(), getUserMemberOfGroupAttribute(), + getUserPermissionsAttribute(), getUserFirstnameAttribute(), getUserLastnameAttribute(), getUserFullnameAttribute(), @@ -630,6 +635,20 @@ private Set getGroupIdsFromContext(final DirContextOperations context) { return groups; } + private Set getPermissionIdsFromContext(final DirContextOperations context) { + String[] permissionAttributes = context.getStringAttributes(getUserPermissionsAttribute()); + Set permissions = + permissionAttributes != null ? Set.of(permissionAttributes) : Collections.emptySet(); + if (useLowerCaseForAccessIds) { + permissions = + permissions.stream() + .filter(Objects::nonNull) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + } + return permissions; + } + /** Context Mapper for user entries. */ class GroupContextMapper extends AbstractContextMapper { @@ -650,6 +669,7 @@ public User doMapFromContext(final DirContextOperations context) { final User user = new UserImpl(); user.setId(getUserIdFromContext(context)); user.setGroups(getGroupIdsFromContext(context)); + user.setPermissions(getPermissionIdsFromContext(context)); user.setFirstName(context.getStringAttribute(getUserFirstnameAttribute())); user.setLastName(context.getStringAttribute(getUserLastnameAttribute())); user.setFullName(context.getStringAttribute(getUserFullnameAttribute())); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapSettings.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapSettings.java index 00c4654820..7e7347f9d2 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapSettings.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/ldap/LdapSettings.java @@ -19,6 +19,7 @@ enum LdapSettings { TASKANA_LDAP_USER_ORG_LEVEL_3_ATTRIBUTE("taskana.ldap.userOrglevel3Attribute"), TASKANA_LDAP_USER_ORG_LEVEL_4_ATTRIBUTE("taskana.ldap.userOrglevel4Attribute"), TASKANA_LDAP_USER_MEMBER_OF_GROUP_ATTRIBUTE("taskana.ldap.userMemberOfGroupAttribute"), + TASKANA_LDAP_USER_PERMISSIONS_ATTRIBUTE("taskana.ldap.userPermissionsAttribute"), TASKANA_LDAP_GROUP_SEARCH_BASE("taskana.ldap.groupSearchBase"), TASKANA_LDAP_BASE_DN("taskana.ldap.baseDn"), TASKANA_LDAP_GROUP_SEARCH_FILTER_NAME("taskana.ldap.groupSearchFilterName"), diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/jobs/UserInfoRefreshJob.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/jobs/UserInfoRefreshJob.java index a8af13ff01..d8d9be1d75 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/jobs/UserInfoRefreshJob.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/jobs/UserInfoRefreshJob.java @@ -62,7 +62,7 @@ protected void execute() { .map(refreshUserPostprocessorManager::processUserAfterRefresh) .collect(Collectors.toList()); addExistingConfigurationDataToUsers(usersAfterProcessing); - clearExistingUsersAndGroups(); + clearExistingUsersAndGroupsAndPermissions(); insertNewUsers(usersAfterProcessing); LOGGER.info("Job to refresh all user info has finished."); @@ -72,18 +72,18 @@ protected void execute() { } } - private void clearExistingUsersAndGroups() { + private void clearExistingUsersAndGroupsAndPermissions() { sqlConnectionRunner.runWithConnection( connection -> { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Trying to delete all users and groups"); + LOGGER.debug("Trying to delete all users, groups and permissions"); } - String sql = "DELETE FROM USER_INFO; DELETE FROM GROUP_INFO"; + String sql = "DELETE FROM USER_INFO; DELETE FROM GROUP_INFO; DELETE FROM PERMISSION_INFO"; PreparedStatement statement = connection.prepareStatement(sql); statement.execute(); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Successfully deleted all users and groups"); + LOGGER.debug("Successfully deleted all users, groups and permissions"); } if (!connection.getAutoCommit()) { diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssembler.java index 6da78c7526..6aa696f379 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssembler.java @@ -22,6 +22,7 @@ public UserRepresentationModel toModel(User entity) { UserRepresentationModel repModel = new UserRepresentationModel(); repModel.setUserId(entity.getId()); repModel.setGroups(entity.getGroups()); + repModel.setPermissions(entity.getPermissions()); repModel.setFirstName(entity.getFirstName()); repModel.setLastName(entity.getLastName()); repModel.setFullName(entity.getFullName()); @@ -43,6 +44,7 @@ public User toEntityModel(UserRepresentationModel repModel) { UserImpl user = new UserImpl(); user.setId(repModel.getUserId()); user.setGroups(repModel.getGroups()); + user.setPermissions(repModel.getPermissions()); user.setFirstName(repModel.getFirstName()); user.setLastName(repModel.getLastName()); user.setFullName(repModel.getFullName()); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/models/UserRepresentationModel.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/models/UserRepresentationModel.java index 06d7d5140f..e8e29793a1 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/models/UserRepresentationModel.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/user/rest/models/UserRepresentationModel.java @@ -14,6 +14,8 @@ public class UserRepresentationModel extends RepresentationModel groups; + /** The permissions of the User. */ + private Set permissions; /** * The domains of the User. * @@ -62,6 +64,14 @@ public void setGroups(Set groups) { this.groups = groups; } + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + public String getFirstName() { return firstName; } @@ -172,6 +182,7 @@ public int hashCode() { super.hashCode(), userId, groups, + permissions, domains, firstName, lastName, @@ -201,6 +212,7 @@ public boolean equals(Object obj) { UserRepresentationModel other = (UserRepresentationModel) obj; return Objects.equals(userId, other.userId) && Objects.equals(groups, other.groups) + && Objects.equals(permissions, other.permissions) && Objects.equals(domains, other.domains) && Objects.equals(firstName, other.firstName) && Objects.equals(lastName, other.lastName) diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/ldap/LdapClientTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/ldap/LdapClientTest.java index 76703ab192..d6172886cf 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/ldap/LdapClientTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/ldap/LdapClientTest.java @@ -215,6 +215,7 @@ private void setUpEnvMock() { {"taskana.ldap.userSearchFilterName", "objectclass"}, {"taskana.ldap.groupsOfUser", "memberUid"}, {"taskana.ldap.groupNameAttribute", "cn"}, + {"taskana.ldap.userPermissionsAttribute", "permission"}, {"taskana.ldap.groupSearchFilterValue", "groupOfUniqueNames"}, {"taskana.ldap.groupSearchFilterName", "objectclass"}, {"taskana.ldap.groupSearchBase", "ou=groups"}, @@ -230,7 +231,7 @@ private void setUpEnvMock() { {"taskana.ldap.userOrglevel1Attribute", "orgLevel1"}, {"taskana.ldap.userOrglevel2Attribute", "orgLevel2"}, {"taskana.ldap.userOrglevel3Attribute", "orgLevel3"}, - {"taskana.ldap.userOrglevel4Attribute", "orgLevel4"}, + {"taskana.ldap.userOrglevel4Attribute", "orgLevel4"} }) .forEach( strings -> diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/jobs/UserInfoRefreshJobIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/jobs/UserInfoRefreshJobIntTest.java index c83a44cbee..38e24a9393 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/jobs/UserInfoRefreshJobIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/jobs/UserInfoRefreshJobIntTest.java @@ -91,6 +91,23 @@ void should_RefreshUserInfo_When_UserInfoRefreshJobIsExecuted() throws Exception .hasSameSizeAs(ldapGroups) .containsExactlyElementsOf(ldapGroups); } + + // validate permissions + for (int i = 0; i < users.size(); i++) { + User user = users.get(i); + List permissionIds = getPermissionInfo(connection, user.getId()); + permissionIds.sort(Comparator.naturalOrder()); + + User ldapUser = ldapusers.get(i); + List ldapPermissions = + ldapUser.getPermissions().stream() + .sorted(Comparator.naturalOrder()) + .collect(Collectors.toList()); + + assertThat(permissionIds) + .hasSameSizeAs(ldapPermissions) + .containsExactlyElementsOf(ldapPermissions); + } } } @@ -142,4 +159,19 @@ private List getGroupInfo(Connection connection, String userId) throws E } return groupIds; } + + private List getPermissionInfo(Connection connection, String userId) throws Exception { + List permissionIds = new ArrayList<>(); + PreparedStatement ps = + connection.prepareStatement( + "SELECT permission_id FROM " + + connection.getSchema() + + ".permission_info WHERE user_id = ?"); + ps.setString(1, userId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + permissionIds.add(rs.getString(1)); + } + return permissionIds; + } } diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/UserControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/UserControllerIntTest.java index 031a41a7c9..9ef02b3c34 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/UserControllerIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/UserControllerIntTest.java @@ -184,6 +184,7 @@ void should_CreateValidUser_When_CallingCreateEndpoint() throws Exception { UserRepresentationModel newUser = new UserRepresentationModel(); newUser.setUserId("12345"); newUser.setGroups(Set.of("group1", "group2")); + newUser.setPermissions(Set.of("perm1", "perm2")); newUser.setFirstName("Hans"); newUser.setLastName("Georg"); newUser.setFullName("Georg, Hans"); diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssemblerTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssemblerTest.java index 8ee621a7a3..e0e0fdd66e 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssemblerTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/user/rest/assembler/UserRepresentationModelAssemblerTest.java @@ -29,6 +29,7 @@ void should_ReturnRepresentationModel_When_ConvertingUserEntityToRepresentationM UserImpl user = (UserImpl) userService.newUser(); user.setId("user-1-2"); user.setGroups(Set.of("group1", "group2")); + user.setPermissions(Set.of("perm1", "perm2")); user.setFirstName("Hans"); user.setLastName("Georg"); user.setFullName("Hans Georg"); @@ -52,6 +53,7 @@ void should_ReturnEntity_When_ConvertingUserRepresentationModelToEntity() { UserRepresentationModel repModel = new UserRepresentationModel(); repModel.setUserId("user-1-2"); repModel.setGroups(Set.of("group1", "group2")); + repModel.setPermissions(Set.of("perm1", "perm2")); repModel.setFirstName("Hans"); repModel.setLastName("Georg"); repModel.setFullName("Hans Georg"); @@ -75,6 +77,7 @@ void should_BeEqual_When_ConvertingEntityToRepModelAndBackToEntity() { UserImpl user = (UserImpl) userService.newUser(); user.setId("user-1-2"); user.setGroups(Set.of("group1", "group2")); + user.setPermissions(Set.of("perm1", "perm2")); user.setFirstName("Hans"); user.setLastName("Georg"); user.setFullName("Hans Georg"); @@ -104,6 +107,7 @@ private static void testEquality(User entity, UserRepresentationModel repModel) assertThat(entity.getId()).isEqualTo(repModel.getUserId()); assertThat(entity.getGroups()).isEqualTo(repModel.getGroups()); + assertThat(entity.getPermissions()).isEqualTo(repModel.getPermissions()); assertThat(entity.getFirstName()).isEqualTo(repModel.getFirstName()); assertThat(entity.getLastName()).isEqualTo(repModel.getLastName()); assertThat(entity.getFullName()).isEqualTo(repModel.getFullName()); diff --git a/rest/taskana-rest-spring/src/test/resources/application.properties b/rest/taskana-rest-spring/src/test/resources/application.properties index ce45493a25..19c3098ba7 100644 --- a/rest/taskana-rest-spring/src/test/resources/application.properties +++ b/rest/taskana-rest-spring/src/test/resources/application.properties @@ -28,6 +28,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames diff --git a/routing/taskana-routing-rest/src/test/resources/application.properties b/routing/taskana-routing-rest/src/test/resources/application.properties index d654d386e8..f9a08ea66a 100644 --- a/routing/taskana-routing-rest/src/test/resources/application.properties +++ b/routing/taskana-routing-rest/src/test/resources/application.properties @@ -34,6 +34,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userIdAttribute=uid taskana.ldap.userMemberOfGroupAttribute=memberOf +taskana.ldap.userPermissionsAttribute=permission taskana.ldap.groupSearchBase= taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterValue=groupOfUniqueNames