title: Spring Activiti verify assignee or candidateGroups before completing task
body: <p>I'm working with <code>Spring Acitviti</code> to manage workflows for a project. I'm new to Activiti and I'm trying get it to work with custom user base. Following is the sample project that I'm working with </p>
  
  <p><a href="https://github.com/ConsciousObserver/AcitvitySpringTest.git" rel="nofollow noreferrer">https://github.com/ConsciousObserver/AcitvitySpringTest.git</a></p>
  
  <p>Currently I'm trying to restrict users who can execute a particular task <code>taskA</code> in the process <code>simple-test-process</code>.</p>
  
  <p>It seems like when calling <code>taskService.complete(task.getId())</code> Activiti is completely ignoring who the task is assigned to. Since there's no security configured in the project yet, and <code>taskA</code> is assigned to <code>Hello World</code> it should fail as anonymous user is trying to execute it.</p>
  
  <p><strong>Does activity require us to manually check the <code>assignee</code>, <code>candidateUsers</code>, <code>candidateGroups</code> before executing tasks?</strong></p>
  
  <p>If not then how do we use indicate to Activiti to let only a particular user complete the task.</p>
  
  <p>Including the relevant code below
  <strong>simple-test-process.bpmn diagram</strong></p>
  
  <p><a href="https://i.stack.imgur.com/BuoC9.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BuoC9.jpg" alt="simple-test-process.bpmn diagram"></a></p>
  
  <p><strong>simple-test-process.bpmn</strong></p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:activiti="http://activiti.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.example.org/test"&gt;
      &lt;process id="simple-test-process" name="simple-test-process"&gt;
          &lt;startEvent id="startEvent" name="startEvent"&gt;&lt;/startEvent&gt;
          &lt;sequenceFlow id="sequenceFlow1" targetRef="initTask" sourceRef="startEvent" /&gt;
          &lt;serviceTask id="initTask" name="initTask" activiti:expression="${myService.init(photos)}"&gt;&lt;/serviceTask&gt;
          &lt;sequenceFlow targetRef="taskA" sourceRef="initTask"&gt;&lt;/sequenceFlow&gt;
          &lt;userTask id="taskA" name="taskA" activiti:assignee="Hello World" &gt;&lt;/userTask&gt;
          &lt;sequenceFlow id="sequenceFlow2" targetRef="taskB" sourceRef="taskA"&gt;&lt;/sequenceFlow&gt;
          &lt;userTask id="taskB" name="taskB" activiti:assignee="$INITIATOR"&gt;&lt;/userTask&gt;
          &lt;sequenceFlow id="sequenceFlow3" targetRef="endEvent" sourceRef="taskB" /&gt;
          &lt;endEvent id="endEvent" name="endEvent"&gt;&lt;/endEvent&gt;
      &lt;/process&gt;
  &lt;/definitions&gt;
  </code></pre>
  
  <p><strong>ActivitiTestApplication.java</strong></p>
  
  <pre class="lang-java prettyprint-override"><code>package com.example;
  
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  
  import org.activiti.engine.IdentityService;
  import org.activiti.engine.RuntimeService;
  import org.activiti.engine.TaskService;
  import org.activiti.engine.identity.Group;
  import org.activiti.engine.identity.User;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.task.Task;
  import org.springframework.beans.factory.InitializingBean;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.context.annotation.Bean;
  import org.springframework.stereotype.Component;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.PathVariable;
  import org.springframework.web.bind.annotation.RestController;
  
  @SpringBootApplication
  public class ActivitiTestApplication {
  
      public static void main(String[] args) {
          SpringApplication.run(ActivitiTestApplication.class, args);
      }
  
      @Bean
      InitializingBean usersAndGroupsInitializer(final IdentityService identityService) {
  
          return () -&gt; {
                  Group group = identityService.newGroup("user");
                  group.setName("users");
                  group.setType("security-role");
                  identityService.saveGroup(group);
  
                  User admin = identityService.newUser("admin");
                  admin.setPassword("admin");
                  identityService.saveUser(admin);
              };
      }
  }
  
  @RestController
  class ProcessRest {
      @Autowired
      private RuntimeService runtimeService;
  
      @Autowired
      private TaskService taskService;
  
      @GetMapping("/start")
      public List&lt;Map&lt;String, String&gt;&gt; start() {
          List&lt;String&gt; photos = Arrays.asList("photo1", "photo2");
          runtimeService.startProcessInstanceByKey("simple-test-process", Collections.singletonMap("photos", photos));
  
          long runningProcessCount = runtimeService.createProcessInstanceQuery().count();
          List&lt;ProcessInstance&gt; processes = runtimeService.createProcessInstanceQuery().list();
  
          return processes
                  .stream()
                  .map(p -&gt; getMap(p.getId(), p.getProcessInstanceId(), p.getProcessDefinitionName()))
                  .collect(Collectors.toList());
      }
  
      @GetMapping("/getTasks/{processInstanceId}")
      public List&lt;Map&lt;String, String&gt;&gt; getTasks(@PathVariable String processInstanceId) {
          List&lt;Task&gt; tasks = taskService
              .createTaskQuery()
              .processInstanceId(processInstanceId)
              .list();
  
          return tasks
                  .stream()
                  .map(t -&gt; getMap(t.getId(), t.getProcessInstanceId(), t.getName()))
                  .collect(Collectors.toList());
      }
  
      @GetMapping("/completeTask/{processInstanceId}")
      public Map&lt;String, String&gt; completeNextTask(@PathVariable String processInstanceId) {
          Task task = taskService.createTaskQuery()
                          .processInstanceId(processInstanceId)
                          .singleResult();
  
          if(task == null) {
              throw new RuntimeException("No pending task for this processInstance");
          }
  
          System.out.println("variables: " + task.getProcessVariables());
  
          taskService.complete(task.getId());
  
          return getMap(task.getId(), task.getProcessInstanceId(), task.getName());
      }
  
      public Map&lt;String, String&gt; getMap(String id, String instanceId, String name) {
          Map&lt;String, String&gt; map = new HashMap&lt;&gt;();
          map.put("id", id);
          map.put("processInstanceId", instanceId);
          map.put("name", name);
  
          return map;
      }
  }
  @Component
  class MyService {
      public void init(List&lt;String&gt; photos) {
          System.out.println("################################### Running Init Task ########################");
          for(String photo: photos) {
              System.out.println("Photo name: " + photo);
          }
      }
  
      public String processPhoto(List&lt;String&gt; photos) {
          for(String photo: photos) {
              System.out.println("Photo name: " + photo);
          }
          return "processed";
      }
  }
  </code></pre>