Skip to content

Commit

Permalink
Closes Taskana#2302 - Adds Permissions to Users
Browse files Browse the repository at this point in the history
- Extends LDAP client to read permission attributes from users
- Extends database schema with PERMISSION_INFO table and sets schema version to 6.3.0
- Exetends User models (Builder, Mapper,..) to have permission attribute

Signed-off-by: Kálmán Képes <2853992+nyuuyn@users.noreply.github.com>
  • Loading branch information
nyuuyn committed Jun 27, 2023
1 parent 1a965a0 commit 2a778f7
Show file tree
Hide file tree
Showing 39 changed files with 385 additions and 7 deletions.
Expand Up @@ -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;
Expand Up @@ -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;
Expand Up @@ -355,6 +355,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
Expand Down
@@ -0,0 +1,12 @@
-- this script updates the TASKANA database schema from version 5.10.0 to version 6.3.0.
SET SCHEMA %schemaName%;

INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED)
VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.3.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)
);
Expand Up @@ -361,6 +361,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
Expand Down
@@ -0,0 +1,11 @@
-- this script updates the TASKANA database schema from version 5.10.0 to version 6.3.0.

INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED)
VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.3.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)
);
Expand Up @@ -354,6 +354,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
Expand Down
@@ -0,0 +1,12 @@
-- this script updates the TASKANA database schema from version 5.10.0 to version 6.3.0.
ALTER SESSION SET CURRENT_SCHEMA = %schemaName%;

INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED)
VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '6.3.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)
);
Expand Up @@ -358,6 +358,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
Expand Down
@@ -0,0 +1,13 @@
-- this script updates the TASKANA database schema from version 5.1.0 to version 5.2.0.

SET search_path = %schemaName%;

INSERT INTO TASKANA_SCHEMA_VERSION (ID, VERSION, CREATED)
VALUES (nextval('TASKANA_SCHEMA_VERSION_ID_SEQ'), '5.10.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)
);
Expand Up @@ -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
Expand Down
Expand Up @@ -312,6 +312,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<DynamicTest>
Expand Down Expand Up @@ -483,12 +497,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");
}
}

Expand Down Expand Up @@ -549,6 +566,34 @@ Stream<DynamicTest> should_UpdateGroups() {
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().groups(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()
Expand Down Expand Up @@ -722,6 +767,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() {
Expand Down Expand Up @@ -826,6 +884,20 @@ 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().groups(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() {
Expand Down
Expand Up @@ -34,6 +34,20 @@ public interface User {
*/
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.
*
Expand Down
Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -50,6 +54,9 @@ public interface UserMapper {
@SelectProvider(type = UserMapperSqlProvider.class, method = "findGroupsById")
Set<String> findGroupsById(String id);

@SelectProvider(type = UserMapperSqlProvider.class, method = "findPermissionsById")
Set<String> findPermissionsById(String id);

@InsertProvider(type = UserMapperSqlProvider.class, method = "insert")
void insert(User user);

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

Expand Down
Expand Up @@ -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 + ")";
}
Expand All @@ -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 "
+ "<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() {
return "UPDATE USER_INFO "
+ "SET FIRST_NAME = #{firstName}, "
Expand Down
Expand Up @@ -124,6 +124,10 @@ public User updateUser(User userToUpdate)
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()) {
Expand Down Expand Up @@ -181,6 +185,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 {
Expand Down Expand Up @@ -209,6 +216,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()));
}
}

Expand All @@ -230,6 +239,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()));
}
}
}

0 comments on commit 2a778f7

Please sign in to comment.