This is an automated email from the ASF dual-hosted git repository.

pefernan pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git


The following commit(s) were added to refs/heads/main by this push:
     new 0c347c5b4e incubator-kie-issues#1837: Ensure 
`DefaultUserTasksLifeCycle` checks the user Identity calculating the 
`allowedTransitions` (#3848)
0c347c5b4e is described below

commit 0c347c5b4e0c176c303f6899df4c8c857140aa3a
Author: Pere Fernández <[email protected]>
AuthorDate: Thu Feb 20 09:23:27 2025 +0100

    incubator-kie-issues#1837: Ensure `DefaultUserTasksLifeCycle` checks the 
user Identity calculating the `allowedTransitions` (#3848)
    
    * incubator-kie-issues#1837: Ensure DefaultUserTasksLifeCycle checks the 
user Identity calculating the allowedTransitions
    
    * Removed unnecessary outputs modification
---
 .../usertask/lifecycle/UserTaskLifeCycle.java      |  2 +-
 .../kogito/usertask/impl/UserTaskServiceImpl.java  |  2 +-
 .../impl/lifecycle/DefaultUserTaskLifeCycle.java   |  5 +-
 .../jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java  | 94 ++++++++++++++++++++++
 .../jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java  | 94 ++++++++++++++++++++++
 5 files changed, 193 insertions(+), 4 deletions(-)

diff --git 
a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java
 
b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java
index 9fbe1cdcc2..16d62b304a 100644
--- 
a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java
+++ 
b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java
@@ -51,6 +51,6 @@ public interface UserTaskLifeCycle {
 
     UserTaskTransitionToken newAbortTransitionToken(UserTaskInstance 
userTaskInstance, Map<String, Object> data);
 
-    List<UserTaskTransition> allowedTransitions(UserTaskInstance ut);
+    List<UserTaskTransition> allowedTransitions(UserTaskInstance ut, 
IdentityProvider identity);
 
 }
diff --git 
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java
 
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java
index 3f1009245e..f2148e5d63 100644
--- 
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java
+++ 
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java
@@ -98,7 +98,7 @@ public class UserTaskServiceImpl implements UserTaskService {
         }
         UserTaskInstance ut = userTaskInstance.get();
         UserTaskLifeCycle userTaskLifeCycle = 
application.config().get(UserTaskConfig.class).userTaskLifeCycle();
-        List<UserTaskTransition> transitions = 
userTaskLifeCycle.allowedTransitions(ut);
+        List<UserTaskTransition> transitions = 
userTaskLifeCycle.allowedTransitions(ut, identity);
         return toUserTaskTransitionView(transitions);
     }
 
diff --git 
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java
 
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java
index 986de80780..b237ca7850 100644
--- 
a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java
+++ 
b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java
@@ -87,7 +87,8 @@ public class DefaultUserTaskLifeCycle implements 
UserTaskLifeCycle {
     }
 
     @Override
-    public List<UserTaskTransition> allowedTransitions(UserTaskInstance 
userTaskInstance) {
+    public List<UserTaskTransition> allowedTransitions(UserTaskInstance 
userTaskInstance, IdentityProvider identity) {
+        checkPermission(userTaskInstance, identity);
         return transitions.stream().filter(t -> 
t.source().equals(userTaskInstance.getStatus())).toList();
     }
 
@@ -258,7 +259,7 @@ public class DefaultUserTaskLifeCycle implements 
UserTaskLifeCycle {
             }
         }
 
-        throw new UserTaskInstanceNotAuthorizedException("user " + user + " 
with roles " + roles + " not autorized to perform an operation on user task " + 
userTaskInstance.getId());
+        throw new UserTaskInstanceNotAuthorizedException("user " + user + " 
with roles " + roles + " not authorized to perform an operation on user task " 
+ userTaskInstance.getId());
     }
 
 }
diff --git 
a/quarkus/integration-tests/integration-tests-quarkus-usertasks/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
 
b/quarkus/integration-tests/integration-tests-quarkus-usertasks/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
index b2c7b29e11..7ad7745a39 100644
--- 
a/quarkus/integration-tests/integration-tests-quarkus-usertasks/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
+++ 
b/quarkus/integration-tests/integration-tests-quarkus-usertasks/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
@@ -19,19 +19,24 @@
 
 package org.jbpm.userTask.jpa.it;
 
+import java.util.List;
 import java.util.Map;
 
 import org.acme.travels.Address;
 import org.acme.travels.Traveller;
 import org.junit.jupiter.api.Test;
 import org.kie.kogito.testcontainers.quarkus.PostgreSqlQuarkusTestResource;
+import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle;
+import org.kie.kogito.usertask.lifecycle.UserTaskState;
 import org.kie.kogito.usertask.model.TransitionInfo;
+import org.kie.kogito.usertask.view.UserTaskTransitionView;
 
 import io.quarkus.test.common.QuarkusTestResource;
 import io.quarkus.test.junit.QuarkusIntegrationTest;
 import io.restassured.http.ContentType;
 
 import static io.restassured.RestAssured.given;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.CoreMatchers.*;
 
 @QuarkusIntegrationTest
@@ -189,4 +194,93 @@ public class UserTaskLifeCycleIT extends BaseUserTaskIT {
                 .then()
                 .statusCode(404);
     }
+
+    @Test
+    public void testUserTaskAllowedTransitions() {
+        Traveller traveller = new Traveller("John", "Doe", 
"[email protected]", "American", new Address("main street", "Boston", 
"10005", "US"));
+
+        String processId = startProcessInstance(traveller);
+
+        String taskId = given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "department-managers")
+                .get(USER_TASKS_ENDPOINT)
+                .then()
+                .statusCode(200)
+                .body("$.size()", is(1))
+                .extract()
+                .path("[0].id");
+
+        List<UserTaskTransitionView> transitions = 
given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .extract()
+                .jsonPath().getList(".", UserTaskTransitionView.class);
+
+        assertThat(transitions)
+                .hasSize(5)
+                .satisfiesExactlyInAnyOrder(transition -> 
matchTransitionView(transition, DefaultUserTaskLifeCycle.RELEASE, 
DefaultUserTaskLifeCycle.RESERVED, DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.COMPLETE, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.COMPLETED),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.REASSIGN, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.FAIL, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.ERROR),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.SKIP, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.OBSOLETE));
+
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "john")
+                .queryParam("group", "it")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(500);
+
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .body(new TransitionInfo("release", Map.of()))
+                .post(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .body("id", equalTo(taskId))
+                .body("status.name", equalTo("Ready"))
+                .body("status.terminate", nullValue());
+
+        transitions = given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .extract()
+                .jsonPath().getList(".", UserTaskTransitionView.class);
+
+        assertThat(transitions)
+                .hasSize(4)
+                .satisfiesExactlyInAnyOrder(transition -> 
matchTransitionView(transition, DefaultUserTaskLifeCycle.CLAIM, 
DefaultUserTaskLifeCycle.ACTIVE, DefaultUserTaskLifeCycle.RESERVED),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.REASSIGN, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.FAIL, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.ERROR),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.SKIP, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.OBSOLETE));
+
+        given()
+                .accept(ContentType.JSON)
+                .when()
+                .delete("/{processId}/{id}", PROCESS_ID, processId)
+                .then()
+                .statusCode(200);
+    }
+
+    private void matchTransitionView(UserTaskTransitionView transition, String 
expectedId, UserTaskState expectedSource, UserTaskState expectedTarget) {
+        assertThat(transition)
+                .hasFieldOrPropertyWithValue("transitionId", expectedId)
+                .hasFieldOrPropertyWithValue("source", expectedSource)
+                .hasFieldOrPropertyWithValue("target", expectedTarget);
+    }
 }
diff --git 
a/springboot/integration-tests/integration-tests-springboot-usertasks-it/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
 
b/springboot/integration-tests/integration-tests-springboot-usertasks-it/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
index 0f04acf956..2b9621626c 100644
--- 
a/springboot/integration-tests/integration-tests-springboot-usertasks-it/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
+++ 
b/springboot/integration-tests/integration-tests-springboot-usertasks-it/src/test/java/org/jbpm/userTask/jpa/it/UserTaskLifeCycleIT.java
@@ -19,6 +19,7 @@
 
 package org.jbpm.userTask.jpa.it;
 
+import java.util.List;
 import java.util.Map;
 
 import org.acme.travels.Address;
@@ -26,13 +27,17 @@ import org.acme.travels.Traveller;
 import org.junit.jupiter.api.Test;
 import org.kie.kogito.it.KogitoSpringbootApplication;
 import 
org.kie.kogito.testcontainers.springboot.PostgreSqlSpringBootTestResource;
+import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle;
+import org.kie.kogito.usertask.lifecycle.UserTaskState;
 import org.kie.kogito.usertask.model.TransitionInfo;
+import org.kie.kogito.usertask.view.UserTaskTransitionView;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ContextConfiguration;
 
 import io.restassured.http.ContentType;
 
 import static io.restassured.RestAssured.given;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.CoreMatchers.*;
 
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
classes = KogitoSpringbootApplication.class)
@@ -193,4 +198,93 @@ public class UserTaskLifeCycleIT extends BaseUserTaskIT {
                 .then()
                 .statusCode(404);
     }
+
+    @Test
+    public void testUserTaskAllowedTransitions() {
+        Traveller traveller = new Traveller("John", "Doe", 
"[email protected]", "American", new Address("main street", "Boston", 
"10005", "US"));
+
+        String processId = startProcessInstance(traveller);
+
+        String taskId = given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "department-managers")
+                .get(USER_TASKS_ENDPOINT)
+                .then()
+                .statusCode(200)
+                .body("$.size()", is(1))
+                .extract()
+                .path("[0].id");
+
+        List<UserTaskTransitionView> transitions = 
given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .extract()
+                .jsonPath().getList(".", UserTaskTransitionView.class);
+
+        assertThat(transitions)
+                .hasSize(5)
+                .satisfiesExactlyInAnyOrder(transition -> 
matchTransitionView(transition, DefaultUserTaskLifeCycle.RELEASE, 
DefaultUserTaskLifeCycle.RESERVED, DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.COMPLETE, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.COMPLETED),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.REASSIGN, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.FAIL, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.ERROR),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.SKIP, DefaultUserTaskLifeCycle.RESERVED, 
DefaultUserTaskLifeCycle.OBSOLETE));
+
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "john")
+                .queryParam("group", "it")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(500);
+
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .body(new TransitionInfo("release", Map.of()))
+                .post(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .body("id", equalTo(taskId))
+                .body("status.name", equalTo("Ready"))
+                .body("status.terminate", nullValue());
+
+        transitions = given().contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", "manager")
+                .queryParam("group", "managers")
+                .get(USER_TASKS_INSTANCE_TRANSITION_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .extract()
+                .jsonPath().getList(".", UserTaskTransitionView.class);
+
+        assertThat(transitions)
+                .hasSize(4)
+                .satisfiesExactlyInAnyOrder(transition -> 
matchTransitionView(transition, DefaultUserTaskLifeCycle.CLAIM, 
DefaultUserTaskLifeCycle.ACTIVE, DefaultUserTaskLifeCycle.RESERVED),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.REASSIGN, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.ACTIVE),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.FAIL, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.ERROR),
+                        transition -> matchTransitionView(transition, 
DefaultUserTaskLifeCycle.SKIP, DefaultUserTaskLifeCycle.ACTIVE, 
DefaultUserTaskLifeCycle.OBSOLETE));
+
+        given()
+                .accept(ContentType.JSON)
+                .when()
+                .delete("/{processId}/{id}", PROCESS_ID, processId)
+                .then()
+                .statusCode(200);
+    }
+
+    private void matchTransitionView(UserTaskTransitionView transition, String 
expectedId, UserTaskState expectedSource, UserTaskState expectedTarget) {
+        assertThat(transition)
+                .hasFieldOrPropertyWithValue("transitionId", expectedId)
+                .hasFieldOrPropertyWithValue("source", expectedSource)
+                .hasFieldOrPropertyWithValue("target", expectedTarget);
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to