You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by sk...@apache.org on 2018/11/30 09:53:00 UTC

[syncope] branch master updated: [SYNCOPE-1394] Added unclaim capability for user requests

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

skylark17 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new f000afa  [SYNCOPE-1394] Added unclaim capability for user requests
f000afa is described below

commit f000afa7e15cafb5f7fc26bd5332b34e602c4f6c
Author: skylark17 <ma...@tirasa.net>
AuthorDate: Fri Nov 30 10:15:16 2018 +0100

    [SYNCOPE-1394] Added unclaim capability for user requests
---
 .../wicket/markup/html/form/ActionLink.java        |  1 +
 .../wicket/markup/html/form/ActionPanel.properties |  4 ++
 .../panels/UserRequestFormDirectoryPanel.java      | 36 +++++++++++-
 .../client/console/rest/UserRequestRestClient.java |  4 ++
 .../client/console/pages/UserRequests.properties   |  3 +-
 .../console/pages/UserRequests_it.properties       |  3 +-
 .../console/pages/UserRequests_ja.properties       |  3 +-
 .../console/pages/UserRequests_pt_BR.properties    |  3 +-
 .../console/pages/UserRequests_ru.properties       |  3 +-
 .../widgets/UserRequestFormsWidget.properties      |  2 +-
 .../widgets/UserRequestFormsWidget_it.properties   |  2 +-
 .../widgets/UserRequestFormsWidget_ja.properties   |  2 +-
 .../UserRequestFormsWidget_pt_BR.properties        |  2 +-
 .../widgets/UserRequestFormsWidget_ru.properties   |  2 +-
 .../syncope/common/lib/to/UserRequestForm.java     | 10 ++--
 .../common/lib/types/FlowableEntitlement.java      |  2 +
 .../core/flowable/api/UserRequestHandler.java      |  8 +++
 .../flowable/impl/FlowableUserRequestHandler.java  | 42 +++++++++++---
 .../syncope/core/logic/UserRequestLogic.java       |  9 +++
 .../rest/api/service/UserRequestService.java       | 14 ++++-
 .../rest/cxf/service/UserRequestServiceImpl.java   |  5 ++
 .../apache/syncope/fit/core/UserSelfITCase.java    | 66 +++++++++++++++++++---
 22 files changed, 192 insertions(+), 34 deletions(-)

diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index 7f8ade0..dd53e21 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@ -68,6 +68,7 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         REQUEST_PASSWORD_RESET("update"),
         DRYRUN("execute"),
         CLAIM("claim"),
+        UNCLAIM("unclaim"),
         SELECT("read"),
         CLOSE("read"),
         EXPORT("read"),
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
index af1d580..698e4cc 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionPanel.properties
@@ -142,6 +142,10 @@ claim.class=fa fa-ticket
 claim.title=claim
 claim.alt=claim icon
 
+unclaim.class=fa fa-undo
+unclaim.title=unclaim
+unclaim.alt=unclaim icon
+
 select.class=glyphicon glyphicon-ok
 select.title=select
 select.alt=select icon
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
index bf82896..c6b254b 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
@@ -132,7 +132,7 @@ public class UserRequestFormDirectoryPanel
         columns.add(new DatePropertyColumn<>(
                 new ResourceModel("dueDate"), "dueDate", "dueDate"));
         columns.add(new PropertyColumn<>(
-                new ResourceModel("owner"), "owner", "owner"));
+                new ResourceModel("assignee"), "assignee", "assignee"));
 
         return columns;
     }
@@ -152,10 +152,32 @@ public class UserRequestFormDirectoryPanel
                 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
                 target.add(container);
             }
+
         }, ActionLink.ActionType.CLAIM, FlowableEntitlement.USER_REQUEST_FORM_CLAIM);
 
         panel.add(new ActionLink<UserRequestForm>() {
 
+            private static final long serialVersionUID = -4496313424398213416L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final UserRequestForm ignore) {
+                unclaimForm(model.getObject().getTaskId());
+                SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                UserRequestFormDirectoryPanel.this.getTogglePanel().close(target);
+                target.add(container);
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+
+            @Override
+            protected boolean statusCondition(final UserRequestForm modelObject) {
+                return SyncopeConsoleSession.get().getSelfTO().getUsername().
+                        equals(model.getObject().getAssignee());
+            }
+
+        }, ActionLink.ActionType.UNCLAIM, FlowableEntitlement.USER_REQUEST_FORM_UNCLAIM);
+
+        panel.add(new ActionLink<UserRequestForm>() {
+
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
@@ -188,7 +210,7 @@ public class UserRequestFormDirectoryPanel
             @Override
             protected boolean statusCondition(final UserRequestForm modelObject) {
                 return SyncopeConsoleSession.get().getSelfTO().getUsername().
-                        equals(model.getObject().getOwner());
+                        equals(model.getObject().getAssignee());
             }
 
         }, ActionLink.ActionType.MANAGE_APPROVAL, FlowableEntitlement.USER_REQUEST_FORM_SUBMIT);
@@ -237,7 +259,7 @@ public class UserRequestFormDirectoryPanel
             @Override
             protected boolean statusCondition(final UserRequestForm modelObject) {
                 return SyncopeConsoleSession.get().getSelfTO().getUsername().
-                        equals(model.getObject().getOwner());
+                        equals(model.getObject().getAssignee());
             }
 
         }, ActionLink.ActionType.EDIT_APPROVAL, FlowableEntitlement.USER_REQUEST_FORM_SUBMIT);
@@ -305,6 +327,14 @@ public class UserRequestFormDirectoryPanel
         }
     }
 
+    private void unclaimForm(final String taskId) {
+        try {
+            restClient.unclaimForm(taskId);
+        } catch (SyncopeClientException scee) {
+            SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + scee.getMessage());
+        }
+    }
+
     private class FormUserWizardBuilder extends UserWizardBuilder {
 
         private static final long serialVersionUID = 1854981134836384069L;
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
index 8d3b03b..7921249 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/UserRequestRestClient.java
@@ -73,6 +73,10 @@ public class UserRequestRestClient extends BaseRestClient {
         return getService(UserRequestService.class).claimForm(taskKey);
     }
 
+    public UserRequestForm unclaimForm(final String taskKey) {
+        return getService(UserRequestService.class).unclaimForm(taskKey);
+    }
+
     public void submitForm(final UserRequestForm form) {
         getService(UserRequestService.class).submitForm(form);
     }
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests.properties
index 9f30811..41ac106 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests.properties
@@ -19,8 +19,9 @@ key=Key
 description=Description
 createTime=Create Time
 dueDate=Due Date
-owner=Owner
+assignee=Assignee
 claim=Claim
+unclaim=Unclaim
 manage=Manage
 userRequests=User Requests
 delete=Delete
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_it.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_it.properties
index 04f5462..e355780 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_it.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_it.properties
@@ -19,8 +19,9 @@ key=Chiave
 description=Descrizione
 createTime=Data di creazione
 dueDate=Scadenza
-owner=Esecutore
+assignee=Esecutore
 claim=Richiedi
+unclaim=Annulla richiesta
 manage=Gestisci
 userRequests=Richieste utente
 delete=Rimuovi
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ja.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ja.properties
index 61fef13..51ae26a 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ja.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ja.properties
@@ -19,8 +19,9 @@ key=\u30ad\u30fc
 description=\u8aac\u660e
 createTime=\u4f5c\u6210\u6642\u523b
 dueDate=\u671f\u9650
-owner=\u30aa\u30fc\u30ca\u30fc
+assignee=\u30aa\u30fc\u30ca\u30fc
 claim=\u7533\u8acb
+unclaim=\u7533\u3057\u7acb\u3066\u3092\u53d6\u308a\u6d88\u3059
 manage=\u7ba1\u7406
 userRequests=User Requests
 delete=\u524a\u9664
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_pt_BR.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_pt_BR.properties
index 65c43f4..e9c3e92 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_pt_BR.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_pt_BR.properties
@@ -19,8 +19,9 @@ key=Chave
 description=Descri\u00e7\u00e3o
 createTime=Tempo de Cria\u00e7\u00e3o
 dueDate=Data acordada
-owner=Propriet\u00e1rio
+assignee=Propriet\u00e1rio
 claim=Requerimento
+unclaim=Cancelar requerimento
 manage=Ger\u00eancia
 userRequests=User Requests
 delete=Excluir
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ru.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ru.properties
index 52b454b..f97c4ef 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ru.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/pages/UserRequests_ru.properties
@@ -26,9 +26,10 @@ createTime=\u0414\u0430\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u
 # dueDate=\u00d0\u00a1\u00d1\u0080\u00d0\u00be\u00d0\u00ba
 dueDate=\u0421\u0440\u043e\u043a
 # owner=\u00d0\u0092\u00d0\u00bb\u00d0\u00b0\u00d0\u00b4\u00d0\u00b5\u00d0\u00bb\u00d0\u00b5\u00d1\u0086
-owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
+assignee=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
 # claim=\u00d0\u0092\u00d1\u008b\u00d0\u00bf\u00d0\u00be\u00d0\u00bb\u00d0\u00bd\u00d0\u00b8\u00d1\u0082\u00d1\u008c \u00d1\u0081\u00d0\u00be\u00d0\u00b3\u00d0\u00bb\u00d0\u00b0\u00d1\u0081\u00d0\u00be\u00d0\u00b2\u00d0\u00b0\u00d0\u00bd\u00d0\u00b8\u00d0\u00b5
 claim=\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043d\u0438\u0435
+unclaim=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441
 # manage=\u00d0\u00a3\u00d0\u00bf\u00d1\u0080\u00d0\u00b0\u00d0\u00b2\u00d0\u00bb\u00d0\u00b5\u00d0\u00bd\u00d0\u00b8\u00d0\u00b5
 manage=\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435
 # approvals=\u00d0\u0097\u00d0\u00b0\u00d1\u008f\u00d0\u00b2\u00d0\u00ba\u00d0\u00b8
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget.properties
index 36e1e2e..753a2d8 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget.properties
@@ -16,5 +16,5 @@
 # under the License.
 alerts.view.all=View all forms
 duedate=Due date
-owner=Owner
+assignee=Assignee
 summary=${number} pending form(s)
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_it.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_it.properties
index bf3e9ad..796636e 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_it.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_it.properties
@@ -16,5 +16,5 @@
 # under the License.
 alerts.view.all=Tutte le form
 duedate=Scadenza
-owner=Assegnato
+assignee=Assegnato
 summary=${number} form pendenti
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ja.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ja.properties
index 0d938e3..ef5d77f 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ja.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ja.properties
@@ -16,5 +16,5 @@
 # under the License.
 alerts.view.all=View all forms
 duedate=\u671f\u9650
-owner=\u30aa\u30fc\u30ca\u30fc
+assignee=\u30aa\u30fc\u30ca\u30fc
 summary=${number} pending form(s)
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_pt_BR.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_pt_BR.properties
index 36e1e2e..753a2d8 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_pt_BR.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_pt_BR.properties
@@ -16,5 +16,5 @@
 # under the License.
 alerts.view.all=View all forms
 duedate=Due date
-owner=Owner
+assignee=Assignee
 summary=${number} pending form(s)
diff --git a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ru.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ru.properties
index e713681..bb1fa45 100644
--- a/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ru.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/widgets/UserRequestFormsWidget_ru.properties
@@ -17,5 +17,5 @@
 #
 alerts.view.all=View all forms
 duedate=\u0421\u0440\u043e\u043a
-owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
+assignee=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
 summary=${number} pending form(s)
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
index 8508c2c..a073a0b 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
@@ -51,7 +51,7 @@ public class UserRequestForm extends BaseBean {
 
     private Date dueDate;
 
-    private String owner;
+    private String assignee;
 
     private UserTO userTO;
 
@@ -129,12 +129,12 @@ public class UserRequestForm extends BaseBean {
         }
     }
 
-    public String getOwner() {
-        return owner;
+    public String getAssignee() {
+        return assignee;
     }
 
-    public void setOwner(final String owner) {
-        this.owner = owner;
+    public void setAssignee(final String assignee) {
+        this.assignee = assignee;
     }
 
     public UserTO getUserTO() {
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
index fbf5b02..0c41c99 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
@@ -42,6 +42,8 @@ public final class FlowableEntitlement {
 
     public static final String USER_REQUEST_FORM_CLAIM = "USER_REQUEST_FORM_CLAIM";
 
+    public static final String USER_REQUEST_FORM_UNCLAIM = "USER_REQUEST_FORM_UNCLAIM";
+
     public static final String USER_REQUEST_FORM_SUBMIT = "USER_REQUEST_FORM_SUBMIT";
 
     public static final String USER_REQUEST_START = "USER_REQUEST_START";
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
index c3c6f0d..f7d7a35 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
@@ -103,6 +103,14 @@ public interface UserRequestHandler {
      * @return updated form
      */
     UserRequestForm claimForm(String taskId);
+    
+    /**
+     * Unclaim a form for a given object.
+     *
+     * @param taskId Workflow task to which the form is associated
+     * @return updated form
+     */
+    UserRequestForm unclaimForm(String taskId);
 
     /**
      * Submit a form.
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
index ac28126..1f539fb 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
@@ -62,6 +62,7 @@ import org.flowable.engine.history.HistoricActivityInstance;
 import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
 import org.flowable.engine.impl.persistence.entity.HistoricFormPropertyEntity;
 import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.identitylink.api.IdentityLinkType;
 import org.flowable.task.api.Task;
 import org.flowable.task.api.TaskQuery;
 import org.flowable.task.api.history.HistoricTaskInstance;
@@ -310,7 +311,7 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         formTO.setDueDate(task.getDueDate());
         formTO.setExecutionId(task.getExecutionId());
         formTO.setFormKey(task.getFormKey());
-        formTO.setOwner(task.getOwner());
+        formTO.setAssignee(task.getAssignee());
 
         return formTO;
     }
@@ -328,7 +329,7 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         formTO.setDueDate(task.getDueDate());
         formTO.setExecutionId(task.getExecutionId());
         formTO.setFormKey(task.getFormKey());
-        formTO.setOwner(task.getOwner());
+        formTO.setAssignee(task.getAssignee());
 
         HistoricActivityInstance historicActivityInstance = engine.getHistoryService().
                 createHistoricActivityInstanceQuery().
@@ -509,8 +510,8 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
                     query.orderByTaskDueDate();
                     break;
 
-                case "owner":
-                    query.orderByTaskOwner();
+                case "assignee":
+                    query.orderByTaskAssignee();
                     break;
 
                 default:
@@ -570,9 +571,21 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
             }
         }
 
+        boolean hasAssignees =
+                engine.getTaskService().getIdentityLinksForTask(taskId).stream().anyMatch(identityLink -> {
+                    return IdentityLinkType.ASSIGNEE.equals(identityLink.getType());
+                });
+        if (hasAssignees) {
+            try {
+                engine.getTaskService().unclaim(taskId);
+            } catch (FlowableException e) {
+                throw new WorkflowException("While unclaiming task " + taskId, e);
+            }
+        }
+
         Task task;
         try {
-            engine.getTaskService().setOwner(taskId, authUser);
+            engine.getTaskService().claim(taskId, authUser);
             task = FlowableRuntimeUtils.createTaskQuery(engine, true).taskId(taskId).singleResult();
         } catch (FlowableException e) {
             throw new WorkflowException("While reading task " + taskId, e);
@@ -581,6 +594,21 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         return FlowableUserRequestHandler.this.getForm(task, parsed.getRight());
     }
 
+    @Override
+    public UserRequestForm unclaimForm(final String taskId) {
+        Pair<Task, TaskFormData> parsed = parseTask(taskId);
+
+        Task task;
+        try {
+            engine.getTaskService().unclaim(taskId);
+            task = FlowableRuntimeUtils.createTaskQuery(engine, true).taskId(taskId).singleResult();
+        } catch (FlowableException e) {
+            throw new WorkflowException("While unclaiming task " + taskId, e);
+        }
+
+        return FlowableUserRequestHandler.this.getForm(task, parsed.getRight());
+    }
+
     private Map<String, String> getPropertiesForSubmit(final UserRequestForm form) {
         Map<String, String> props = new HashMap<>();
         form.getProperties().stream().
@@ -596,9 +624,9 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         Pair<Task, TaskFormData> parsed = parseTask(form.getTaskId());
 
         String authUser = AuthContextUtils.getUsername();
-        if (!parsed.getLeft().getOwner().equals(authUser)) {
+        if (!parsed.getLeft().getAssignee().equals(authUser)) {
             throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
-                    + parsed.getLeft().getOwner() + " but submitted by " + authUser));
+                    + parsed.getLeft().getAssignee() + " but submitted by " + authUser));
         }
 
         String procInstId = parsed.getLeft().getProcessInstanceId();
diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
index edff188..d3851cd 100644
--- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
+++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
@@ -144,6 +144,15 @@ public class UserRequestLogic extends AbstractTransactionalLogic<EntityTO> {
     }
 
     @PreAuthorize("isAuthenticated()")
+    public UserRequestForm unclaimForm(final String taskId) {
+        UserRequestForm form = userRequestHandler.unclaimForm(taskId);
+        securityChecks(form.getUsername(),
+                FlowableEntitlement.USER_REQUEST_FORM_UNCLAIM,
+                "Unclaiming form " + taskId + " not allowed");
+        return form;
+    }
+
+    @PreAuthorize("isAuthenticated()")
     @Transactional(readOnly = true)
     public Pair<Integer, List<UserRequestForm>> getForms(
             final String userKey,
diff --git a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
index 11aac50..c58020a 100644
--- a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
+++ b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
@@ -47,7 +47,8 @@ import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
  */
 @Tag(name = "Flowable")
 @SecurityRequirements({
-    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "BasicAuthentication")
+    ,
     @SecurityRequirement(name = "Bearer") })
 @Path("flowable/userRequests")
 public interface UserRequestService extends JAXRSService {
@@ -114,6 +115,17 @@ public interface UserRequestService extends JAXRSService {
     UserRequestForm claimForm(@NotNull @PathParam("taskId") String taskId);
 
     /**
+     * Cancels request to manage the form for the given task id.
+     *
+     * @param taskId workflow task id
+     * @return the workflow form for the given task id
+     */
+    @POST
+    @Path("forms/{taskId}/unclaim")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    UserRequestForm unclaimForm(@NotNull @PathParam("taskId") String taskId);
+
+    /**
      * Submits a user request form.
      *
      * @param form user request form.
diff --git a/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImpl.java b/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImpl.java
index 188d22e..580ca76 100644
--- a/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImpl.java
+++ b/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImpl.java
@@ -68,6 +68,11 @@ public class UserRequestServiceImpl extends AbstractServiceImpl implements UserR
     public UserRequestForm claimForm(final String taskId) {
         return logic.claimForm(taskId);
     }
+    
+    @Override
+    public UserRequestForm unclaimForm(final String taskId) {
+        return logic.unclaimForm(taskId);
+    }
 
     @Override
     public PagedResult<UserRequestForm> getForms(final UserRequestFormQuery query) {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
index 162cb88..63e1672 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
@@ -106,7 +106,7 @@ public class UserSelfITCase extends AbstractITCase {
     public void createAndApprove() {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
-        // self-create user with membership: goes 'createApproval' with resources and membership but no propagation
+        // 1. self-create user with membership: goes 'createApproval' with resources and membership but no propagation
         UserTO userTO = UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org");
         userTO.getMemberships().add(
                 new MembershipTO.Builder().group("29f96485-729e-4d31-88a1-6fc60e4677f3").build());
@@ -129,7 +129,7 @@ public class UserSelfITCase extends AbstractITCase {
             assertEquals(ClientExceptionType.NotFound, e.getType());
         }
 
-        // now approve and verify that propagation has happened
+        // 2. now approve and verify that propagation has happened
         UserRequestForm form = userRequestService.getForms(
                 new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
@@ -141,6 +141,56 @@ public class UserSelfITCase extends AbstractITCase {
     }
 
     @Test
+    public void createAndUnclaim() {
+        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+
+        // 1. self-create user with membership: goes 'createApproval' with resources and membership but no propagation
+        UserTO userTO = UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org");
+        userTO.getMemberships().add(
+                new MembershipTO.Builder().group("29f96485-729e-4d31-88a1-6fc60e4677f3").build());
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+        SyncopeClient anonClient = clientFactory.create();
+        userTO = anonClient.getService(UserSelfService.class).
+                create(userTO, true).
+                readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                }).getEntity();
+        assertNotNull(userTO);
+        assertEquals("createApproval", userTO.getStatus());
+        assertFalse(userTO.getMemberships().isEmpty());
+        assertFalse(userTO.getResources().isEmpty());
+        try {
+            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        // 2. unclaim and verify that propagation has NOT happened
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
+        form = userRequestService.unclaimForm(form.getTaskId());
+        assertNull(form.getAssignee());
+        assertNotNull(userTO);
+        assertNotEquals("active", userTO.getStatus());
+        try {
+            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        // 3. approve and verify that propagation has happened
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
+        form = userRequestService.claimForm(form.getTaskId());
+        form.getProperty("approveCreate").get().setValue(Boolean.TRUE.toString());
+        userTO = userRequestService.submitForm(form);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey()));
+    }
+
+    @Test
     public void read() {
         UserTO user = createUser(UserITCase.getUniqueSampleTO("selfread@syncope.apache.org")).getEntity();
         UserService us2 = clientFactory.create(user.getUsername(), "password123").getService(UserService.class);
@@ -428,7 +478,7 @@ public class UserSelfITCase extends AbstractITCase {
         assertNotNull(form.getUsername());
         assertEquals(userTO.getUsername(), form.getUsername());
         assertNotNull(form.getTaskId());
-        assertNull(form.getOwner());
+        assertNull(form.getAssignee());
 
         // 3. claim task as rossini, with role "User manager" granting entitlement to claim forms but not in
         // groupForWorkflowApproval, designated for approval in workflow definition: fail
@@ -457,7 +507,7 @@ public class UserSelfITCase extends AbstractITCase {
         form = userService3.claimForm(form.getTaskId());
         assertNotNull(form);
         assertNotNull(form.getTaskId());
-        assertNotNull(form.getOwner());
+        assertNotNull(form.getAssignee());
 
         // 5. reject user
         form.getProperty("approveCreate").get().setValue(Boolean.FALSE.toString());
@@ -533,7 +583,7 @@ public class UserSelfITCase extends AbstractITCase {
         assertNotNull(form.getUserTO());
         assertEquals(updatedUsername, form.getUserTO().getUsername());
         assertNull(form.getUserPatch());
-        assertNull(form.getOwner());
+        assertNull(form.getAssignee());
 
         // 4. claim task (as admin)
         form = userRequestService.claimForm(form.getTaskId());
@@ -542,7 +592,7 @@ public class UserSelfITCase extends AbstractITCase {
         assertNotNull(form.getUserTO());
         assertEquals(updatedUsername, form.getUserTO().getUsername());
         assertNull(form.getUserPatch());
-        assertNotNull(form.getOwner());
+        assertNotNull(form.getAssignee());
 
         // 5. approve user (and verify that propagation occurred)
         form.getProperty("approveCreate").get().setValue(Boolean.TRUE.toString());
@@ -595,7 +645,7 @@ public class UserSelfITCase extends AbstractITCase {
                 new UserRequestFormQuery.Builder().user(created.getKey()).build()).getResult().get(0);
         assertNotNull(form);
         assertNotNull(form.getTaskId());
-        assertNull(form.getOwner());
+        assertNull(form.getAssignee());
         assertNotNull(form.getUserTO());
         assertNotNull(form.getUserPatch());
         assertEquals(patch, form.getUserPatch());
@@ -684,7 +734,7 @@ public class UserSelfITCase extends AbstractITCase {
         form = userService3.claimForm(form.getTaskId());
         assertNotNull(form);
         assertNotNull(form.getTaskId());
-        assertNotNull(form.getOwner());
+        assertNotNull(form.getAssignee());
 
         // 4. second claim task by admin
         form = userRequestService.claimForm(form.getTaskId());