Closes #2302 - Adds Permissions to Users

- Extends LDAP client to read permission attributes from users
            - Extends database schema with PERMISSION_INFO table and sets schema version to 6.4.0
            - Exetends User models (Builder, Mapper,..) to have permission attribute
    	- Determination of Domains is now able to be done via permissions defined on users

    Signed-off-by: Kálmán Képes <2853992+nyuuyn@users.noreply.github.com>
This commit is contained in:
Kálmán Képes 2023-09-01 09:33:54 +02:00 committed by Elena Mokeeva
parent 8cff46a969
commit 18c34d1dfd
39 changed files with 566 additions and 8 deletions

View File

@ -14,5 +14,6 @@ DELETE FROM OBJECT_REFERENCE;
DELETE FROM SCHEDULED_JOB; DELETE FROM SCHEDULED_JOB;
DELETE FROM USER_INFO; DELETE FROM USER_INFO;
DELETE FROM GROUP_INFO; DELETE FROM GROUP_INFO;
DELETE FROM PERMISSION_INFO;
INSERT INTO CONFIGURATION (NAME) VALUES ('MASTER'); INSERT INTO CONFIGURATION (NAME) VALUES ('MASTER');
COMMIT; COMMIT;

View File

@ -14,6 +14,7 @@ DROP TABLE OBJECT_REFERENCE;
DROP TABLE SCHEDULED_JOB; DROP TABLE SCHEDULED_JOB;
DROP TABLE USER_INFO; DROP TABLE USER_INFO;
DROP TABLE GROUP_INFO; DROP TABLE GROUP_INFO;
DROP TABLE PERMISSION_INFO;
DROP SEQUENCE SCHEDULED_JOB_SEQ; DROP SEQUENCE SCHEDULED_JOB_SEQ;
DROP SEQUENCE TASKANA_SCHEMA_VERSION_ID_SEQ; DROP SEQUENCE TASKANA_SCHEMA_VERSION_ID_SEQ;
COMMIT; COMMIT;

View File

@ -357,6 +357,13 @@ CREATE TABLE GROUP_INFO
PRIMARY KEY (USER_ID, GROUP_ID) 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 CREATE SEQUENCE SCHEDULED_JOB_SEQ
MINVALUE 1 MINVALUE 1
START WITH 1 START WITH 1

View File

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

View File

@ -363,6 +363,13 @@ CREATE TABLE GROUP_INFO
PRIMARY KEY (USER_ID, GROUP_ID) 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 CREATE SEQUENCE SCHEDULED_JOB_SEQ
MINVALUE 1 MINVALUE 1
START WITH 1 START WITH 1

View File

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

View File

@ -356,6 +356,13 @@ CREATE TABLE GROUP_INFO
CONSTRAINT GROUP_INFO_PKEY PRIMARY KEY (USER_ID, GROUP_ID) 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 CREATE SEQUENCE SCHEDULED_JOB_SEQ
START WITH 1 START WITH 1
INCREMENT BY 1 INCREMENT BY 1

View File

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

View File

@ -360,6 +360,13 @@ CREATE TABLE GROUP_INFO
PRIMARY KEY (USER_ID, GROUP_ID) 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 CREATE SEQUENCE SCHEDULED_JOB_SEQ
MINVALUE 1 MINVALUE 1
START WITH 1 START WITH 1

View File

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

View File

@ -26,6 +26,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchBase=cn=groups
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -202,6 +202,59 @@ class UserServiceAccTest {
domains -> Set.of(workbasketDomainB.getDomain()).equals(domains), "DOMAIN_B")); 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<User> 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<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
List<User> 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") @WithAccessId(user = "user-1-1")
@Test @Test
void should_ReturnAllUsers_When_TryingToGetUsersWithIdsExistingInCaps() throws Exception { void should_ReturnAllUsers_When_TryingToGetUsersWithIdsExistingInCaps() throws Exception {
@ -312,6 +365,20 @@ class UserServiceAccTest {
assertThat(userToCreate).isNotSameAs(userInDatabase).isEqualTo(userInDatabase); 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") @WithAccessId(user = "businessadmin")
@TestFactory @TestFactory
Stream<DynamicTest> Stream<DynamicTest>
@ -483,12 +550,15 @@ class UserServiceAccTest {
userToCreate.setFirstName("firstName"); userToCreate.setFirstName("firstName");
userToCreate.setLastName("lastName"); userToCreate.setLastName("lastName");
userToCreate.setGroups(Set.of("GROUP1-ID-WITH-CAPS", "Group2-Id-With-Caps")); 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); User userInDatabase = userService.createUser(userToCreate);
assertThat(userInDatabase.getId()).isEqualTo("user-id-with-caps"); assertThat(userInDatabase.getId()).isEqualTo("user-id-with-caps");
assertThat(userInDatabase.getGroups()) assertThat(userInDatabase.getGroups())
.containsExactlyInAnyOrder("group1-id-with-caps", "group2-id-with-caps"); .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 @@ class UserServiceAccTest {
return DynamicTest.stream(testCases, Triplet::getLeft, test); return DynamicTest.stream(testCases, Triplet::getLeft, test);
} }
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_UpdatePermissions() {
Stream<Triplet<String, Set<String>, Set<String>>> 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<Triplet<String, Set<String>, Set<String>>> test =
t -> {
Set<String> existingPerms = t.getMiddle();
Set<String> 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") @WithAccessId(user = "user-1-1")
@Test @Test
void should_ThrowNotAuthorizedException_When_TryingToUpdateUserWithNoAdminRole() void should_ThrowNotAuthorizedException_When_TryingToUpdateUserWithNoAdminRole()
@ -722,6 +822,19 @@ class UserServiceAccTest {
.containsExactlyInAnyOrder("group1-id-with-caps", "group2-id-with-caps"); .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") @WithAccessId(user = "businessadmin")
@Test @Test
void should_ThrowInvalidArgumentException_When_TryingToUpdateUserWithFirstNameNull() { void should_ThrowInvalidArgumentException_When_TryingToUpdateUserWithFirstNameNull() {
@ -826,6 +939,22 @@ class UserServiceAccTest {
assertThat(userInDatabase.getGroups()).isEmpty(); 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") @WithAccessId(user = "businessadmin")
@Test @Test
void should_ThrowUserNotFoundException_When_TryingToDeleteUserWithNonExistingId() { void should_ThrowUserNotFoundException_When_TryingToDeleteUserWithNonExistingId() {
@ -913,6 +1042,25 @@ class UserServiceAccTest {
assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); 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") @WithAccessId(user = "user-1-1")
@Test @Test
void should_ReturnEmptyDomains_When_UserHasSufficientPermissionsWhichThenGetRevoked() void should_ReturnEmptyDomains_When_UserHasSufficientPermissionsWhichThenGetRevoked()
@ -961,6 +1109,29 @@ class UserServiceAccTest {
assertThat(userInDatabase.getDomains()).isEmpty(); 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") @WithAccessId(user = "user-1-1")
@Test @Test
void should_ReturnMultipleDomains_When_UserHasSufficientMinimalPermissionsForMultipleDomains() void should_ReturnMultipleDomains_When_UserHasSufficientMinimalPermissionsForMultipleDomains()
@ -1007,6 +1178,32 @@ class UserServiceAccTest {
.containsExactlyInAnyOrder(workbasket1.getDomain(), workbasket2.getDomain()); .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 @Nested
@TestInstance(Lifecycle.PER_CLASS) @TestInstance(Lifecycle.PER_CLASS)
class DifferentMinimalPermissionsToAssignDomains implements TaskanaConfigurationModifier { class DifferentMinimalPermissionsToAssignDomains implements TaskanaConfigurationModifier {
@ -1050,6 +1247,27 @@ class UserServiceAccTest {
assertThat(userInDatabase.getDomains()).isEmpty(); 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") @WithAccessId(user = "user-1-1")
@Test @Test
void should_ReturnOneDomain_When_UserHasSufficientMinimalPermissionsToAssignDomains() void should_ReturnOneDomain_When_UserHasSufficientMinimalPermissionsToAssignDomains()
@ -1081,6 +1299,7 @@ class UserServiceAccTest {
assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain()); assertThat(userInDatabase.getDomains()).containsExactly(workbasket.getDomain());
} }
} }
@Nested @Nested
@ -1124,6 +1343,27 @@ class UserServiceAccTest {
assertThat(userInDatabase.getDomains()).isEmpty(); 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();
}
} }
} }
} }

View File

@ -34,6 +34,20 @@ public interface User {
*/ */
void setGroups(Set<String> groups); void setGroups(Set<String> groups);
/**
* Returns the permissions of the User.
*
* @return permissions
*/
Set<String> getPermissions();
/**
* Sets the permissions of the User.
*
* @param permissions the permissions of the User
*/
void setPermissions(Set<String> permissions);
/** /**
* Returns the first name of the User. * Returns the first name of the User.
* *

View File

@ -16,6 +16,8 @@ public interface UserMapper {
@SelectProvider(type = UserMapperSqlProvider.class, method = "findById") @SelectProvider(type = UserMapperSqlProvider.class, method = "findById")
@Result(property = "id", column = "USER_ID") @Result(property = "id", column = "USER_ID")
@Result(property = "groups", column = "USER_ID", many = @Many(select = "findGroupsById")) @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 = "firstName", column = "FIRST_NAME")
@Result(property = "lastName", column = "LASTNAME") @Result(property = "lastName", column = "LASTNAME")
@Result(property = "fullName", column = "FULL_NAME") @Result(property = "fullName", column = "FULL_NAME")
@ -32,6 +34,8 @@ public interface UserMapper {
@Result(property = "id", column = "USER_ID") @Result(property = "id", column = "USER_ID")
@Result(property = "groups", column = "USER_ID", many = @Many(select = "findGroupsById")) @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 = "firstName", column = "FIRST_NAME")
@Result(property = "lastName", column = "LASTNAME") @Result(property = "lastName", column = "LASTNAME")
@Result(property = "fullName", column = "FULL_NAME") @Result(property = "fullName", column = "FULL_NAME")
@ -50,6 +54,9 @@ public interface UserMapper {
@SelectProvider(type = UserMapperSqlProvider.class, method = "findGroupsById") @SelectProvider(type = UserMapperSqlProvider.class, method = "findGroupsById")
Set<String> findGroupsById(String id); Set<String> findGroupsById(String id);
@SelectProvider(type = UserMapperSqlProvider.class, method = "findPermissionsById")
Set<String> findPermissionsById(String id);
@InsertProvider(type = UserMapperSqlProvider.class, method = "insert") @InsertProvider(type = UserMapperSqlProvider.class, method = "insert")
void insert(User user); void insert(User user);
@ -60,6 +67,13 @@ public interface UserMapper {
@InsertProvider(type = UserMapperSqlProvider.class, method = "insertGroups") @InsertProvider(type = UserMapperSqlProvider.class, method = "insertGroups")
void insertGroups(User user); 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") @UpdateProvider(type = UserMapperSqlProvider.class, method = "update")
void update(User user); void update(User user);
@ -68,4 +82,7 @@ public interface UserMapper {
@DeleteProvider(type = UserMapperSqlProvider.class, method = "deleteGroups") @DeleteProvider(type = UserMapperSqlProvider.class, method = "deleteGroups")
void deleteGroups(String id); void deleteGroups(String id);
@DeleteProvider(type = UserMapperSqlProvider.class, method = "deletePermissions")
void deletePermissions(String id);
} }

View File

@ -42,6 +42,13 @@ public class UserMapperSqlProvider {
+ CLOSING_SCRIPT_TAG; + 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() { public static String insert() {
return "INSERT INTO USER_INFO ( " + USER_INFO_COLUMNS + ") VALUES(" + USER_INFO_VALUES + ")"; return "INSERT INTO USER_INFO ( " + USER_INFO_COLUMNS + ") VALUES(" + USER_INFO_VALUES + ")";
} }
@ -65,6 +72,26 @@ public class UserMapperSqlProvider {
+ CLOSING_SCRIPT_TAG; + CLOSING_SCRIPT_TAG;
} }
public static String insertPermissions() {
return OPENING_SCRIPT_TAG
+ "INSERT INTO PERMISSION_INFO (USER_ID, PERMISSION_ID) VALUES "
+ "<foreach item='permission' collection='permissions' "
+ "open='(' separator='),(' close=')'>"
+ "#{id}, #{permission}"
+ "</foreach> "
+ CLOSING_SCRIPT_TAG;
}
public static String insertPermissionsOracle() {
return OPENING_SCRIPT_TAG
+ "INSERT ALL "
+ "<foreach item='permission' collection='permissions' separator='\n'>"
+ "INTO PERMISSION_INFO (USER_ID, PERMISSION_ID) VALUES ( #{id}, #{permission} )"
+ "</foreach> "
+ "SELECT 1 FROM DUAL"
+ CLOSING_SCRIPT_TAG;
}
public static String update() { public static String update() {
return "UPDATE USER_INFO " return "UPDATE USER_INFO "
+ "SET FIRST_NAME = #{firstName}, " + "SET FIRST_NAME = #{firstName}, "
@ -82,4 +109,8 @@ public class UserMapperSqlProvider {
public static String deleteGroups() { public static String deleteGroups() {
return "DELETE FROM GROUP_INFO WHERE USER_ID = #{id} "; return "DELETE FROM GROUP_INFO WHERE USER_ID = #{id} ";
} }
public static String deletePermissions() {
return "DELETE FROM PERMISSION_INFO WHERE USER_ID = #{id} ";
}
} }

View File

@ -122,11 +122,18 @@ public class UserServiceImpl implements UserService {
internalTaskanaEngine.executeInDatabaseConnection(() -> userMapper.update(userToUpdate)); internalTaskanaEngine.executeInDatabaseConnection(() -> userMapper.update(userToUpdate));
internalTaskanaEngine.executeInDatabaseConnection( internalTaskanaEngine.executeInDatabaseConnection(
() -> userMapper.deleteGroups(userToUpdate.getId())); () -> {
userMapper.deleteGroups(userToUpdate.getId());
userMapper.deletePermissions(userToUpdate.getId());
});
if (userToUpdate.getGroups() != null && !userToUpdate.getGroups().isEmpty()) { if (userToUpdate.getGroups() != null && !userToUpdate.getGroups().isEmpty()) {
internalTaskanaEngine.executeInDatabaseConnection( internalTaskanaEngine.executeInDatabaseConnection(
() -> userMapper.insertGroups(userToUpdate)); () -> userMapper.insertGroups(userToUpdate));
} }
if (userToUpdate.getPermissions() != null && !userToUpdate.getPermissions().isEmpty()) {
internalTaskanaEngine.executeInDatabaseConnection(
() -> userMapper.insertPermissions(userToUpdate));
}
((UserImpl) userToUpdate).setDomains(determineDomains(userToUpdate)); ((UserImpl) userToUpdate).setDomains(determineDomains(userToUpdate));
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
@ -150,6 +157,7 @@ public class UserServiceImpl implements UserService {
() -> { () -> {
userMapper.delete(id); userMapper.delete(id);
userMapper.deleteGroups(id); userMapper.deleteGroups(id);
userMapper.deletePermissions(id);
}); });
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Method deleteUser() deleted User with id '{}'.", id); LOGGER.debug("Method deleteUser() deleted User with id '{}'.", id);
@ -158,6 +166,7 @@ public class UserServiceImpl implements UserService {
private Set<String> determineDomains(User user) { private Set<String> determineDomains(User user) {
Set<String> accessIds = new HashSet<>(user.getGroups()); Set<String> accessIds = new HashSet<>(user.getGroups());
accessIds.addAll(user.getPermissions());
accessIds.add(user.getId()); accessIds.add(user.getId());
if (minimalWorkbasketPermissions != null && !minimalWorkbasketPermissions.isEmpty()) { if (minimalWorkbasketPermissions != null && !minimalWorkbasketPermissions.isEmpty()) {
// since WorkbasketService#accessIdsHavePermissions requires some role permissions we have to // since WorkbasketService#accessIdsHavePermissions requires some role permissions we have to
@ -186,6 +195,9 @@ public class UserServiceImpl implements UserService {
if (userToCreate.getGroups() != null && !userToCreate.getGroups().isEmpty()) { if (userToCreate.getGroups() != null && !userToCreate.getGroups().isEmpty()) {
userMapper.insertGroups(userToCreate); userMapper.insertGroups(userToCreate);
} }
if (userToCreate.getPermissions() != null && !userToCreate.getPermissions().isEmpty()) {
userMapper.insertPermissions(userToCreate);
}
} catch (PersistenceException e) { } catch (PersistenceException e) {
throw new UserAlreadyExistException(userToCreate.getId(), e); throw new UserAlreadyExistException(userToCreate.getId(), e);
} finally { } finally {
@ -214,6 +226,8 @@ public class UserServiceImpl implements UserService {
user.setId(user.getId().toLowerCase()); user.setId(user.getId().toLowerCase());
user.setGroups( user.setGroups(
user.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet())); user.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet()));
user.setPermissions(
user.getPermissions().stream().map((String::toLowerCase)).collect(Collectors.toSet()));
} }
} }
@ -235,6 +249,8 @@ public class UserServiceImpl implements UserService {
newUser.setId(newUser.getId().toLowerCase()); newUser.setId(newUser.getId().toLowerCase());
newUser.setGroups( newUser.setGroups(
newUser.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet())); newUser.getGroups().stream().map((String::toLowerCase)).collect(Collectors.toSet()));
newUser.setPermissions(
newUser.getPermissions().stream().map((String::toLowerCase)).collect(Collectors.toSet()));
} }
} }
} }

View File

@ -8,6 +8,7 @@ import pro.taskana.user.api.models.User;
public class UserImpl implements User { public class UserImpl implements User {
private String id; private String id;
private Set<String> groups = Collections.emptySet(); private Set<String> groups = Collections.emptySet();
private Set<String> permissions = Collections.emptySet();
private String firstName; private String firstName;
private String lastName; private String lastName;
private String fullName; private String fullName;
@ -27,6 +28,7 @@ public class UserImpl implements User {
protected UserImpl(UserImpl copyFrom) { protected UserImpl(UserImpl copyFrom) {
this.id = copyFrom.id; this.id = copyFrom.id;
this.groups = copyFrom.groups; this.groups = copyFrom.groups;
this.permissions = copyFrom.permissions;
this.firstName = copyFrom.firstName; this.firstName = copyFrom.firstName;
this.lastName = copyFrom.lastName; this.lastName = copyFrom.lastName;
this.fullName = copyFrom.fullName; this.fullName = copyFrom.fullName;
@ -62,6 +64,16 @@ public class UserImpl implements User {
this.groups = groups; this.groups = groups;
} }
@Override
public Set<String> getPermissions() {
return permissions;
}
@Override
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
@Override @Override
public String getFirstName() { public String getFirstName() {
return firstName; return firstName;
@ -201,6 +213,7 @@ public class UserImpl implements User {
return Objects.hash( return Objects.hash(
id, id,
groups, groups,
permissions,
firstName, firstName,
lastName, lastName,
fullName, fullName,
@ -230,6 +243,7 @@ public class UserImpl implements User {
UserImpl other = (UserImpl) obj; UserImpl other = (UserImpl) obj;
return Objects.equals(id, other.id) return Objects.equals(id, other.id)
&& Objects.equals(groups, other.groups) && Objects.equals(groups, other.groups)
&& Objects.equals(permissions, other.permissions)
&& Objects.equals(firstName, other.firstName) && Objects.equals(firstName, other.firstName)
&& Objects.equals(lastName, other.lastName) && Objects.equals(lastName, other.lastName)
&& Objects.equals(fullName, other.fullName) && Objects.equals(fullName, other.fullName)
@ -251,6 +265,8 @@ public class UserImpl implements User {
+ id + id
+ ", groups=" + ", groups="
+ groups + groups
+ ", permissions="
+ permissions
+ ", firstName=" + ", firstName="
+ firstName + firstName
+ ", lastName=" + ", lastName="

View File

@ -28,6 +28,11 @@ public class UserBuilder implements EntityBuilder<User, UserService> {
return this; return this;
} }
public UserBuilder permissions(Set<String> permissions) {
testUser.setPermissions(permissions);
return this;
}
public UserBuilder firstName(String firstName) { public UserBuilder firstName(String firstName) {
testUser.setFirstName(firstName); testUser.setFirstName(firstName);
return this; return this;

View File

@ -75,6 +75,8 @@ taskana.ldap.userOrglevel2Attribute=orgLevel2
taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchBase=cn=groups
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -53,6 +53,8 @@ taskana.ldap.userOrglevel2Attribute=orgLevel2
taskana.ldap.userOrglevel3Attribute=someDepartement taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase=ou=groups taskana.ldap.groupSearchBase=ou=groups
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -71,6 +71,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupofuniquenames taskana.ldap.groupSearchFilterValue=groupofuniquenames

View File

@ -16,6 +16,11 @@ cn: users
objectclass: top objectclass: top
objectclass: container objectclass: container
dn: cn=other-users,OU=Test,O=TASKANA
cn: users
objectclass: top
objectclass: container
dn: cn=organisation,OU=Test,O=TASKANA dn: cn=organisation,OU=Test,O=TASKANA
cn: organisation cn: organisation
objectclass: top objectclass: top
@ -91,7 +96,7 @@ description: desc
phoneNumber: 012345678 phoneNumber: 012345678
mobileNumber: 09876554321 mobileNumber: 09876554321
email: Titus.Toll@taskana.de email: Titus.Toll@taskana.de
orgLevel1: QWERT orgLevel1: ABC
orgLevel2: DEF/GHI orgLevel2: DEF/GHI
someDepartement: JKL someDepartement: JKL
orgLevel4: MNO/PQR orgLevel4: MNO/PQR
@ -104,6 +109,8 @@ sn: Toll
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Titus Toll cn: Titus Toll
userPassword: teamlead-1 userPassword: teamlead-1
permission: organize
permission: inet
dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -119,6 +126,8 @@ sn: Mustermann
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Max Mustermann cn: Max Mustermann
userPassword: user-1-1 userPassword: user-1-1
permission: organize
permission: inet
dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -134,6 +143,9 @@ sn: Eifrig
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Elena Eifrig cn: Elena Eifrig
userPassword: user-1-2 userPassword: user-1-2
permission: organize
permission: inet
permission: program
dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -211,6 +223,9 @@ sn: Bach
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Thomas Bach cn: Thomas Bach
userPassword: user-2-3 userPassword: user-2-3
permission: organize
permission: inet
permission: program
dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -267,6 +282,9 @@ sn: Meyer
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Wiebke Meyer cn: Wiebke Meyer
userPassword: user-2-7 userPassword: user-2-7
permission: organize
permission: inet
permission: manage
dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -352,6 +370,26 @@ sn: Bio
ou: Organisationseinheit/Organisationseinheit B ou: Organisationseinheit/Organisationseinheit B
cn: Brunhilde Bio cn: Brunhilde Bio
userPassword: user-b-2 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
######################## ########################

View File

@ -30,6 +30,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase=cn=groups taskana.ldap.groupSearchBase=cn=groups
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -22,6 +22,7 @@ taskana.ldap.groupNameAttribute=cn
taskana.ldap.minSearchForLength=3 taskana.ldap.minSearchForLength=3
taskana.ldap.maxNumberOfReturnedAccessIds=50 taskana.ldap.maxNumberOfReturnedAccessIds=50
taskana.ldap.groupsOfUser=memberUid taskana.ldap.groupsOfUser=memberUid
taskana.ldap.userPermissionsAttribute=permission
####### JobScheduler cron expression that specifies when the JobSchedler runs ####### JobScheduler cron expression that specifies when the JobSchedler runs
taskana.jobscheduler.async.cron=0 * * * * * taskana.jobscheduler.async.cron=0 * * * * *

View File

@ -16,6 +16,7 @@ taskana.ldap.userLastnameAttribute=sn
taskana.ldap.userFullnameAttribute=cn taskana.ldap.userFullnameAttribute=cn
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupofuniquenames taskana.ldap.groupSearchFilterValue=groupofuniquenames

View File

@ -23,6 +23,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -21,6 +21,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -109,6 +109,8 @@ sn: Toll
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Titus Toll cn: Titus Toll
userPassword: teamlead-1 userPassword: teamlead-1
permission: organize
permission: inet
dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA dn: uid=user-1-1,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -124,6 +126,8 @@ sn: Mustermann
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Max Mustermann cn: Max Mustermann
userPassword: user-1-1 userPassword: user-1-1
permission: organize
permission: inet
dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA dn: uid=user-1-2,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -139,6 +143,9 @@ sn: Eifrig
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 1
cn: Elena Eifrig cn: Elena Eifrig
userPassword: user-1-2 userPassword: user-1-2
permission: organize
permission: inet
permission: program
dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA dn: uid=user-1-3,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -216,6 +223,9 @@ sn: Bach
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Thomas Bach cn: Thomas Bach
userPassword: user-2-3 userPassword: user-2-3
permission: organize
permission: inet
permission: program
dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA dn: uid=user-2-4,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -272,6 +282,9 @@ sn: Meyer
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2 ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Wiebke Meyer cn: Wiebke Meyer
userPassword: user-2-7 userPassword: user-2-7
permission: organize
permission: inet
permission: manage
dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA dn: uid=user-2-8,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson objectclass: inetorgperson
@ -357,6 +370,10 @@ sn: Bio
ou: Organisationseinheit/Organisationseinheit B ou: Organisationseinheit/Organisationseinheit B
cn: Brunhilde Bio cn: Brunhilde Bio
userPassword: user-b-2 userPassword: user-b-2
permission: organize
permission: inet
permission: siegen
permission: frieden
######################## ########################
# Users in other cn # Users in other cn

View File

@ -413,6 +413,10 @@ public class LdapClient {
return LdapSettings.TASKANA_LDAP_USER_MEMBER_OF_GROUP_ATTRIBUTE.getValueFromEnv(env); 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() { public String getGroupSearchBase() {
return LdapSettings.TASKANA_LDAP_GROUP_SEARCH_BASE.getValueFromEnv(env); return LdapSettings.TASKANA_LDAP_GROUP_SEARCH_BASE.getValueFromEnv(env);
} }
@ -538,6 +542,7 @@ public class LdapClient {
return new String[] { return new String[] {
getUserIdAttribute(), getUserIdAttribute(),
getUserMemberOfGroupAttribute(), getUserMemberOfGroupAttribute(),
getUserPermissionsAttribute(),
getUserFirstnameAttribute(), getUserFirstnameAttribute(),
getUserLastnameAttribute(), getUserLastnameAttribute(),
getUserFullnameAttribute(), getUserFullnameAttribute(),
@ -630,6 +635,20 @@ public class LdapClient {
return groups; return groups;
} }
private Set<String> getPermissionIdsFromContext(final DirContextOperations context) {
String[] permissionAttributes = context.getStringAttributes(getUserPermissionsAttribute());
Set<String> 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. */ /** Context Mapper for user entries. */
class GroupContextMapper extends AbstractContextMapper<AccessIdRepresentationModel> { class GroupContextMapper extends AbstractContextMapper<AccessIdRepresentationModel> {
@ -650,6 +669,7 @@ public class LdapClient {
final User user = new UserImpl(); final User user = new UserImpl();
user.setId(getUserIdFromContext(context)); user.setId(getUserIdFromContext(context));
user.setGroups(getGroupIdsFromContext(context)); user.setGroups(getGroupIdsFromContext(context));
user.setPermissions(getPermissionIdsFromContext(context));
user.setFirstName(context.getStringAttribute(getUserFirstnameAttribute())); user.setFirstName(context.getStringAttribute(getUserFirstnameAttribute()));
user.setLastName(context.getStringAttribute(getUserLastnameAttribute())); user.setLastName(context.getStringAttribute(getUserLastnameAttribute()));
user.setFullName(context.getStringAttribute(getUserFullnameAttribute())); user.setFullName(context.getStringAttribute(getUserFullnameAttribute()));

View File

@ -19,6 +19,7 @@ enum LdapSettings {
TASKANA_LDAP_USER_ORG_LEVEL_3_ATTRIBUTE("taskana.ldap.userOrglevel3Attribute"), TASKANA_LDAP_USER_ORG_LEVEL_3_ATTRIBUTE("taskana.ldap.userOrglevel3Attribute"),
TASKANA_LDAP_USER_ORG_LEVEL_4_ATTRIBUTE("taskana.ldap.userOrglevel4Attribute"), TASKANA_LDAP_USER_ORG_LEVEL_4_ATTRIBUTE("taskana.ldap.userOrglevel4Attribute"),
TASKANA_LDAP_USER_MEMBER_OF_GROUP_ATTRIBUTE("taskana.ldap.userMemberOfGroupAttribute"), 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_GROUP_SEARCH_BASE("taskana.ldap.groupSearchBase"),
TASKANA_LDAP_BASE_DN("taskana.ldap.baseDn"), TASKANA_LDAP_BASE_DN("taskana.ldap.baseDn"),
TASKANA_LDAP_GROUP_SEARCH_FILTER_NAME("taskana.ldap.groupSearchFilterName"), TASKANA_LDAP_GROUP_SEARCH_FILTER_NAME("taskana.ldap.groupSearchFilterName"),

View File

@ -62,7 +62,7 @@ public class UserInfoRefreshJob extends AbstractTaskanaJob {
.map(refreshUserPostprocessorManager::processUserAfterRefresh) .map(refreshUserPostprocessorManager::processUserAfterRefresh)
.collect(Collectors.toList()); .collect(Collectors.toList());
addExistingConfigurationDataToUsers(usersAfterProcessing); addExistingConfigurationDataToUsers(usersAfterProcessing);
clearExistingUsersAndGroups(); clearExistingUsersAndGroupsAndPermissions();
insertNewUsers(usersAfterProcessing); insertNewUsers(usersAfterProcessing);
LOGGER.info("Job to refresh all user info has finished."); LOGGER.info("Job to refresh all user info has finished.");
@ -72,18 +72,18 @@ public class UserInfoRefreshJob extends AbstractTaskanaJob {
} }
} }
private void clearExistingUsersAndGroups() { private void clearExistingUsersAndGroupsAndPermissions() {
sqlConnectionRunner.runWithConnection( sqlConnectionRunner.runWithConnection(
connection -> { connection -> {
if (LOGGER.isDebugEnabled()) { 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); PreparedStatement statement = connection.prepareStatement(sql);
statement.execute(); statement.execute();
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Successfully deleted all users and groups"); LOGGER.debug("Successfully deleted all users, groups and permissions");
} }
if (!connection.getAutoCommit()) { if (!connection.getAutoCommit()) {

View File

@ -22,6 +22,7 @@ public class UserRepresentationModelAssembler
UserRepresentationModel repModel = new UserRepresentationModel(); UserRepresentationModel repModel = new UserRepresentationModel();
repModel.setUserId(entity.getId()); repModel.setUserId(entity.getId());
repModel.setGroups(entity.getGroups()); repModel.setGroups(entity.getGroups());
repModel.setPermissions(entity.getPermissions());
repModel.setFirstName(entity.getFirstName()); repModel.setFirstName(entity.getFirstName());
repModel.setLastName(entity.getLastName()); repModel.setLastName(entity.getLastName());
repModel.setFullName(entity.getFullName()); repModel.setFullName(entity.getFullName());
@ -43,6 +44,7 @@ public class UserRepresentationModelAssembler
UserImpl user = new UserImpl(); UserImpl user = new UserImpl();
user.setId(repModel.getUserId()); user.setId(repModel.getUserId());
user.setGroups(repModel.getGroups()); user.setGroups(repModel.getGroups());
user.setPermissions(repModel.getPermissions());
user.setFirstName(repModel.getFirstName()); user.setFirstName(repModel.getFirstName());
user.setLastName(repModel.getLastName()); user.setLastName(repModel.getLastName());
user.setFullName(repModel.getFullName()); user.setFullName(repModel.getFullName());

View File

@ -14,6 +14,8 @@ public class UserRepresentationModel extends RepresentationModel<UserRepresentat
@NotNull private String userId; @NotNull private String userId;
/** The groups of the User. */ /** The groups of the User. */
private Set<String> groups; private Set<String> groups;
/** The permissions of the User. */
private Set<String> permissions;
/** /**
* The domains of the User. * The domains of the User.
* *
@ -62,6 +64,14 @@ public class UserRepresentationModel extends RepresentationModel<UserRepresentat
this.groups = groups; this.groups = groups;
} }
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public String getFirstName() { public String getFirstName() {
return firstName; return firstName;
} }
@ -172,6 +182,7 @@ public class UserRepresentationModel extends RepresentationModel<UserRepresentat
super.hashCode(), super.hashCode(),
userId, userId,
groups, groups,
permissions,
domains, domains,
firstName, firstName,
lastName, lastName,
@ -201,6 +212,7 @@ public class UserRepresentationModel extends RepresentationModel<UserRepresentat
UserRepresentationModel other = (UserRepresentationModel) obj; UserRepresentationModel other = (UserRepresentationModel) obj;
return Objects.equals(userId, other.userId) return Objects.equals(userId, other.userId)
&& Objects.equals(groups, other.groups) && Objects.equals(groups, other.groups)
&& Objects.equals(permissions, other.permissions)
&& Objects.equals(domains, other.domains) && Objects.equals(domains, other.domains)
&& Objects.equals(firstName, other.firstName) && Objects.equals(firstName, other.firstName)
&& Objects.equals(lastName, other.lastName) && Objects.equals(lastName, other.lastName)

View File

@ -215,6 +215,7 @@ class LdapClientTest {
{"taskana.ldap.userSearchFilterName", "objectclass"}, {"taskana.ldap.userSearchFilterName", "objectclass"},
{"taskana.ldap.groupsOfUser", "memberUid"}, {"taskana.ldap.groupsOfUser", "memberUid"},
{"taskana.ldap.groupNameAttribute", "cn"}, {"taskana.ldap.groupNameAttribute", "cn"},
{"taskana.ldap.userPermissionsAttribute", "permission"},
{"taskana.ldap.groupSearchFilterValue", "groupOfUniqueNames"}, {"taskana.ldap.groupSearchFilterValue", "groupOfUniqueNames"},
{"taskana.ldap.groupSearchFilterName", "objectclass"}, {"taskana.ldap.groupSearchFilterName", "objectclass"},
{"taskana.ldap.groupSearchBase", "ou=groups"}, {"taskana.ldap.groupSearchBase", "ou=groups"},
@ -230,7 +231,7 @@ class LdapClientTest {
{"taskana.ldap.userOrglevel1Attribute", "orgLevel1"}, {"taskana.ldap.userOrglevel1Attribute", "orgLevel1"},
{"taskana.ldap.userOrglevel2Attribute", "orgLevel2"}, {"taskana.ldap.userOrglevel2Attribute", "orgLevel2"},
{"taskana.ldap.userOrglevel3Attribute", "orgLevel3"}, {"taskana.ldap.userOrglevel3Attribute", "orgLevel3"},
{"taskana.ldap.userOrglevel4Attribute", "orgLevel4"}, {"taskana.ldap.userOrglevel4Attribute", "orgLevel4"}
}) })
.forEach( .forEach(
strings -> strings ->

View File

@ -91,6 +91,23 @@ class UserInfoRefreshJobIntTest {
.hasSameSizeAs(ldapGroups) .hasSameSizeAs(ldapGroups)
.containsExactlyElementsOf(ldapGroups); .containsExactlyElementsOf(ldapGroups);
} }
// validate permissions
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
List<String> permissionIds = getPermissionInfo(connection, user.getId());
permissionIds.sort(Comparator.naturalOrder());
User ldapUser = ldapusers.get(i);
List<String> ldapPermissions =
ldapUser.getPermissions().stream()
.sorted(Comparator.naturalOrder())
.collect(Collectors.toList());
assertThat(permissionIds)
.hasSameSizeAs(ldapPermissions)
.containsExactlyElementsOf(ldapPermissions);
}
} }
} }
@ -142,4 +159,19 @@ class UserInfoRefreshJobIntTest {
} }
return groupIds; return groupIds;
} }
private List<String> getPermissionInfo(Connection connection, String userId) throws Exception {
List<String> 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;
}
} }

View File

@ -184,6 +184,7 @@ class UserControllerIntTest {
UserRepresentationModel newUser = new UserRepresentationModel(); UserRepresentationModel newUser = new UserRepresentationModel();
newUser.setUserId("12345"); newUser.setUserId("12345");
newUser.setGroups(Set.of("group1", "group2")); newUser.setGroups(Set.of("group1", "group2"));
newUser.setPermissions(Set.of("perm1", "perm2"));
newUser.setFirstName("Hans"); newUser.setFirstName("Hans");
newUser.setLastName("Georg"); newUser.setLastName("Georg");
newUser.setFullName("Georg, Hans"); newUser.setFullName("Georg, Hans");

View File

@ -29,6 +29,7 @@ class UserRepresentationModelAssemblerTest {
UserImpl user = (UserImpl) userService.newUser(); UserImpl user = (UserImpl) userService.newUser();
user.setId("user-1-2"); user.setId("user-1-2");
user.setGroups(Set.of("group1", "group2")); user.setGroups(Set.of("group1", "group2"));
user.setPermissions(Set.of("perm1", "perm2"));
user.setFirstName("Hans"); user.setFirstName("Hans");
user.setLastName("Georg"); user.setLastName("Georg");
user.setFullName("Hans Georg"); user.setFullName("Hans Georg");
@ -52,6 +53,7 @@ class UserRepresentationModelAssemblerTest {
UserRepresentationModel repModel = new UserRepresentationModel(); UserRepresentationModel repModel = new UserRepresentationModel();
repModel.setUserId("user-1-2"); repModel.setUserId("user-1-2");
repModel.setGroups(Set.of("group1", "group2")); repModel.setGroups(Set.of("group1", "group2"));
repModel.setPermissions(Set.of("perm1", "perm2"));
repModel.setFirstName("Hans"); repModel.setFirstName("Hans");
repModel.setLastName("Georg"); repModel.setLastName("Georg");
repModel.setFullName("Hans Georg"); repModel.setFullName("Hans Georg");
@ -75,6 +77,7 @@ class UserRepresentationModelAssemblerTest {
UserImpl user = (UserImpl) userService.newUser(); UserImpl user = (UserImpl) userService.newUser();
user.setId("user-1-2"); user.setId("user-1-2");
user.setGroups(Set.of("group1", "group2")); user.setGroups(Set.of("group1", "group2"));
user.setPermissions(Set.of("perm1", "perm2"));
user.setFirstName("Hans"); user.setFirstName("Hans");
user.setLastName("Georg"); user.setLastName("Georg");
user.setFullName("Hans Georg"); user.setFullName("Hans Georg");
@ -104,6 +107,7 @@ class UserRepresentationModelAssemblerTest {
assertThat(entity.getId()).isEqualTo(repModel.getUserId()); assertThat(entity.getId()).isEqualTo(repModel.getUserId());
assertThat(entity.getGroups()).isEqualTo(repModel.getGroups()); assertThat(entity.getGroups()).isEqualTo(repModel.getGroups());
assertThat(entity.getPermissions()).isEqualTo(repModel.getPermissions());
assertThat(entity.getFirstName()).isEqualTo(repModel.getFirstName()); assertThat(entity.getFirstName()).isEqualTo(repModel.getFirstName());
assertThat(entity.getLastName()).isEqualTo(repModel.getLastName()); assertThat(entity.getLastName()).isEqualTo(repModel.getLastName());
assertThat(entity.getFullName()).isEqualTo(repModel.getFullName()); assertThat(entity.getFullName()).isEqualTo(repModel.getFullName());

View File

@ -28,6 +28,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames

View File

@ -34,6 +34,7 @@ taskana.ldap.userOrglevel3Attribute=someDepartement
taskana.ldap.userOrglevel4Attribute=orgLevel4 taskana.ldap.userOrglevel4Attribute=orgLevel4
taskana.ldap.userIdAttribute=uid taskana.ldap.userIdAttribute=uid
taskana.ldap.userMemberOfGroupAttribute=memberOf taskana.ldap.userMemberOfGroupAttribute=memberOf
taskana.ldap.userPermissionsAttribute=permission
taskana.ldap.groupSearchBase= taskana.ldap.groupSearchBase=
taskana.ldap.groupSearchFilterName=objectclass taskana.ldap.groupSearchFilterName=objectclass
taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupSearchFilterValue=groupOfUniqueNames