From commits-return-12627-archive-asf-public=cust-asf.ponee.io@syncope.apache.org Thu Nov 29 16:15:44 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 264C218066C for ; Thu, 29 Nov 2018 16:15:42 +0100 (CET) Received: (qmail 53358 invoked by uid 500); 29 Nov 2018 15:15:42 -0000 Mailing-List: contact commits-help@syncope.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@syncope.apache.org Delivered-To: mailing list commits@syncope.apache.org Received: (qmail 53349 invoked by uid 99); 29 Nov 2018 15:15:42 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 29 Nov 2018 15:15:42 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 8CC9480968; Thu, 29 Nov 2018 15:15:41 +0000 (UTC) Date: Thu, 29 Nov 2018 15:15:41 +0000 To: "commits@syncope.apache.org" Subject: [syncope] branch 2_0_X updated: [SYNCOPE-1394] Added unclaim capability for user requests MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <154350454149.14468.1303894030090239427@gitbox.apache.org> From: skylark17@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: syncope X-Git-Refname: refs/heads/2_0_X X-Git-Reftype: branch X-Git-Oldrev: af393ffe82e57defce6d26f65941416ce2261906 X-Git-Newrev: 13fd70567ce4552b20e4d108bdd91bcb9adfaac6 X-Git-Rev: 13fd70567ce4552b20e4d108bdd91bcb9adfaac6 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. skylark17 pushed a commit to branch 2_0_X in repository https://gitbox.apache.org/repos/asf/syncope.git The following commit(s) were added to refs/heads/2_0_X by this push: new 13fd705 [SYNCOPE-1394] Added unclaim capability for user requests 13fd705 is described below commit 13fd70567ce4552b20e4d108bdd91bcb9adfaac6 Author: skylark17 AuthorDate: Thu Nov 29 16:15:22 2018 +0100 [SYNCOPE-1394] Added unclaim capability for user requests --- .../console/approvals/ApprovalDirectoryPanel.java | 36 +++++- .../console/rest/UserWorkflowRestClient.java | 4 + .../wicket/markup/html/form/ActionLink.java | 1 + .../client/console/pages/Approvals.properties | 5 +- .../client/console/pages/Approvals_it.properties | 3 +- .../client/console/pages/Approvals_ja.properties | 3 +- .../console/pages/Approvals_pt_BR.properties | 3 +- .../client/console/pages/Approvals_ru.properties | 5 +- .../wicket/markup/html/form/ActionPanel.properties | 4 + .../console/widgets/ApprovalsWidget.properties | 2 +- .../console/widgets/ApprovalsWidget_it.properties | 2 +- .../console/widgets/ApprovalsWidget_ja.properties | 2 +- .../widgets/ApprovalsWidget_pt_BR.properties | 2 +- .../console/widgets/ApprovalsWidget_ru.properties | 2 +- .../syncope/common/lib/to/WorkflowFormTO.java | 10 +- .../common/lib/types/StandardEntitlement.java | 2 + .../rest/api/service/UserWorkflowService.java | 14 ++- .../syncope/core/logic/UserWorkflowLogic.java | 6 +- .../rest/cxf/service/UserWorkflowServiceImpl.java | 5 + .../activiti/ActivitiUserWorkflowAdapter.java | 42 ++++++- .../syncope/core/workflow/api/WorkflowAdapter.java | 8 ++ .../flowable/FlowableUserWorkflowAdapter.java | 42 ++++++- .../java/DefaultAnyObjectWorkflowAdapter.java | 5 + .../workflow/java/DefaultGroupWorkflowAdapter.java | 5 + .../workflow/java/DefaultUserWorkflowAdapter.java | 5 + .../apache/syncope/fit/core/UserSelfITCase.java | 55 ++++++++- .../syncope/fit/core/UserWorkflowITCase.java | 123 ++++++++++++++++++++- 27 files changed, 360 insertions(+), 36 deletions(-) diff --git a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java index 7722202..d272ba8 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java @@ -126,7 +126,7 @@ public class ApprovalDirectoryPanel 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; } @@ -146,10 +146,32 @@ public class ApprovalDirectoryPanel ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); target.add(container); } + }, ActionLink.ActionType.CLAIM, StandardEntitlement.WORKFLOW_FORM_CLAIM); panel.add(new ActionLink() { + private static final long serialVersionUID = -8250444429732720947L; + + @Override + public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) { + unclaimForm(model.getObject().getTaskId()); + SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED)); + ApprovalDirectoryPanel.this.getTogglePanel().close(target); + target.add(container); + ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); + } + + @Override + protected boolean statusCondition(final WorkflowFormTO modelObject) { + return SyncopeConsoleSession.get().getSelfTO().getUsername(). + equals(model.getObject().getAssignee()); + } + + }, ActionLink.ActionType.UNCLAIM, StandardEntitlement.WORKFLOW_FORM_UNCLAIM); + + panel.add(new ActionLink() { + private static final long serialVersionUID = -3722207913631435501L; @Override @@ -182,7 +204,7 @@ public class ApprovalDirectoryPanel @Override protected boolean statusCondition(final WorkflowFormTO modelObject) { return SyncopeConsoleSession.get().getSelfTO().getUsername(). - equals(model.getObject().getOwner()); + equals(model.getObject().getAssignee()); } }, ActionLink.ActionType.MANAGE_APPROVAL, StandardEntitlement.WORKFLOW_FORM_READ); @@ -234,7 +256,7 @@ public class ApprovalDirectoryPanel @Override protected boolean statusCondition(final WorkflowFormTO modelObject) { return SyncopeConsoleSession.get().getSelfTO().getUsername(). - equals(model.getObject().getOwner()); + equals(model.getObject().getAssignee()); } }, ActionLink.ActionType.EDIT_APPROVAL, StandardEntitlement.WORKFLOW_FORM_SUBMIT); @@ -302,6 +324,14 @@ public class ApprovalDirectoryPanel } } + private void unclaimForm(final String taskId) { + try { + restClient.unclaimForm(taskId); + } catch (SyncopeClientException scee) { + SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + scee.getMessage()); + } + } + private class ApprovalUserWizardBuilder extends UserWizardBuilder { private static final long serialVersionUID = 1854981134836384069L; diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowRestClient.java index 8ccf057..6dda0bc 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowRestClient.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/UserWorkflowRestClient.java @@ -49,6 +49,10 @@ public class UserWorkflowRestClient extends BaseRestClient { return getService(UserWorkflowService.class).claimForm(taskKey); } + public WorkflowFormTO unclaimForm(final String taskKey) { + return getService(UserWorkflowService.class).unclaimForm(taskKey); + } + public void submitForm(final WorkflowFormTO form) { getService(UserWorkflowService.class).submitForm(form); } 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 d8e8f77..8d6cb60 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 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/pages/Approvals.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals.properties index 1ea8063..4ccf481 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals.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 approvals=Approvals delete=Delete @@ -28,7 +29,7 @@ type=Type username=Username new_user=New User creationDate = Creation Date -claimDate = Claim Dare +claimDate = Claim Date approval.edit=Approval Edit approval.manage=Approval Manage any.edit=Edit ${anyTO.type} ${anyTO.username} diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_it.properties index 7e12ce5..37e0406 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_it.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_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 approvals=Approvazioni delete=Rimuovi diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ja.properties index b2ff626..ae74807 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ja.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_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 approvals=\u627f\u8a8d delete=\u524a\u9664 diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_pt_BR.properties index e5ff74c..aa878f3 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_pt_BR.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_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 approvals=Aprova\u00e7\u00f5es delete=Excluir diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ru.properties index 6f4fb58..18d3476 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ru.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Approvals_ru.properties @@ -25,10 +25,11 @@ description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 createTime=\u0414\u0430\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f # 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=\u00d0\u0092\u00d0\u00bb\u00d0\u00b0\u00d0\u00b4\u00d0\u00b5\u00d0\u00bb\u00d0\u00b5\u00d1\u0086 +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/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 1086b88..819ed50 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/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget.properties index dd5b98f..47f6b8c 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget.properties @@ -16,6 +16,6 @@ # under the License. alerts.view.all=View all approvals duedate=Due date -owner=Owner +assignee=Assignee createApproval=Create Approval summary=${number} pending approval(s) diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_it.properties index 7b2287e..63bbe4b 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_it.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_it.properties @@ -16,6 +16,6 @@ # under the License. alerts.view.all=Tutte le approvazioni duedate=Scadenza -owner=Assegnato +assignee=Assegnato createApproval=Approvazione Creazione summary=${number} approvazioni pendenti diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ja.properties index a6089eb..385d98b 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ja.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ja.properties @@ -16,6 +16,6 @@ # under the License. alerts.view.all=\u3059\u3079\u3066\u306e\u627f\u8a8d\u3092\u8868\u793a duedate=\u671f\u9650 -owner=\u30aa\u30fc\u30ca\u30fc +assignee=\u30aa\u30fc\u30ca\u30fc createApproval=\u627f\u8a8d\u3092\u4f5c\u6210 summary=${number} \u4fdd\u7559\u306e\u627f\u8a8d diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_pt_BR.properties index c24f3d8..6b708e9 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_pt_BR.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_pt_BR.properties @@ -16,6 +16,6 @@ # under the License. alerts.view.all=View all Approvals duedate=Due date -owner=Owner +assignee=Assignee createApproval=Create Approval summary=${number} pending approval(s) diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ru.properties index 10281ff..ac3734f 100644 --- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ru.properties +++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ApprovalsWidget_ru.properties @@ -17,6 +17,6 @@ # alerts.view.all=\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u044f\u0432\u043a\u0438 duedate=\u0421\u0440\u043e\u043a -owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446 +assignee=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446 createApproval=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0437\u0430\u044f\u0432\u043a\u0443 summary=\u041e\u0436\u0438\u0434\u0430\u044e\u0442 \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043d\u0438\u044f: ${number} diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java index 0e80be6..f2bafb3 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java @@ -48,7 +48,7 @@ public class WorkflowFormTO extends AbstractBaseBean { private Date dueDate; - private String owner; + private String assignee; private UserTO userTO; @@ -110,12 +110,12 @@ public class WorkflowFormTO extends AbstractBaseBean { } } - 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/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java index 99c66d7..70c03fe 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java @@ -208,6 +208,8 @@ public final class StandardEntitlement { public static final String WORKFLOW_FORM_CLAIM = "WORKFLOW_FORM_CLAIM"; + public static final String WORKFLOW_FORM_UNCLAIM = "WORKFLOW_FORM_UNCLAIM"; + public static final String WORKFLOW_FORM_SUBMIT = "WORKFLOW_FORM_SUBMIT"; public static final String MAIL_TEMPLATE_LIST = "MAIL_TEMPLATE_LIST"; diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java index d89f250..47cd160 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java @@ -41,7 +41,8 @@ import org.apache.syncope.common.rest.api.beans.WorkflowFormQuery; * REST operations related to user workflow. */ @Api(tags = "UserWorkflow", authorizations = { - @Authorization(value = "BasicAuthentication"), + @Authorization(value = "BasicAuthentication") + , @Authorization(value = "Bearer") }) @Path("userworkflow") public interface UserWorkflowService extends JAXRSService { @@ -80,6 +81,17 @@ public interface UserWorkflowService extends JAXRSService { WorkflowFormTO claimForm(@NotNull @PathParam("taskId") String taskId); /** + * Unclaims 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, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + WorkflowFormTO unclaimForm(@NotNull @PathParam("taskId") String taskId); + + /** * Submits a workflow form. * * @param form workflow form. diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java index dabf93b..4fb0c64 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java @@ -60,11 +60,15 @@ public class UserWorkflowLogic extends AbstractTransactionalLogic updated = uwfAdapter.execute(userTO, taskId); diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserWorkflowServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserWorkflowServiceImpl.java index 7a25cb8..643baf3 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserWorkflowServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserWorkflowServiceImpl.java @@ -42,6 +42,11 @@ public class UserWorkflowServiceImpl extends AbstractServiceImpl implements User } @Override + public WorkflowFormTO unclaimForm(final String taskId) { + return logic.unclaimForm(taskId); + } + + @Override public UserTO executeTask(final String taskId, final UserTO userTO) { return logic.executeWorkflowTask(userTO, taskId); } diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java index a1c63a7..f85fdf6 100644 --- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java +++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java @@ -52,9 +52,13 @@ import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.Model; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.IdentityLink; +import org.activiti.engine.task.IdentityLinkType; import org.activiti.engine.task.Task; import org.activiti.engine.task.TaskQuery; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.collections4.Predicate; import org.apache.commons.collections4.Transformer; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -767,9 +771,25 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter { } } + boolean hasAssignees = IterableUtils.matchesAny(engine.getTaskService().getIdentityLinksForTask(taskId), + new Predicate() { + + @Override + public boolean evaluate(final IdentityLink identityLink) { + return IdentityLinkType.ASSIGNEE.equals(identityLink.getType()); + } + }); + if (hasAssignees) { + try { + engine.getTaskService().unclaim(taskId); + } catch (ActivitiException e) { + throw new WorkflowException("While unclaiming task " + taskId, e); + } + } + Task task; try { - engine.getTaskService().setOwner(taskId, authUser); + engine.getTaskService().claim(taskId, authUser); task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); } catch (ActivitiException e) { throw new WorkflowException("While reading task " + taskId, e); @@ -778,6 +798,22 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter { return getFormTO(task, checked.getValue()); } + @Override + public WorkflowFormTO unclaimForm(final String taskId) { + String authUser = AuthContextUtils.getUsername(); + Pair checked = checkTask(taskId, authUser); + + Task task; + try { + engine.getTaskService().unclaim(taskId); + task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); + } catch (ActivitiException e) { + throw new WorkflowException("While unclaiming task " + taskId, e); + } + + return getFormTO(task, checked.getValue()); + } + private Map getPropertiesForSubmit(final WorkflowFormTO form) { Map props = new HashMap<>(); for (WorkflowFormPropertyTO prop : form.getProperties()) { @@ -794,9 +830,9 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter { String authUser = AuthContextUtils.getUsername(); Pair checked = checkTask(form.getTaskId(), authUser); - if (!checked.getKey().getOwner().equals(authUser)) { + if (!checked.getKey().getAssignee().equals(authUser)) { throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to " - + checked.getKey().getOwner() + " but submitted by " + authUser)); + + checked.getKey().getAssignee() + " but submitted by " + authUser)); } User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId()); diff --git a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/WorkflowAdapter.java b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/WorkflowAdapter.java index 515d05f..f95ede7 100644 --- a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/WorkflowAdapter.java +++ b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/WorkflowAdapter.java @@ -65,6 +65,14 @@ public interface WorkflowAdapter { WorkflowFormTO claimForm(String taskId); /** + * Unclaim a form for a given object. + * + * @param taskId Workflow task to which the form is associated + * @return updated form + */ + WorkflowFormTO unclaimForm(String taskId); + + /** * Submit a form. * * @param form to be submitted diff --git a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java index 4e50994..d8d2221 100644 --- a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java +++ b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java @@ -51,9 +51,13 @@ import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.Model; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.IdentityLink; +import org.activiti.engine.task.IdentityLinkType; import org.activiti.engine.task.Task; import org.activiti.engine.task.TaskQuery; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.collections4.Predicate; import org.apache.commons.collections4.Transformer; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -766,9 +770,25 @@ public class FlowableUserWorkflowAdapter extends AbstractUserWorkflowAdapter { } } + boolean hasAssignees = IterableUtils.matchesAny(engine.getTaskService().getIdentityLinksForTask(taskId), + new Predicate() { + + @Override + public boolean evaluate(final IdentityLink identityLink) { + return IdentityLinkType.ASSIGNEE.equals(identityLink.getType()); + } + }); + if (hasAssignees) { + try { + engine.getTaskService().unclaim(taskId); + } catch (ActivitiException e) { + throw new WorkflowException("While unclaiming task " + taskId, e); + } + } + Task task; try { - engine.getTaskService().setOwner(taskId, authUser); + engine.getTaskService().claim(taskId, authUser); task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); } catch (ActivitiException e) { throw new WorkflowException("While reading task " + taskId, e); @@ -777,6 +797,22 @@ public class FlowableUserWorkflowAdapter extends AbstractUserWorkflowAdapter { return getFormTO(task, checked.getValue()); } + @Override + public WorkflowFormTO unclaimForm(final String taskId) { + String authUser = AuthContextUtils.getUsername(); + Pair checked = checkTask(taskId, authUser); + + Task task; + try { + engine.getTaskService().unclaim(taskId); + task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); + } catch (ActivitiException e) { + throw new WorkflowException("While unclaiming task " + taskId, e); + } + + return getFormTO(task, checked.getValue()); + } + private Map getPropertiesForSubmit(final WorkflowFormTO form) { Map props = new HashMap<>(); for (WorkflowFormPropertyTO prop : form.getProperties()) { @@ -793,9 +829,9 @@ public class FlowableUserWorkflowAdapter extends AbstractUserWorkflowAdapter { String authUser = AuthContextUtils.getUsername(); Pair checked = checkTask(form.getTaskId(), authUser); - if (!checked.getKey().getOwner().equals(authUser)) { + if (!checked.getKey().getAssignee().equals(authUser)) { throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to " - + checked.getKey().getOwner() + " but submitted by " + authUser)); + + checked.getKey().getAssignee() + " but submitted by " + authUser)); } User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId()); diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java index c51d3a4..0975928 100644 --- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java +++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java @@ -91,6 +91,11 @@ public class DefaultAnyObjectWorkflowAdapter extends AbstractAnyObjectWorkflowAd } @Override + public WorkflowFormTO unclaimForm(final String taskId) { + throw new WorkflowException(new UnsupportedOperationException("Not supported.")); + } + + @Override public WorkflowResult submitForm(final WorkflowFormTO form) { throw new WorkflowException(new UnsupportedOperationException("Not supported.")); } diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java index c50dcbd..0518e59 100644 --- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java +++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java @@ -89,6 +89,11 @@ public class DefaultGroupWorkflowAdapter extends AbstractGroupWorkflowAdapter { public WorkflowFormTO claimForm(final String taskId) { throw new WorkflowException(new UnsupportedOperationException("Not supported.")); } + + @Override + public WorkflowFormTO unclaimForm(final String taskId) { + throw new WorkflowException(new UnsupportedOperationException("Not supported.")); + } @Override public WorkflowResult submitForm(final WorkflowFormTO form) { diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java index 1847828..7d458cd 100644 --- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java +++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java @@ -179,6 +179,11 @@ public class DefaultUserWorkflowAdapter extends AbstractUserWorkflowAdapter { } @Override + public WorkflowFormTO unclaimForm(final String taskId) { + throw new WorkflowException(new UnsupportedOperationException("Not supported.")); + } + + @Override public WorkflowResult submitForm(final WorkflowFormTO form) { throw new WorkflowException(new UnsupportedOperationException("Not supported.")); } 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 25c8edc..c39a16c 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 @@ -103,7 +103,7 @@ public class UserSelfITCase extends AbstractITCase { public void createAndApprove() { Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(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()); @@ -126,7 +126,7 @@ public class UserSelfITCase extends AbstractITCase { assertEquals(ClientExceptionType.NotFound, e.getType()); } - // now approve and verify that propagation has happened + // 2. approve and verify that propagation has happened WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey()); form = userWorkflowService.claimForm(form.getTaskId()); form.getProperty("approveCreate").setValue(Boolean.TRUE.toString()); @@ -137,6 +137,57 @@ public class UserSelfITCase extends AbstractITCase { } @Test + public void createAndUnclaim() { + Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(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>() { + }).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 + WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey()); + form = userWorkflowService.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 = userWorkflowService.getFormForUser(userTO.getKey()); + form = userWorkflowService.claimForm(form.getTaskId()); + form.getProperty("approveCreate").setValue(Boolean.TRUE.toString()); + assertNotNull(form.getAssignee()); + userTO = userWorkflowService.submitForm(form); + assertNotNull(userTO); + assertEquals("active", userTO.getStatus()); + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey())); + } + + @Test public void read() { UserService userService2 = clientFactory.create("rossini", ADMIN_PWD).getService(UserService.class); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java index 0338105..f1f92a8 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java @@ -93,7 +93,7 @@ public class UserWorkflowITCase 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 group 7, // designated for approval in workflow definition: fail @@ -122,7 +122,7 @@ public class UserWorkflowITCase extends AbstractITCase { form = userService3.claimForm(form.getTaskId()); assertNotNull(form); assertNotNull(form.getTaskId()); - assertNotNull(form.getOwner()); + assertNotNull(form.getAssignee()); // 5. reject user form.getProperty("approveCreate").setValue(Boolean.FALSE.toString()); @@ -196,7 +196,7 @@ public class UserWorkflowITCase 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 = userWorkflowService.claimForm(form.getTaskId()); @@ -205,7 +205,7 @@ public class UserWorkflowITCase 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").setValue(Boolean.TRUE.toString()); @@ -229,6 +229,117 @@ public class UserWorkflowITCase extends AbstractITCase { } @Test + public void createAndUnclaim() { + Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)); + + // read forms *before* any operation + PagedResult forms = + userWorkflowService.getForms(new WorkflowFormQuery.Builder().page(1).size(1000).build()); + int preForms = forms.getTotalCount(); + + UserTO userTO = UserITCase.getUniqueSampleTO("createWithUnclaim@syncope.apache.org"); + userTO.getResources().add(RESOURCE_NAME_TESTDB); + + // User with group 0cbcabd2-4410-4b6b-8f05-a052b451d18f are defined in workflow as subject to approval + userTO.getMemberships().add( + new MembershipTO.Builder().group("0cbcabd2-4410-4b6b-8f05-a052b451d18f").build()); + + // 1. create user and verify that no propagation occurred) + ProvisioningResult result = createUser(userTO); + assertNotNull(result); + userTO = result.getEntity(); + assertEquals(1, userTO.getMemberships().size()); + assertEquals("0cbcabd2-4410-4b6b-8f05-a052b451d18f", userTO.getMemberships().get(0).getGroupKey()); + assertEquals("createApproval", userTO.getStatus()); + assertEquals(Collections.singleton(RESOURCE_NAME_TESTDB), userTO.getResources()); + + assertTrue(result.getPropagationStatuses().isEmpty()); + + JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); + + Exception exception = null; + try { + jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", + new String[] { userTO.getUsername() }, Integer.class); + } catch (EmptyResultDataAccessException e) { + exception = e; + } + assertNotNull(exception); + + // 2. request if there is any pending form for user just created + forms = userWorkflowService.getForms(new WorkflowFormQuery.Builder().page(1).size(1000).build()); + assertEquals(preForms + 1, forms.getTotalCount()); + + // 3. as admin, request for changes: still pending approval + String updatedUsername = "changed-" + UUID.randomUUID().toString(); + userTO.setUsername(updatedUsername); + userWorkflowService.executeTask("default", userTO); + + WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey()); + assertNotNull(form); + assertNotNull(form.getTaskId()); + assertNotNull(form.getUserTO()); + assertEquals(updatedUsername, form.getUserTO().getUsername()); + assertNull(form.getUserPatch()); + assertNull(form.getAssignee()); + + // 4. claim task (as admin) + form = userWorkflowService.claimForm(form.getTaskId()); + assertNotNull(form); + assertNotNull(form.getTaskId()); + assertNotNull(form.getUserTO()); + assertEquals(updatedUsername, form.getUserTO().getUsername()); + assertNull(form.getUserPatch()); + assertNotNull(form.getAssignee()); + + // 5. UNclaim task (as admin) and verify there is NO assignee now + form = userWorkflowService.unclaimForm(form.getTaskId()); + assertNotNull(form); + assertNotNull(form.getTaskId()); + assertNotNull(form.getUserTO()); + assertNull(form.getAssignee()); + + // 6. verify that propagation still did NOT occur + exception = null; + try { + jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", + new String[] { userTO.getUsername() }, Integer.class); + } catch (EmptyResultDataAccessException e) { + exception = e; + } + assertNotNull(exception); + + // 7. claim task again (as admin) + form = userWorkflowService.claimForm(form.getTaskId()); + assertNotNull(form); + assertNotNull(form.getTaskId()); + assertNotNull(form.getUserTO()); + assertEquals(updatedUsername, form.getUserTO().getUsername()); + assertNull(form.getUserPatch()); + assertNotNull(form.getAssignee()); + + // 8. approve user (and verify that propagation occurred) + form.getProperty("approveCreate").setValue(Boolean.TRUE.toString()); + userTO = userWorkflowService.submitForm(form); + assertNotNull(userTO); + assertEquals(updatedUsername, userTO.getUsername()); + assertEquals("active", userTO.getStatus()); + assertEquals(Collections.singleton(RESOURCE_NAME_TESTDB), userTO.getResources()); + + String username = queryForObject( + jdbcTemplate, 50, "SELECT id FROM test WHERE id=?", String.class, userTO.getUsername()); + assertEquals(userTO.getUsername(), username); + + // 9. update user + UserPatch userPatch = new UserPatch(); + userPatch.setKey(userTO.getKey()); + userPatch.setPassword(new PasswordPatch.Builder().value("anotherPassword123").build()); + + userTO = updateUser(userPatch).getEntity(); + assertNotNull(userTO); + } + + @Test public void updateApproval() { Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)); @@ -257,7 +368,7 @@ public class UserWorkflowITCase extends AbstractITCase { WorkflowFormTO form = userWorkflowService.getFormForUser(created.getKey()); assertNotNull(form); assertNotNull(form.getTaskId()); - assertNull(form.getOwner()); + assertNull(form.getAssignee()); assertNotNull(form.getUserTO()); assertNotNull(form.getUserPatch()); assertEquals(patch, form.getUserPatch()); @@ -359,7 +470,7 @@ public class UserWorkflowITCase 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 = userWorkflowService.claimForm(form.getTaskId());