You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/10/02 12:32:20 UTC

[4/6] syncope git commit: [SYNCOPE-1369] User requests forms now support dropdowns - via Flowable customization

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
----------------------------------------------------------------------
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 1acc1da..686677e 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
@@ -25,13 +25,12 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
 import org.apache.syncope.common.lib.types.BpmnProcessFormat;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.FlowableEntitlement;
-import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.flowable.api.BpmnProcessManager;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -41,6 +40,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.flowable.api.UserRequestHandler;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -52,24 +52,49 @@ import org.springframework.transaction.annotation.Transactional;
 public class UserRequestLogic extends AbstractTransactionalLogic<UserRequestForm> {
 
     @Autowired
-    private BpmnProcessManager bpmnProcessManager;
+    protected BpmnProcessManager bpmnProcessManager;
 
     @Autowired
-    private UserRequestHandler userRequestHandler;
+    protected UserRequestHandler userRequestHandler;
 
     @Autowired
-    private PropagationManager propagationManager;
+    protected PropagationManager propagationManager;
 
     @Autowired
-    private PropagationTaskExecutor taskExecutor;
+    protected PropagationTaskExecutor taskExecutor;
 
     @Autowired
-    private UserDataBinder binder;
+    protected UserDataBinder binder;
 
     @Autowired
-    private UserDAO userDAO;
+    protected UserDAO userDAO;
 
-    protected UserRequestTO doStart(final String bpmnProcess, final User user) {
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public Pair<Integer, List<UserRequest>> list(final String userKey,
+            final int page,
+            final int size,
+            final List<OrderByClause> orderByClauses) {
+
+        if (userKey == null) {
+            securityChecks(null,
+                    FlowableEntitlement.USER_REQUEST_LIST,
+                    "Listing user requests not allowed");
+        } else {
+            User user = userDAO.find(userKey);
+            if (user == null) {
+                throw new NotFoundException("User " + userKey);
+            }
+
+            securityChecks(user.getUsername(),
+                    FlowableEntitlement.USER_REQUEST_LIST,
+                    "Listing requests for user" + user.getUsername() + " not allowed");
+        }
+
+        return userRequestHandler.getUserRequests(userKey, page, size, orderByClauses);
+    }
+
+    protected UserRequest doStart(final String bpmnProcess, final User user) {
         // check if BPMN process exists
         bpmnProcessManager.exportProcess(bpmnProcess, BpmnProcessFormat.XML, new NullOutputStream());
 
@@ -77,56 +102,84 @@ public class UserRequestLogic extends AbstractTransactionalLogic<UserRequestForm
     }
 
     @PreAuthorize("isAuthenticated()")
-    public UserRequestTO start(final String bpmnProcess) {
+    public UserRequest start(final String bpmnProcess) {
         return doStart(bpmnProcess, userDAO.findByUsername(AuthContextUtils.getUsername()));
     }
 
     @PreAuthorize("hasRole('" + FlowableEntitlement.USER_REQUEST_START + "')")
-    public UserRequestTO start(final String bpmnProcess, final String userKey) {
+    public UserRequest start(final String bpmnProcess, final String userKey) {
         return doStart(bpmnProcess, userDAO.authFind(userKey));
     }
 
-    @PreAuthorize("isAuthenticated()")
-    public void cancel(final String executionId, final String reason) {
-        Pair<ProcessInstance, String> parsed = userRequestHandler.parse(executionId);
-
-        if (!AuthContextUtils.getUsername().equals(userDAO.find(parsed.getRight()).getUsername())
+    protected void securityChecks(final String username, final String entitlement, final String errorMessage) {
+        if (!AuthContextUtils.getUsername().equals(username)
                 && !AuthContextUtils.getAuthorities().stream().
-                        anyMatch(auth -> FlowableEntitlement.USER_REQUEST_CANCEL.equals(auth.getAuthority()))) {
+                        anyMatch(auth -> entitlement.equals(auth.getAuthority()))) {
 
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.DelegatedAdministration);
-            sce.getElements().add("Canceling " + executionId + " not allowed");
+            sce.getElements().add(errorMessage);
             throw sce;
         }
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    public void cancel(final String executionId, final String reason) {
+        Pair<ProcessInstance, String> parsed = userRequestHandler.parse(executionId);
+
+        securityChecks(userDAO.find(parsed.getRight()).getUsername(),
+                FlowableEntitlement.USER_REQUEST_CANCEL,
+                "Canceling " + executionId + " not allowed");
 
         userRequestHandler.cancel(parsed.getLeft(), reason);
     }
 
-    @PreAuthorize("hasRole('" + FlowableEntitlement.WORKFLOW_FORM_CLAIM + "')")
+    @PreAuthorize("isAuthenticated()")
     public UserRequestForm claimForm(final String taskId) {
-        return userRequestHandler.claimForm(taskId);
-    }
-
-    @PreAuthorize("hasRole('" + FlowableEntitlement.WORKFLOW_FORM_READ + "') "
-            + "and hasRole('" + StandardEntitlement.USER_READ + "')")
-    @Transactional(readOnly = true)
-    public List<UserRequestForm> getForms(final String key) {
-        User user = userDAO.authFind(key);
-        return userRequestHandler.getForms(user.getKey());
+        UserRequestForm form = userRequestHandler.claimForm(taskId);
+        securityChecks(form.getUsername(),
+                FlowableEntitlement.USER_REQUEST_FORM_CLAIM,
+                "Claiming form " + taskId + " not allowed");
+        return form;
     }
 
-    @PreAuthorize("hasRole('" + FlowableEntitlement.WORKFLOW_FORM_LIST + "')")
+    @PreAuthorize("isAuthenticated()")
     @Transactional(readOnly = true)
     public Pair<Integer, List<UserRequestForm>> getForms(
+            final String userKey,
             final int page,
             final int size,
             final List<OrderByClause> orderByClauses) {
 
-        return userRequestHandler.getForms(page, size, orderByClauses);
+        if (userKey == null) {
+            securityChecks(null,
+                    FlowableEntitlement.USER_REQUEST_FORM_LIST,
+                    "Listing forms not allowed");
+        } else {
+            User user = userDAO.find(userKey);
+            if (user == null) {
+                throw new NotFoundException("User " + userKey);
+            }
+
+            securityChecks(user.getUsername(),
+                    FlowableEntitlement.USER_REQUEST_FORM_LIST,
+                    "Listing forms for user" + user.getUsername() + " not allowed");
+        }
+
+        return userRequestHandler.getForms(userKey, page, size, orderByClauses);
     }
 
-    @PreAuthorize("hasRole('" + FlowableEntitlement.WORKFLOW_FORM_SUBMIT + "')")
+    @PreAuthorize("isAuthenticated()")
     public UserTO submitForm(final UserRequestForm form) {
+        if (form.getUsername() == null) {
+            securityChecks(null,
+                    FlowableEntitlement.USER_REQUEST_FORM_SUBMIT,
+                    "Submitting forms not allowed");
+        } else {
+            securityChecks(form.getUsername(),
+                    FlowableEntitlement.USER_REQUEST_FORM_SUBMIT,
+                    "Submitting forms for user" + form.getUsername() + " not allowed");
+        }
+
         WorkflowResult<UserPatch> wfResult = userRequestHandler.submitForm(form);
 
         // propByRes can be made empty by the workflow definition if no propagation should occur 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestFormQuery.java
----------------------------------------------------------------------
diff --git a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestFormQuery.java b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestFormQuery.java
index 4097afd..c4ac3a0 100644
--- a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestFormQuery.java
+++ b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestFormQuery.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.rest.api.beans;
 
+import javax.ws.rs.QueryParam;
+
 public class UserRequestFormQuery extends AbstractQuery {
 
     private static final long serialVersionUID = -4762457303770028554L;
@@ -28,5 +30,21 @@ public class UserRequestFormQuery extends AbstractQuery {
         protected UserRequestFormQuery newInstance() {
             return new UserRequestFormQuery();
         }
+
+        public Builder user(final String user) {
+            getInstance().setUser(user);
+            return this;
+        }
+    }
+
+    private String user;
+
+    public String getUser() {
+        return user;
+    }
+
+    @QueryParam("user")
+    public void setUser(final String user) {
+        this.user = user;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestQuery.java
----------------------------------------------------------------------
diff --git a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestQuery.java b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestQuery.java
new file mode 100644
index 0000000..3f53629
--- /dev/null
+++ b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/UserRequestQuery.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.rest.api.beans;
+
+import javax.ws.rs.QueryParam;
+
+public class UserRequestQuery extends AbstractQuery {
+
+    private static final long serialVersionUID = 427312034580076640L;
+
+    public static class Builder extends AbstractQuery.Builder<UserRequestQuery, Builder> {
+
+        @Override
+        protected UserRequestQuery newInstance() {
+            return new UserRequestQuery();
+        }
+
+        public Builder user(final String user) {
+            getInstance().setUser(user);
+            return this;
+        }
+    }
+
+    private String user;
+
+    public String getUser() {
+        return user;
+    }
+
+    @QueryParam("user")
+    public void setUser(final String user) {
+        this.user = user;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java
----------------------------------------------------------------------
diff --git a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java
index 06cb056..bb34fdd 100644
--- a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java
+++ b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java
@@ -19,6 +19,9 @@
 package org.apache.syncope.common.rest.api.service;
 
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@@ -35,7 +38,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
@@ -51,11 +54,16 @@ public interface BpmnProcessService extends JAXRSService {
     /**
      * Lists the available BPMN processes.
      *
-     * @return available BPMN processs, for the given any object type
+     * @return available BPMN processs
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "available BPMN processes", content =
+                    @Content(array =
+                            @ArraySchema(schema =
+                                    @Schema(implementation = BpmnProcess.class)))))
     @GET
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    List<BpmnProcessTO> list();
+    List<BpmnProcess> list();
 
     /**
      * Exports the BPMN process for matching key.
@@ -63,6 +71,8 @@ public interface BpmnProcessService extends JAXRSService {
      * @param key BPMN process key
      * @return BPMN process for matching key
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "BPMN process for matching key"))
     @GET
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -72,8 +82,10 @@ public interface BpmnProcessService extends JAXRSService {
      * Exports the BPMN diagram representation (if available), for matching key.
      *
      * @param key BPMN process key
-     * @return workflow diagram representation
+     * @return BPMN diagram representation
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "BPMN diagram representation"))
     @GET
     @Path("{key}/diagram.png")
     @Produces({ RESTHeaders.MEDIATYPE_IMAGE_PNG })

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
----------------------------------------------------------------------
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 701772d..5ad7b12 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
@@ -18,10 +18,13 @@
  */
 package org.apache.syncope.common.rest.api.service;
 
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.security.SecurityRequirements;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import java.util.List;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
@@ -34,11 +37,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import org.apache.syncope.common.lib.to.PagedResult;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
+import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
 
 /**
  * REST operations related to user workflow.
@@ -51,19 +55,37 @@ import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
 public interface UserRequestService extends JAXRSService {
 
     /**
-     * Starts a new user request, for the given BOMN Process and
-     * user (if provided) or requesting user (if not provided).
+     * Returns a list of running user requests matching the given query.
+     *
+     * @param query query conditions
+     * @return list of all running user requests
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "list of all running user requests", content =
+                    @Content(schema =
+                            @Schema(implementation = PagedResult.class))))
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    PagedResult<UserRequest> list(@BeanParam UserRequestQuery query);
+
+    /**
+     * Starts a new request for the given BPMN Process and user (if provided) or requesting user (if not provided).
      *
      * @param bpmnProcess BPMN process
-     * @param userKey user key
+     * @param user if value looks like a UUID then it is interpreted as key otherwise as a username
      * @return data about the started request service, including execution id
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200",
+                    description = "data about the started request service, including execution id", content =
+                    @Content(schema =
+                            @Schema(implementation = UserRequest.class))))
     @POST
     @Path("start/{bpmnProcess}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    UserRequestTO start(
+    UserRequest start(
             @NotNull @PathParam("bpmnProcess") String bpmnProcess,
-            @QueryParam("userKey") String userKey);
+            @QueryParam("user") String user);
 
     /**
      * Cancel a running user request.
@@ -71,6 +93,8 @@ public interface UserRequestService extends JAXRSService {
      * @param executionId execution id
      * @param reason reason to cancel the user request
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{executionId}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@@ -79,44 +103,45 @@ public interface UserRequestService extends JAXRSService {
             @QueryParam("reason") String reason);
 
     /**
-     * Returns a list of all available workflow forms.
+     * Returns a list of user request forms matching the given query.
      *
      * @param query query conditions
-     * @return list of all available workflow forms
+     * @return list of all available user request forms
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "list of all available user request forms", content =
+                    @Content(schema =
+                            @Schema(implementation = PagedResult.class))))
     @GET
     @Path("forms")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     PagedResult<UserRequestForm> getForms(@BeanParam UserRequestFormQuery query);
 
     /**
-     * Returns a list of available forms for the given user key.
-     *
-     * @param userKey user key
-     * @return list of available forms for the given user key
-     */
-    @GET
-    @Path("forms/{userKey}")
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    List<UserRequestForm> getForms(@NotNull @PathParam("userKey") String userKey);
-
-    /**
-     * Claims the form for the given task id.
+     * Requests to manage the form for the given task id.
      *
      * @param taskId workflow task id
-     * @return the workflow form for the given task id
+     * @return the form for the given task id
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "the form for the given task id", content =
+                    @Content(schema =
+                            @Schema(implementation = UserRequestForm.class))))
     @POST
     @Path("forms/{taskId}/claim")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     UserRequestForm claimForm(@NotNull @PathParam("taskId") String taskId);
 
     /**
-     * Submits a workflow form.
+     * Submits a user request form.
      *
-     * @param form workflow form.
+     * @param form user request form.
      * @return updated user
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "updated user", content =
+                    @Content(schema =
+                            @Schema(implementation = UserTO.class))))
     @POST
     @Path("forms")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowTaskService.java
----------------------------------------------------------------------
diff --git a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowTaskService.java b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowTaskService.java
index 54f38d0..b396d6e 100644
--- a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowTaskService.java
+++ b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowTaskService.java
@@ -18,6 +18,11 @@
  */
 package org.apache.syncope.common.rest.api.service;
 
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.security.SecurityRequirements;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -48,6 +53,11 @@ public interface UserWorkflowTaskService extends JAXRSService {
      * @param userKey user key
      * @return list of available tasks for the given user key
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "list of available tasks for the given user key", content =
+                    @Content(array =
+                            @ArraySchema(schema =
+                                    @Schema(implementation = WorkflowTask.class)))))
     @GET
     @Path("tasks/{userKey}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@@ -59,6 +69,10 @@ public interface UserWorkflowTaskService extends JAXRSService {
      * @param workflowTaskExecInput input for task execution
      * @return updated user
      */
+    @ApiResponses(
+            @ApiResponse(responseCode = "200", description = "updated user", content =
+                    @Content(schema =
+                            @Schema(implementation = UserTO.class))))
     @POST
     @Path("tasks/{userKey}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java
----------------------------------------------------------------------
diff --git a/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java b/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java
index dd1aea8..4f976b5 100644
--- a/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java
+++ b/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java
@@ -22,7 +22,7 @@ import java.util.List;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.common.lib.types.BpmnProcessFormat;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.core.logic.BpmnProcessLogic;
@@ -37,7 +37,7 @@ public class BpmnProcessServiceImpl extends AbstractServiceImpl implements BpmnP
     private BpmnProcessLogic logic;
 
     @Override
-    public List<BpmnProcessTO> list() {
+    public List<BpmnProcess> list() {
         return logic.list();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImpl.java
----------------------------------------------------------------------
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 2f174a0..188d22e 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
@@ -21,14 +21,16 @@ package org.apache.syncope.core.rest.cxf.service;
 import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.PagedResult;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
 import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
+import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
 import org.apache.syncope.core.logic.UserRequestLogic;
+import org.apache.syncope.common.rest.api.service.UserRequestService;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.apache.syncope.common.rest.api.service.UserRequestService;
 
 @Service
 public class UserRequestServiceImpl extends AbstractServiceImpl implements UserRequestService {
@@ -36,11 +38,25 @@ public class UserRequestServiceImpl extends AbstractServiceImpl implements UserR
     @Autowired
     private UserRequestLogic logic;
 
+    @Autowired
+    private UserDAO userDAO;
+
+    @Override
+    public PagedResult<UserRequest> list(final UserRequestQuery query) {
+        if (query.getUser() != null) {
+            query.setUser(getActualKey(userDAO, query.getUser()));
+        }
+
+        Pair<Integer, List<UserRequest>> result = logic.list(
+                query.getUser(), query.getPage(), query.getSize(), getOrderByClauses(query.getOrderBy()));
+        return buildPagedResult(result.getRight(), query.getPage(), query.getSize(), result.getLeft());
+    }
+
     @Override
-    public UserRequestTO start(final String bpmnProcess, final String userKey) {
-        return userKey == null
+    public UserRequest start(final String bpmnProcess, final String user) {
+        return user == null
                 ? logic.start(bpmnProcess)
-                : logic.start(bpmnProcess, userKey);
+                : logic.start(bpmnProcess, getActualKey(userDAO, user));
     }
 
     @Override
@@ -54,14 +70,13 @@ public class UserRequestServiceImpl extends AbstractServiceImpl implements UserR
     }
 
     @Override
-    public List<UserRequestForm> getForms(final String userKey) {
-        return logic.getForms(userKey);
-    }
-
-    @Override
     public PagedResult<UserRequestForm> getForms(final UserRequestFormQuery query) {
+        if (query.getUser() != null) {
+            query.setUser(getActualKey(userDAO, query.getUser()));
+        }
+
         Pair<Integer, List<UserRequestForm>> result = logic.getForms(
-                query.getPage(), query.getSize(), getOrderByClauses(query.getOrderBy()));
+                query.getUser(), query.getPage(), query.getSize(), getOrderByClauses(query.getOrderBy()));
         return buildPagedResult(result.getRight(), query.getPage(), query.getSize(), result.getLeft());
     }
 
@@ -69,5 +84,4 @@ public class UserRequestServiceImpl extends AbstractServiceImpl implements UserR
     public UserTO submitForm(final UserRequestForm form) {
         return logic.submitForm(form);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index c89241e..dc5e8d1 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -1541,6 +1541,26 @@ under the License.
       
       <build>
         <plugins>
+          <!-- Adds Flowable custom classes -->
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>build-helper-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>add-flowable-custom-classes</id>
+                <phase>initialize</phase>
+                <goals>
+                  <goal>add-source</goal>
+                </goals>
+                <configuration>
+                  <sources>
+                    <source>${basedir}/src/main/java-all</source>
+                  </sources>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+
           <!-- Adds Flowable test content -->
           <plugin>
             <groupId>org.codehaus.mojo</groupId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/AssignDirectorGroup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/AssignDirectorGroup.java b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/AssignDirectorGroup.java
new file mode 100644
index 0000000..4db7ac2
--- /dev/null
+++ b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/AssignDirectorGroup.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core.reference.flowable;
+
+import org.apache.syncope.common.lib.patch.MembershipPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.core.flowable.impl.FlowableRuntimeUtils;
+import org.apache.syncope.core.flowable.task.AbstractFlowableServiceTask;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AssignDirectorGroup extends AbstractFlowableServiceTask {
+
+    @Autowired
+    private UserDataBinder dataBinder;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Override
+    protected void doExecute(final String executionId) {
+        User user = engine.getRuntimeService().
+                getVariable(executionId, FlowableRuntimeUtils.USER, User.class);
+
+        Boolean secondLevelApprove = engine.getRuntimeService().
+                getVariable(executionId, "secondLevelApprove", Boolean.class);
+        if (Boolean.TRUE.equals(secondLevelApprove)) {
+            user = userDAO.save(user);
+
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(user.getKey());
+            userPatch.getMemberships().add(new MembershipPatch.Builder().
+                    group("ebf97068-aa4b-4a85-9f01-680e8c4cf227").build());
+
+            PropagationByResource propByRes = dataBinder.update(user, userPatch);
+
+            // report updated user and propagation by resource as result
+            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.USER, user);
+            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
+        } else {
+            LOG.info("Second level was not approved, not assigning the director group to " + user.getUsername());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/CreateARelationship.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/CreateARelationship.java b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/CreateARelationship.java
new file mode 100644
index 0000000..b0c5261
--- /dev/null
+++ b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/CreateARelationship.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core.reference.flowable;
+
+import org.apache.syncope.common.lib.patch.RelationshipPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.RelationshipTO;
+import org.apache.syncope.core.flowable.impl.FlowableRuntimeUtils;
+import org.apache.syncope.core.flowable.task.AbstractFlowableServiceTask;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CreateARelationship extends AbstractFlowableServiceTask {
+
+    @Autowired
+    private UserDataBinder dataBinder;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Override
+    protected void doExecute(final String executionId) {
+        User user = engine.getRuntimeService().
+                getVariable(executionId, FlowableRuntimeUtils.USER, User.class);
+
+        Boolean approve = engine.getRuntimeService().
+                getVariable(executionId, "approve", Boolean.class);
+        if (Boolean.TRUE.equals(approve)) {
+            user = userDAO.save(user);
+
+            String printer = engine.getRuntimeService().
+                    getVariable(executionId, "printer", String.class);
+
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(user.getKey());
+            userPatch.getRelationships().add(new RelationshipPatch.Builder().
+                    relationshipTO(new RelationshipTO.Builder().
+                            otherEnd("PRINTER", printer).type("neighborhood").build()).
+                    build());
+
+            PropagationByResource propByRes = dataBinder.update(user, userPatch);
+
+            // report updated user and propagation by resource as result
+            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.USER, user);
+            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
+        } else {
+            LOG.info("Printer assignment to " + user.getUsername() + " was not approved");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java
new file mode 100644
index 0000000..82e9693
--- /dev/null
+++ b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core.reference.flowable;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.flowable.api.DropdownValueProvider;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class PrintersValueProvider implements DropdownValueProvider {
+
+    private static final SearchCond PRINTER_COND;
+
+    private static final List<OrderByClause> ORDER_BY;
+
+    static {
+        AnyTypeCond anyTypeCond = new AnyTypeCond();
+        anyTypeCond.setAnyTypeKey("PRINTER");
+        PRINTER_COND = SearchCond.getLeafCond(anyTypeCond);
+
+        OrderByClause orderByNameAsc = new OrderByClause();
+        orderByNameAsc.setField("name");
+        orderByNameAsc.setDirection(OrderByClause.Direction.ASC);
+        ORDER_BY = Collections.singletonList(orderByNameAsc);
+    }
+
+    @Autowired
+    private AnySearchDAO anySearchDAO;
+
+    @Transactional(readOnly = true)
+    @Override
+    public Map<String, String> getValues() {
+        return anySearchDAO.<AnyObject>search(PRINTER_COND, ORDER_BY, AnyTypeKind.ANY_OBJECT).stream().
+                collect(Collectors.toMap(
+                        AnyObject::getKey,
+                        AnyObject::getName,
+                        (u, v) -> {
+                            throw new IllegalStateException(String.format("Duplicate key %s", u));
+                        },
+                        LinkedHashMap::new));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/AssignDirectorGroup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/AssignDirectorGroup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/AssignDirectorGroup.java
deleted file mode 100644
index abdfcb6..0000000
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/AssignDirectorGroup.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit.core.reference;
-
-import org.apache.syncope.common.lib.patch.MembershipPatch;
-import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.core.flowable.impl.FlowableRuntimeUtils;
-import org.apache.syncope.core.flowable.task.AbstractFlowableServiceTask;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.api.PropagationByResource;
-import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public class AssignDirectorGroup extends AbstractFlowableServiceTask {
-
-    @Autowired
-    private UserDataBinder dataBinder;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Override
-    protected void doExecute(final String executionId) {
-        User user = engine.getRuntimeService().
-                getVariable(executionId, FlowableRuntimeUtils.USER, User.class);
-
-        Boolean secondLevelApprove = engine.getRuntimeService().
-                getVariable(executionId, "secondLevelApprove", Boolean.class);
-        if (Boolean.TRUE.equals(secondLevelApprove)) {
-            user = userDAO.save(user);
-
-            UserPatch userPatch = new UserPatch();
-            userPatch.setKey(user.getKey());
-            userPatch.getMemberships().add(new MembershipPatch.Builder().
-                    group("ebf97068-aa4b-4a85-9f01-680e8c4cf227").build());
-
-            PropagationByResource propByRes = dataBinder.update(user, userPatch);
-
-            // report updated user and propagation by resource as result
-            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.USER, user);
-            engine.getRuntimeService().setVariable(executionId, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
-        } else {
-            LOG.info("Second level was not approved, not assigning the director group to " + user.getUsername());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/resources/all/workflowTestContext.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/all/workflowTestContext.xml b/fit/core-reference/src/main/resources/all/workflowTestContext.xml
index 2329c53..b14490a 100644
--- a/fit/core-reference/src/main/resources/all/workflowTestContext.xml
+++ b/fit/core-reference/src/main/resources/all/workflowTestContext.xml
@@ -19,9 +19,12 @@ under the License.
 -->
 <beans xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
-                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+                           http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context
+                           http://www.springframework.org/schema/context/spring-context.xsd">
 
-  <bean id="assignDirectorGroup" class="org.apache.syncope.fit.core.reference.AssignDirectorGroup"/>
+  <context:component-scan base-package="org.apache.syncope.fit.core.reference.flowable"/>
 
 </beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml b/fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml
new file mode 100644
index 0000000..c9ecb9c
--- /dev/null
+++ b/fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+             xmlns:flowable="http://flowable.org/bpmn"
+             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" 
+             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" 
+             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" 
+             typeLanguage="http://www.w3.org/2001/XMLSchema" 
+             expressionLanguage="http://www.w3.org/1999/XPath" 
+             targetNamespace="http://www.flowable.org/processdef">
+
+  <process id="assignPrinterRequest" name="Assign printer" isExecutable="true">
+    <startEvent id="startevent1" name="Start"/>
+    <endEvent id="endevent1" name="End"/>
+    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="selectPrinter"/>
+    <userTask id="selectPrinter" name="Select printer" flowable:formKey="selectPrinter" flowable:assignee="${wfExecutor}">
+      <extensionElements>
+        <flowable:formProperty id="printer" name="Printer" variable="printer" type="dropdown" required="true">
+          <flowable:value id="dropdownValueProvider" name="printersValueProvider"/>
+        </flowable:formProperty>
+        <flowable:formProperty id="printMode" name="Preferred print mode?" type="enum">
+          <flowable:value id="bw" name="Black / White"/>
+          <flowable:value id="color" name="Color"/>
+        </flowable:formProperty>
+      </extensionElements>
+    </userTask>
+    <userTask id="approvePrinter" name="Approve printer" flowable:formKey="approvePrinter">
+      <extensionElements>
+        <flowable:formProperty id="username" name="Username" type="string" expression="${userTO.username}" writable="false"/>
+        <flowable:formProperty id="printer" name="Selected printer" type="string" expression="${printer}" writable="false"/>
+        <flowable:formProperty id="approve" name="Approve?" type="boolean" variable="approve" required="true"/>
+      </extensionElements>
+    </userTask>
+    <sequenceFlow id="sid-D7047714-8E57-46B8-B6D4-4844DE330329" sourceRef="selectPrinter" targetRef="approvePrinter"/>
+    <serviceTask id="createARelationship" name="Create ARelationship" flowable:expression="#{createARelationship.execute(execution.processInstanceId)}"/>
+    <sequenceFlow id="sid-33880AE7-35C6-4A39-8E5B-12D8BA53F042" sourceRef="approvePrinter" targetRef="createARelationship"/>
+    <sequenceFlow id="sid-831E1896-EDF9-4F7D-AA42-E86CC1F8C5D3" sourceRef="createARelationship" targetRef="endevent1"/>
+  </process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_assignPrinterRequest">
+    <bpmndi:BPMNPlane bpmnElement="assignPrinterRequest" id="BPMNPlane_assignPrinterRequest">
+      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
+        <omgdc:Bounds height="30.0" width="30.0" x="180.0" y="115.0"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
+        <omgdc:Bounds height="28.0" width="28.0" x="885.0" y="116.0"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="selectPrinter" id="BPMNShape_selectPrinter">
+        <omgdc:Bounds height="80.0" width="100.0" x="330.0" y="90.0"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="approvePrinter" id="BPMNShape_approvePrinter">
+        <omgdc:Bounds height="80.0" width="100.0" x="495.5" y="90.0"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="createARelationship" id="BPMNShape_createARelationship">
+        <omgdc:Bounds height="80.0" width="100.0" x="675.0" y="90.0"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
+        <omgdi:waypoint x="209.94999901196195" y="130.0"/>
+        <omgdi:waypoint x="330.0" y="130.0"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="sid-D7047714-8E57-46B8-B6D4-4844DE330329" id="BPMNEdge_sid-D7047714-8E57-46B8-B6D4-4844DE330329">
+        <omgdi:waypoint x="429.95000000000005" y="130.0"/>
+        <omgdi:waypoint x="495.5" y="130.0"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="sid-33880AE7-35C6-4A39-8E5B-12D8BA53F042" id="BPMNEdge_sid-33880AE7-35C6-4A39-8E5B-12D8BA53F042">
+        <omgdi:waypoint x="595.4499999999068" y="130.0"/>
+        <omgdi:waypoint x="675.0" y="130.0"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="sid-831E1896-EDF9-4F7D-AA42-E86CC1F8C5D3" id="BPMNEdge_sid-831E1896-EDF9-4F7D-AA42-E86CC1F8C5D3">
+        <omgdi:waypoint x="774.9499999999266" y="130.0"/>
+        <omgdi:waypoint x="885.0" y="130.0"/>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</definitions>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
index e2f351b..cbdb19b 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
@@ -66,6 +66,7 @@ import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.common.lib.types.StatusPatchType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
 import org.apache.syncope.common.rest.api.service.AnyObjectService;
 import org.apache.syncope.common.rest.api.service.SchemaService;
 import org.apache.syncope.common.rest.api.service.UserService;
@@ -495,7 +496,8 @@ public class AuthenticationITCase extends AbstractITCase {
         }
 
         // 3. approve user
-        UserRequestForm form = userRequestService.getForms(userTO.getKey()).get(0);
+        UserRequestForm 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);

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
index 443cd83..21fb4fd 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
@@ -28,7 +28,7 @@ import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.FlowableDetector;
 import org.junit.jupiter.api.BeforeAll;
@@ -42,7 +42,7 @@ public class BpmnProcessITCase extends AbstractITCase {
     public static void findDefault() {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
         bpmnProcessService.list().stream().
-                filter(BpmnProcessTO::isUserWorkflow).findAny().
+                filter(BpmnProcess::isUserWorkflow).findAny().
                 ifPresent(process -> {
                     userWorkflowKey = process.getKey();
                 });

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
index 5446894..9281bce 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
@@ -25,14 +25,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.io.IOException;
+import java.util.List;
 import javax.ws.rs.core.MediaType;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.rest.api.beans.UserRequestFormQuery;
+import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
+import org.apache.syncope.common.rest.api.service.UserRequestService;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.FlowableDetector;
 import org.junit.jupiter.api.BeforeAll;
@@ -42,9 +47,13 @@ public class UserRequestITCase extends AbstractITCase {
 
     @BeforeAll
     public static void loadBpmnProcesses() throws IOException {
+        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+
         WebClient.client(bpmnProcessService).type(MediaType.APPLICATION_XML_TYPE);
         bpmnProcessService.set("directorGroupRequest",
                 IOUtils.toString(UserRequestITCase.class.getResourceAsStream("/directorGroupRequest.bpmn20.xml")));
+        bpmnProcessService.set("assignPrinterRequest",
+                IOUtils.toString(UserRequestITCase.class.getResourceAsStream("/assignPrinterRequest.bpmn20.xml")));
     }
 
     @Test
@@ -56,17 +65,29 @@ public class UserRequestITCase extends AbstractITCase {
         assertFalse(user.getMembership("ebf97068-aa4b-4a85-9f01-680e8c4cf227").isPresent());
 
         // start request
-        UserRequestTO req = userRequestService.start("directorGroupRequest", user.getKey());
+        UserRequest req = userRequestService.start("directorGroupRequest", user.getKey());
         assertNotNull(req);
+        assertEquals("directorGroupRequest", req.getBpmnProcess());
+        assertNotNull(req.getExecutionId());
+        assertEquals(req.getUser(), user.getKey());
+        
+        // check that user can see the ongoing request
+        SyncopeClient client = clientFactory.create(user.getUsername(), "password123");
+        PagedResult<UserRequest> requests = client.getService(UserRequestService.class).
+                list(new UserRequestQuery.Builder().user(user.getKey()).build());
+        assertEquals(1, requests.getTotalCount());
+        assertEquals("directorGroupRequest", requests.getResult().get(0).getBpmnProcess());
 
         // 1st approval -> reject
-        UserRequestForm form = userRequestService.getForms(user.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("firstLevelApprove").get().setValue(Boolean.FALSE.toString());
         userRequestService.submitForm(form);
 
         // no more forms, group not assigned
-        assertTrue(userRequestService.getForms(user.getKey()).isEmpty());
+        assertTrue(userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
         assertFalse(userService.read(user.getKey()).getMembership("ebf97068-aa4b-4a85-9f01-680e8c4cf227").isPresent());
 
         // start request again
@@ -74,19 +95,22 @@ public class UserRequestITCase extends AbstractITCase {
         assertNotNull(req);
 
         // 1st approval -> accept
-        form = userRequestService.getForms(user.getKey()).get(0);
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("firstLevelApprove").get().setValue(Boolean.TRUE.toString());
         userRequestService.submitForm(form);
 
         // 2nd approval -> reject
-        form = userRequestService.getForms(user.getKey()).get(0);
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("secondLevelApprove").get().setValue(Boolean.FALSE.toString());
         user = userRequestService.submitForm(form);
 
         // no more forms, group not assigned
-        assertTrue(userRequestService.getForms(user.getKey()).isEmpty());
+        assertTrue(userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
         assertFalse(userService.read(user.getKey()).getMembership("ebf97068-aa4b-4a85-9f01-680e8c4cf227").isPresent());
 
         // start request again
@@ -94,13 +118,15 @@ public class UserRequestITCase extends AbstractITCase {
         assertNotNull(req);
 
         // 1st approval -> accept
-        form = userRequestService.getForms(user.getKey()).get(0);
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("firstLevelApprove").get().setValue(Boolean.TRUE.toString());
         userRequestService.submitForm(form);
 
         // 2nd approval -> accept
-        form = userRequestService.getForms(user.getKey()).get(0);
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("secondLevelApprove").get().setValue(Boolean.TRUE.toString());
         user = userRequestService.submitForm(form);
@@ -115,7 +141,7 @@ public class UserRequestITCase extends AbstractITCase {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
         PagedResult<UserRequestForm> forms =
-                userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+                userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
 
         UserTO user = createUser(UserITCase.getUniqueSampleTO("twoLevelsApproval@tirasa.net")).getEntity();
@@ -123,22 +149,101 @@ public class UserRequestITCase extends AbstractITCase {
         assertFalse(user.getMembership("ebf97068-aa4b-4a85-9f01-680e8c4cf227").isPresent());
 
         // start request
-        UserRequestTO req = userRequestService.start("directorGroupRequest", user.getKey());
+        UserRequest req = userRequestService.start("directorGroupRequest", user.getKey());
         assertNotNull(req);
 
         // check that form was generated
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms + 1, forms.getTotalCount());
 
-        assertEquals(1, userRequestService.getForms(user.getKey()).size());
+        assertEquals(1, userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().size());
 
         // cancel request
         userRequestService.cancel(req.getExecutionId(), "nothing in particular");
 
         // check that form was removed
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
+        assertEquals(preForms, forms.getTotalCount());
+
+        assertTrue(userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
+    }
+
+    @Test
+    public void userSelection() {
+        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+
+        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+
+        PagedResult<UserRequestForm> forms =
+                userRequestService.getForms(new UserRequestFormQuery.Builder().build());
+        int preForms = forms.getTotalCount();
+
+        UserTO user = createUser(UserITCase.getUniqueSampleTO("userSelection@tirasa.net")).getEntity();
+        assertNotNull(user);
+        List<RelationshipTO> relationships = userService.read(user.getKey()).getRelationships();
+        assertTrue(relationships.isEmpty());
+
+        SyncopeClient client = clientFactory.create(user.getUsername(), "password123");
+
+        // start request as user
+        UserRequest req = client.getService(UserRequestService.class).start("assignPrinterRequest", null);
+        assertNotNull(req);
+
+        // check (as admin) that a new form is available
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
+        assertEquals(preForms + 1, forms.getTotalCount());
+
+        // get (as user) the form, claim and submit
+        PagedResult<UserRequestForm> userForms = client.getService(UserRequestService.class).
+                getForms(new UserRequestFormQuery.Builder().user(user.getKey()).build());
+        assertEquals(1, userForms.getTotalCount());
+
+        UserRequestForm form = userForms.getResult().get(0);
+        assertEquals("assignPrinterRequest", form.getBpmnProcess());
+        form = client.getService(UserRequestService.class).claimForm(form.getTaskId());
+
+        assertFalse(form.getProperty("printer").get().getDropdownValues().isEmpty());
+        form.getProperty("printer").ifPresent(printer -> printer.setValue("8559d14d-58c2-46eb-a2d4-a7d35161e8f8"));
+
+        assertFalse(form.getProperty("printMode").get().getEnumValues().isEmpty());
+        form.getProperty("printMode").ifPresent(printMode -> printMode.setValue("color"));
+
+        client.getService(UserRequestService.class).submitForm(form);
+
+        userForms = client.getService(UserRequestService.class).getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build());
+        assertEquals(0, userForms.getTotalCount());
+
+        // check that user can see the ongoing request
+        PagedResult<UserRequest> requests = client.getService(UserRequestService.class).
+                list(new UserRequestQuery.Builder().user(user.getKey()).build());
+        assertEquals(1, requests.getTotalCount());
+        assertEquals("assignPrinterRequest", requests.getResult().get(0).getBpmnProcess());
+
+        // get (as admin) the new form, claim and submit
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(user.getKey()).build()).getResult().get(0);
+        assertEquals("assignPrinterRequest", form.getBpmnProcess());
+        form = userRequestService.claimForm(form.getTaskId());
+
+        assertEquals("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", form.getProperty("printer").get().getValue());
+
+        form.getProperty("approve").get().setValue(Boolean.TRUE.toString());
+        userRequestService.submitForm(form);
+
+        // no more forms available
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms, forms.getTotalCount());
 
-        assertTrue(userRequestService.getForms(user.getKey()).isEmpty());
+        assertTrue(client.getService(UserRequestService.class).
+                list(new UserRequestQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
+
+        // check that relationship was made effective by approval
+        relationships = userService.read(user.getKey()).getRelationships();
+        assertFalse(relationships.isEmpty());
+        assertTrue(relationships.stream().
+                anyMatch(relationship -> "8559d14d-58c2-46eb-a2d4-a7d35161e8f8".equals(relationship.getOtherEndKey())));
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
----------------------------------------------------------------------
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 0e48cf2..63db20e 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
@@ -130,7 +130,8 @@ public class UserSelfITCase extends AbstractITCase {
         }
 
         // now approve and verify that propagation has happened
-        UserRequestForm form = userRequestService.getForms(userTO.getKey()).get(0);
+        UserRequestForm 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);
@@ -228,7 +229,8 @@ public class UserSelfITCase extends AbstractITCase {
         }
 
         // 3. approve self-update as admin
-        UserRequestForm form = userRequestService.getForms(updated.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(updated.getKey()).build()).getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         form.getProperty("approveUpdate").get().setValue(Boolean.TRUE.toString());
         updated = userRequestService.submitForm(form);
@@ -420,7 +422,8 @@ public class UserSelfITCase extends AbstractITCase {
         assertEquals("createApproval", userTO.getStatus());
 
         // 2. request if there is any pending task for user just created
-        UserRequestForm form = userRequestService.getForms(userTO.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
         assertNotNull(form);
         assertNotNull(form.getUsername());
         assertEquals(userTO.getUsername(), form.getUsername());
@@ -480,8 +483,7 @@ public class UserSelfITCase extends AbstractITCase {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
         // read forms *before* any operation
-        PagedResult<UserRequestForm> forms =
-                userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        PagedResult<UserRequestForm> forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
 
         UserTO userTO = UserITCase.getUniqueSampleTO("createWithApproval@syncope.apache.org");
@@ -514,7 +516,7 @@ public class UserSelfITCase extends AbstractITCase {
         assertNotNull(exception);
 
         // 2. request if there is any pending form for user just created
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms + 1, forms.getTotalCount());
 
         // 3. as admin, update user: still pending approval
@@ -524,7 +526,8 @@ public class UserSelfITCase extends AbstractITCase {
         userPatch.setUsername(new StringReplacePatchItem.Builder().value(updatedUsername).build());
         updateUser(userPatch);
 
-        UserRequestForm form = userRequestService.getForms(userTO.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
         assertNotNull(form);
         assertNotNull(form.getTaskId());
         assertNotNull(form.getUserTO());
@@ -567,8 +570,8 @@ public class UserSelfITCase extends AbstractITCase {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
         // read forms *before* any operation
-        PagedResult<UserRequestForm> forms = userRequestService.getForms(new UserRequestFormQuery.Builder().
-                page(1).size(1000).build());
+        PagedResult<UserRequestForm> forms = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
 
         UserTO created = createUser(UserITCase.getUniqueSampleTO("updateApproval@syncope.apache.org")).getEntity();
@@ -585,10 +588,11 @@ public class UserSelfITCase extends AbstractITCase {
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals("updateApproval", userService.read(created.getKey()).getStatus());
 
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms + 1, forms.getTotalCount());
 
-        UserRequestForm form = userRequestService.getForms(created.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(created.getKey()).build()).getResult().get(0);
         assertNotNull(form);
         assertNotNull(form.getTaskId());
         assertNull(form.getOwner());
@@ -607,7 +611,8 @@ public class UserSelfITCase extends AbstractITCase {
         assertEquals(0, updated.getMemberships().size());
 
         // the patch is not updated in the approval form
-        form = userRequestService.getForms(created.getKey()).get(0);
+        form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(created.getKey()).build()).getResult().get(0);
         assertEquals(patch, form.getUserPatch());
 
         // approve the user
@@ -642,8 +647,7 @@ public class UserSelfITCase extends AbstractITCase {
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
         // read forms *before* any operation
-        PagedResult<UserRequestForm> forms = userRequestService.getForms(new UserRequestFormQuery.Builder().
-                page(1).size(1000).build());
+        PagedResult<UserRequestForm> forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
 
         UserTO userTO = UserITCase.getUniqueSampleTO("issueSYNCOPE15@syncope.apache.org");
@@ -667,10 +671,11 @@ public class UserSelfITCase extends AbstractITCase {
         assertEquals(userTO.getCreationDate(), userTO.getLastChangeDate());
 
         // 2. request if there is any pending form for user just created
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms + 1, forms.getTotalCount());
 
-        UserRequestForm form = userRequestService.getForms(userTO.getKey()).get(0);
+        UserRequestForm form = userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
         assertNotNull(form);
 
         // 3. first claim by bellini ....
@@ -692,12 +697,12 @@ public class UserSelfITCase extends AbstractITCase {
         userTO = userRequestService.submitForm(form);
         assertNotNull(userTO);
         assertEquals(preForms,
-                userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build()).
-                        getTotalCount());
-        assertTrue(userRequestService.getForms(userTO.getKey()).isEmpty());
+                userRequestService.getForms(new UserRequestFormQuery.Builder().build()).getTotalCount());
+        assertTrue(userRequestService.getForms(
+                new UserRequestFormQuery.Builder().user(userTO.getKey()).build()).getResult().isEmpty());
 
         // 7.check that no more forms are still to be processed
-        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().page(1).size(1000).build());
+        forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms, forms.getTotalCount());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/fee1317d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index edc38b0..a04a9e5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1748,6 +1748,12 @@ under the License.
         </plugin>
         
         <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>build-helper-maven-plugin</artifactId>
+          <version>3.0.0</version>
+        </plugin>
+
+        <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-javadoc-plugin</artifactId>
           <version>2.10.4</version>          
@@ -2305,8 +2311,8 @@ under the License.
             <link>https://docs.spring.io/spring-security/site/docs/5.0.x/api/</link>
             <link>http://www.flowable.org/docs/javadocs/</link>
             <link>https://build.shibboleth.net/nexus/content/sites/site/java-opensaml/3.3.0/apidocs/</link>
-	    <link>https://artifacts.elastic.co/javadoc/org/elasticsearch/elasticsearch/6.4.1/index.html</link>
-	    <link>http://docs.swagger.io/swagger-core/v2.0.5/apidocs/</link>
+            <link>https://artifacts.elastic.co/javadoc/org/elasticsearch/elasticsearch/6.4.1/index.html</link>
+            <link>http://docs.swagger.io/swagger-core/v2.0.5/apidocs/</link>
           </links>
         </configuration>
         <reportSets>