View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.logic;
20  
21  import java.lang.reflect.Method;
22  import java.util.List;
23  import org.apache.commons.io.output.NullOutputStream;
24  import org.apache.commons.lang3.tuple.Pair;
25  import org.apache.syncope.common.lib.SyncopeClientException;
26  import org.apache.syncope.common.lib.request.UserUR;
27  import org.apache.syncope.common.lib.to.EntityTO;
28  import org.apache.syncope.common.lib.to.ProvisioningResult;
29  import org.apache.syncope.common.lib.to.UserRequest;
30  import org.apache.syncope.common.lib.to.UserRequestForm;
31  import org.apache.syncope.common.lib.to.UserTO;
32  import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
33  import org.apache.syncope.common.lib.types.BpmnProcessFormat;
34  import org.apache.syncope.common.lib.types.ClientExceptionType;
35  import org.apache.syncope.common.lib.types.FlowableEntitlement;
36  import org.apache.syncope.core.flowable.api.BpmnProcessManager;
37  import org.apache.syncope.core.flowable.api.UserRequestHandler;
38  import org.apache.syncope.core.persistence.api.dao.NotFoundException;
39  import org.apache.syncope.core.persistence.api.dao.UserDAO;
40  import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
41  import org.apache.syncope.core.persistence.api.entity.user.User;
42  import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
43  import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
44  import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
45  import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
46  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
47  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
48  import org.apache.syncope.core.spring.security.AuthContextUtils;
49  import org.flowable.engine.runtime.ProcessInstance;
50  import org.springframework.security.access.prepost.PreAuthorize;
51  import org.springframework.transaction.annotation.Transactional;
52  
53  public class UserRequestLogic extends AbstractTransactionalLogic<EntityTO> {
54  
55      protected final BpmnProcessManager bpmnProcessManager;
56  
57      protected final UserRequestHandler userRequestHandler;
58  
59      protected final PropagationManager propagationManager;
60  
61      protected final PropagationTaskExecutor taskExecutor;
62  
63      protected final UserDataBinder binder;
64  
65      protected final UserDAO userDAO;
66  
67      public UserRequestLogic(
68              final BpmnProcessManager bpmnProcessManager,
69              final UserRequestHandler userRequestHandler,
70              final PropagationManager propagationManager,
71              final PropagationTaskExecutor taskExecutor,
72              final UserDataBinder binder,
73              final UserDAO userDAO) {
74  
75          this.bpmnProcessManager = bpmnProcessManager;
76          this.userRequestHandler = userRequestHandler;
77          this.propagationManager = propagationManager;
78          this.taskExecutor = taskExecutor;
79          this.binder = binder;
80          this.userDAO = userDAO;
81      }
82  
83      @PreAuthorize("isAuthenticated()")
84      @Transactional(readOnly = true)
85      public Pair<Integer, List<UserRequest>> listRequests(
86              final String userKey,
87              final int page,
88              final int size,
89              final List<OrderByClause> orderByClauses) {
90  
91          if (userKey == null) {
92              securityChecks(null,
93                      FlowableEntitlement.USER_REQUEST_LIST,
94                      "Listing user requests not allowed");
95          } else {
96              User user = userDAO.find(userKey);
97              if (user == null) {
98                  throw new NotFoundException("User " + userKey);
99              }
100 
101             securityChecks(user.getUsername(),
102                     FlowableEntitlement.USER_REQUEST_LIST,
103                     "Listing requests for user" + user.getUsername() + " not allowed");
104         }
105 
106         return userRequestHandler.getUserRequests(userKey, page, size, orderByClauses);
107     }
108 
109     protected UserRequest doStart(
110             final String bpmnProcess,
111             final User user,
112             final WorkflowTaskExecInput inputVariables) {
113 
114         // check if BPMN process exists
115         bpmnProcessManager.exportProcess(bpmnProcess, BpmnProcessFormat.XML, NullOutputStream.INSTANCE);
116 
117         return userRequestHandler.start(bpmnProcess, user, inputVariables);
118     }
119 
120     @PreAuthorize("isAuthenticated()")
121     public UserRequest startRequest(final String bpmnProcess, final WorkflowTaskExecInput inputVariables) {
122         return doStart(bpmnProcess, userDAO.findByUsername(AuthContextUtils.getUsername()), inputVariables);
123     }
124 
125     @PreAuthorize("hasRole('" + FlowableEntitlement.USER_REQUEST_START + "')")
126     public UserRequest startRequest(
127             final String bpmnProcess,
128             final String userKey,
129             final WorkflowTaskExecInput inputVariables) {
130         return doStart(bpmnProcess, userDAO.authFind(userKey), inputVariables);
131     }
132 
133     protected static void securityChecks(final String username, final String entitlement, final String errorMessage) {
134         if (!AuthContextUtils.getUsername().equals(username)
135                 && !AuthContextUtils.getAuthorities().stream().
136                         anyMatch(auth -> entitlement.equals(auth.getAuthority()))) {
137 
138             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.DelegatedAdministration);
139             sce.getElements().add(errorMessage);
140             throw sce;
141         }
142     }
143 
144     @PreAuthorize("isAuthenticated()")
145     public void cancelRequest(final String executionId, final String reason) {
146         Pair<ProcessInstance, String> parsed = userRequestHandler.parse(executionId);
147 
148         securityChecks(userDAO.find(parsed.getRight()).getUsername(),
149                 FlowableEntitlement.USER_REQUEST_CANCEL,
150                 "Canceling " + executionId + " not allowed");
151 
152         userRequestHandler.cancel(parsed.getLeft(), reason);
153     }
154 
155     @PreAuthorize("isAuthenticated()")
156     public UserRequestForm claimForm(final String taskId) {
157         UserRequestForm form = userRequestHandler.claimForm(taskId);
158         securityChecks(form.getUsername(),
159                 FlowableEntitlement.USER_REQUEST_FORM_CLAIM,
160                 "Claiming form " + taskId + " not allowed");
161         return form;
162     }
163 
164     @PreAuthorize("isAuthenticated()")
165     public UserRequestForm unclaimForm(final String taskId) {
166         UserRequestForm form = userRequestHandler.unclaimForm(taskId);
167         securityChecks(form.getUsername(),
168                 FlowableEntitlement.USER_REQUEST_FORM_UNCLAIM,
169                 "Unclaiming form " + taskId + " not allowed");
170         return form;
171     }
172 
173     protected void evaluateKey(final String userKey) {
174         if (userKey == null) {
175             securityChecks(null,
176                     FlowableEntitlement.USER_REQUEST_FORM_LIST,
177                     "Listing forms not allowed");
178         } else {
179             User user = userDAO.find(userKey);
180             if (user == null) {
181                 throw new NotFoundException("User " + userKey);
182             }
183 
184             securityChecks(user.getUsername(),
185                     FlowableEntitlement.USER_REQUEST_FORM_LIST,
186                     "Listing forms for user" + user.getUsername() + " not allowed");
187         }
188     }
189 
190     @PreAuthorize("isAuthenticated()")
191     public UserRequestForm getForm(final String userKey, final String taskId) {
192         evaluateKey(userKey);
193 
194         return userRequestHandler.getForm(userKey, taskId);
195     }
196 
197     @PreAuthorize("isAuthenticated()")
198     @Transactional(readOnly = true)
199     public Pair<Integer, List<UserRequestForm>> listForms(
200             final String userKey,
201             final int page,
202             final int size,
203             final List<OrderByClause> orderByClauses) {
204 
205         evaluateKey(userKey);
206 
207         return userRequestHandler.getForms(userKey, page, size, orderByClauses);
208     }
209 
210     @PreAuthorize("isAuthenticated()")
211     public ProvisioningResult<UserTO> submitForm(final UserRequestForm form, final boolean nullPriorityAsync) {
212         if (form.getUsername() == null) {
213             securityChecks(null,
214                     FlowableEntitlement.USER_REQUEST_FORM_SUBMIT,
215                     "Submitting forms not allowed");
216         } else {
217             securityChecks(form.getUsername(),
218                     FlowableEntitlement.USER_REQUEST_FORM_SUBMIT,
219                     "Submitting forms for user" + form.getUsername() + " not allowed");
220         }
221 
222         ProvisioningResult<UserTO> result = new ProvisioningResult<>();
223 
224         UserWorkflowResult<UserUR> wfResult = userRequestHandler.submitForm(form);
225 
226         // propByRes can be made empty by the workflow definition if no propagation should occur 
227         // (for example, with rejected users)
228         if (wfResult.getPropByRes() != null && !wfResult.getPropByRes().isEmpty()) {
229             List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
230                     new UserWorkflowResult<>(
231                             Pair.of(wfResult.getResult(), Boolean.TRUE),
232                             wfResult.getPropByRes(),
233                             wfResult.getPropByLinkedAccount(),
234                             wfResult.getPerformedTasks()));
235 
236             PropagationReporter propagationReporter = taskExecutor.execute(
237                     taskInfos, nullPriorityAsync, AuthContextUtils.getUsername());
238             result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
239         }
240 
241         UserTO userTO;
242         if (userDAO.find(wfResult.getResult().getKey()) == null) {
243             userTO = new UserTO();
244             userTO.setKey(wfResult.getResult().getKey());
245         } else {
246             userTO = binder.getUserTO(wfResult.getResult().getKey());
247         }
248         result.setEntity(userTO);
249 
250         return result;
251     }
252 
253     @Override
254     protected EntityTO resolveReference(final Method method, final Object... args)
255             throws UnresolvedReferenceException {
256 
257         throw new UnresolvedReferenceException();
258     }
259 }