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.provisioning.java;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.stream.Collectors;
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.commons.lang3.tuple.Pair;
29  import org.apache.syncope.common.lib.request.GroupCR;
30  import org.apache.syncope.common.lib.request.GroupUR;
31  import org.apache.syncope.common.lib.to.PropagationStatus;
32  import org.apache.syncope.common.lib.types.AnyTypeKind;
33  import org.apache.syncope.common.lib.types.ResourceOperation;
34  import org.apache.syncope.core.persistence.api.dao.GroupDAO;
35  import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
36  import org.apache.syncope.core.provisioning.api.PropagationByResource;
37  import org.apache.syncope.core.provisioning.api.VirAttrHandler;
38  import org.apache.syncope.core.provisioning.api.WorkflowResult;
39  import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
40  import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
41  import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
42  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
43  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
44  import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
45  import org.identityconnectors.framework.common.objects.Attribute;
46  import org.springframework.transaction.annotation.Propagation;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  public class DefaultGroupProvisioningManager implements GroupProvisioningManager {
50  
51      protected final GroupWorkflowAdapter gwfAdapter;
52  
53      protected final PropagationManager propagationManager;
54  
55      protected final PropagationTaskExecutor taskExecutor;
56  
57      protected final GroupDataBinder groupDataBinder;
58  
59      protected final GroupDAO groupDAO;
60  
61      protected final VirAttrHandler virtAttrHandler;
62  
63      public DefaultGroupProvisioningManager(
64              final GroupWorkflowAdapter gwfAdapter,
65              final PropagationManager propagationManager,
66              final PropagationTaskExecutor taskExecutor,
67              final GroupDataBinder groupDataBinder,
68              final GroupDAO groupDAO,
69              final VirAttrHandler virtAttrHandler) {
70  
71          this.gwfAdapter = gwfAdapter;
72          this.propagationManager = propagationManager;
73          this.taskExecutor = taskExecutor;
74          this.groupDataBinder = groupDataBinder;
75          this.groupDAO = groupDAO;
76          this.virtAttrHandler = virtAttrHandler;
77      }
78  
79      @Override
80      public Pair<String, List<PropagationStatus>> create(
81              final GroupCR groupCR, final boolean nullPriorityAsync, final String creator, final String context) {
82  
83          WorkflowResult<String> created = gwfAdapter.create(groupCR, creator, context);
84  
85          List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
86                  AnyTypeKind.GROUP,
87                  created.getResult(),
88                  null,
89                  created.getPropByRes(),
90                  groupCR.getVirAttrs(),
91                  Set.of());
92          PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync, creator);
93  
94          return Pair.of(created.getResult(), propagationReporter.getStatuses());
95      }
96  
97      @Transactional(propagation = Propagation.REQUIRES_NEW)
98      @Override
99      public Pair<String, List<PropagationStatus>> create(
100             final GroupCR groupCR,
101             final Map<String, String> groupOwnerMap,
102             final Set<String> excludedResources,
103             final boolean nullPriorityAsync,
104             final String creator,
105             final String context) {
106 
107         WorkflowResult<String> created = gwfAdapter.create(groupCR, creator, context);
108 
109         // see ConnObjectUtils#getAnyTOFromConnObject for GroupOwnerSchema
110         groupCR.getPlainAttr(StringUtils.EMPTY).
111                 ifPresent(groupOwner -> groupOwnerMap.put(created.getResult(), groupOwner.getValues().get(0)));
112 
113         List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
114                 AnyTypeKind.GROUP,
115                 created.getResult(),
116                 null,
117                 created.getPropByRes(),
118                 groupCR.getVirAttrs(),
119                 excludedResources);
120         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync, creator);
121 
122         return Pair.of(created.getResult(), propagationReporter.getStatuses());
123     }
124 
125     @Transactional(propagation = Propagation.REQUIRES_NEW)
126     @Override
127     public Pair<GroupUR, List<PropagationStatus>> update(
128             final GroupUR groupUR,
129             final Set<String> excludedResources,
130             final boolean nullPriorityAsync,
131             final String updater,
132             final String context) {
133 
134         Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(
135                 AnyTypeKind.GROUP,
136                 groupUR.getKey(),
137                 null,
138                 false,
139                 null,
140                 excludedResources);
141 
142         WorkflowResult<GroupUR> updated = gwfAdapter.update(groupUR, updater, context);
143 
144         List<PropagationTaskInfo> tasks = propagationManager.setAttributeDeltas(
145                 propagationManager.getUpdateTasks(
146                         AnyTypeKind.GROUP,
147                         updated.getResult().getKey(),
148                         false,
149                         null,
150                         updated.getPropByRes(),
151                         null,
152                         groupUR.getVirAttrs(),
153                         excludedResources),
154                 beforeAttrs,
155                 updated.getResult());
156         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync, updater);
157 
158         return Pair.of(updated.getResult(), propagationReporter.getStatuses());
159     }
160 
161     @Override
162     public List<PropagationStatus> delete(
163             final String key, final boolean nullPriorityAsync, final String eraser, final String context) {
164 
165         return delete(key, Set.of(), nullPriorityAsync, eraser, context);
166     }
167 
168     @Transactional(propagation = Propagation.REQUIRES_NEW)
169     @Override
170     public List<PropagationStatus> delete(
171             final String key,
172             final Set<String> excludedResources,
173             final boolean nullPriorityAsync,
174             final String eraser,
175             final String context) {
176 
177         List<PropagationTaskInfo> taskInfos = new ArrayList<>();
178 
179         // Generate propagation tasks for deleting users and any objects from group resources, 
180         // if they are on those resources only because of the reason being deleted (see SYNCOPE-357)
181         groupDataBinder.findUsersWithTransitiveResources(key).forEach((anyKey, propByRes) -> {
182             taskInfos.addAll(propagationManager.getDeleteTasks(
183                     AnyTypeKind.USER,
184                     anyKey,
185                     propByRes,
186                     null,
187                     excludedResources));
188         });
189         groupDataBinder.findAnyObjectsWithTransitiveResources(key).forEach((anyKey, propByRes) -> {
190             taskInfos.addAll(propagationManager.getDeleteTasks(
191                     AnyTypeKind.ANY_OBJECT,
192                     anyKey,
193                     propByRes,
194                     null,
195                     excludedResources));
196         });
197 
198         // Generate propagation tasks for deleting this group from resources
199         taskInfos.addAll(propagationManager.getDeleteTasks(
200                 AnyTypeKind.GROUP,
201                 key,
202                 null,
203                 null,
204                 null));
205 
206         PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, eraser);
207 
208         gwfAdapter.delete(key, eraser, context);
209 
210         return propagationReporter.getStatuses();
211     }
212 
213     @Override
214     public String link(final GroupUR groupUR, final String updater, final String context) {
215         return gwfAdapter.update(groupUR, updater, context).getResult().getKey();
216     }
217 
218     @Override
219     public String unlink(final GroupUR groupUR, final String updater, final String context) {
220         return gwfAdapter.update(groupUR, updater, context).getResult().getKey();
221     }
222 
223     @Override
224     public List<PropagationStatus> provision(
225             final String key,
226             final Collection<String> resources,
227             final boolean nullPriorityAsync,
228             final String executor) {
229 
230         PropagationByResource<String> propByRes = new PropagationByResource<>();
231         propByRes.addAll(ResourceOperation.UPDATE, resources);
232 
233         List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
234                 AnyTypeKind.GROUP,
235                 key,
236                 false,
237                 null,
238                 propByRes,
239                 null,
240                 null,
241                 null);
242         PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, executor);
243 
244         return propagationReporter.getStatuses();
245     }
246 
247     @Override
248     public List<PropagationStatus> deprovision(
249             final String key,
250             final Collection<String> resources,
251             final boolean nullPriorityAsync,
252             final String executor) {
253 
254         PropagationByResource<String> propByRes = new PropagationByResource<>();
255         propByRes.addAll(ResourceOperation.DELETE, resources);
256 
257         List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
258                 AnyTypeKind.GROUP,
259                 key,
260                 propByRes,
261                 null,
262                 groupDAO.findAllResourceKeys(key).stream().
263                         filter(resource -> !resources.contains(resource)).
264                         collect(Collectors.toList()));
265         PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, executor);
266 
267         return propagationReporter.getStatuses();
268     }
269 }