1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.logic;
20
21 import java.lang.reflect.Method;
22 import java.util.Collection;
23 import java.util.Comparator;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import org.apache.commons.lang3.ArrayUtils;
30 import org.apache.commons.lang3.tuple.Pair;
31 import org.apache.syncope.common.lib.SyncopeClientException;
32 import org.apache.syncope.common.lib.request.AnyObjectCR;
33 import org.apache.syncope.common.lib.request.AnyObjectUR;
34 import org.apache.syncope.common.lib.request.MembershipUR;
35 import org.apache.syncope.common.lib.request.StringPatchItem;
36 import org.apache.syncope.common.lib.to.AnyObjectTO;
37 import org.apache.syncope.common.lib.to.MembershipTO;
38 import org.apache.syncope.common.lib.to.PropagationStatus;
39 import org.apache.syncope.common.lib.to.ProvisioningResult;
40 import org.apache.syncope.common.lib.types.AnyEntitlement;
41 import org.apache.syncope.common.lib.types.AnyTypeKind;
42 import org.apache.syncope.common.lib.types.ClientExceptionType;
43 import org.apache.syncope.common.lib.types.PatchOperation;
44 import org.apache.syncope.core.logic.api.LogicActions;
45 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
46 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
47 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
48 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
49 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
50 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
51 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
52 import org.apache.syncope.core.persistence.api.entity.AnyType;
53 import org.apache.syncope.core.persistence.api.entity.Realm;
54 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
55 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
56 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
57 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
58 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
59 import org.apache.syncope.core.spring.security.AuthContextUtils;
60 import org.springframework.transaction.annotation.Transactional;
61
62
63
64
65
66 public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectCR, AnyObjectUR> {
67
68 protected final AnyObjectDAO anyObjectDAO;
69
70 protected final AnySearchDAO searchDAO;
71
72 protected final AnyObjectDataBinder binder;
73
74 protected final AnyObjectProvisioningManager provisioningManager;
75
76 public AnyObjectLogic(
77 final RealmDAO realmDAO,
78 final AnyTypeDAO anyTypeDAO,
79 final TemplateUtils templateUtils,
80 final AnyObjectDAO anyObjectDAO,
81 final AnySearchDAO searchDAO,
82 final AnyObjectDataBinder binder,
83 final AnyObjectProvisioningManager provisioningManager) {
84
85 super(realmDAO, anyTypeDAO, templateUtils);
86
87 this.anyObjectDAO = anyObjectDAO;
88 this.searchDAO = searchDAO;
89 this.binder = binder;
90 this.provisioningManager = provisioningManager;
91 }
92
93 @Transactional(readOnly = true)
94 @Override
95 public AnyObjectTO read(final String key) {
96 return binder.getAnyObjectTO(key);
97 }
98
99 @Transactional(readOnly = true)
100 public AnyObjectTO read(final String type, final String name) {
101 return Optional.ofNullable(anyObjectDAO.findKey(type, name)).
102 map(binder::getAnyObjectTO).
103 orElseThrow(() -> new NotFoundException("AnyObject " + type + " " + name));
104 }
105
106 @Transactional(readOnly = true)
107 @Override
108 public Pair<Integer, List<AnyObjectTO>> search(
109 final SearchCond searchCond,
110 final int page, final int size, final List<OrderByClause> orderBy,
111 final String realm,
112 final boolean recursive,
113 final boolean details) {
114
115 if (searchCond.hasAnyTypeCond() == null) {
116 throw new UnsupportedOperationException("Need to specify " + AnyType.class.getSimpleName());
117 }
118
119 Realm base = Optional.ofNullable(realmDAO.findByFullPath(realm)).
120 orElseThrow(() -> new NotFoundException("Realm " + realm));
121
122 Set<String> authRealms = RealmUtils.getEffective(
123 AuthContextUtils.getAuthorizations().get(AnyEntitlement.SEARCH.getFor(searchCond.hasAnyTypeCond())),
124 realm);
125
126 int count = searchDAO.count(base, recursive, authRealms, searchCond, AnyTypeKind.ANY_OBJECT);
127
128 List<AnyObject> matching = searchDAO.search(
129 base, recursive, authRealms, searchCond, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
130 List<AnyObjectTO> result = matching.stream().
131 map(anyObject -> binder.getAnyObjectTO(anyObject, details)).
132 collect(Collectors.toList());
133
134 return Pair.of(count, result);
135 }
136
137 public ProvisioningResult<AnyObjectTO> create(final AnyObjectCR createReq, final boolean nullPriorityAsync) {
138 Pair<AnyObjectCR, List<LogicActions>> before = beforeCreate(createReq);
139
140 if (before.getLeft().getRealm() == null) {
141 throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
142 }
143 if (before.getLeft().getType() == null) {
144 throw SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
145 }
146
147 Set<String> authRealms = RealmUtils.getEffective(
148 AuthContextUtils.getAuthorizations().get(AnyEntitlement.CREATE.getFor(before.getLeft().getType())),
149 before.getLeft().getRealm());
150 anyObjectDAO.securityChecks(
151 authRealms,
152 null,
153 before.getLeft().getRealm(),
154 before.getLeft().getMemberships().stream().filter(Objects::nonNull).
155 map(MembershipTO::getGroupKey).filter(Objects::nonNull).
156 collect(Collectors.toSet()));
157
158 Pair<String, List<PropagationStatus>> created = provisioningManager.create(
159 before.getLeft(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
160
161 return afterCreate(binder.getAnyObjectTO(created.getKey()), created.getRight(), before.getRight());
162 }
163
164 protected Set<String> groups(final AnyObjectTO anyObjectTO) {
165 return anyObjectTO.getMemberships().stream().filter(Objects::nonNull).
166 map(MembershipTO::getGroupKey).filter(Objects::nonNull).
167 collect(Collectors.toSet());
168 }
169
170 @Override
171 public ProvisioningResult<AnyObjectTO> update(final AnyObjectUR req, final boolean nullPriorityAsync) {
172 AnyObjectTO anyObjectTO = binder.getAnyObjectTO(req.getKey());
173 Pair<AnyObjectUR, List<LogicActions>> before = beforeUpdate(req, anyObjectTO.getRealm());
174
175 Set<String> authRealms = RealmUtils.getEffective(
176 AuthContextUtils.getAuthorizations().get(AnyEntitlement.UPDATE.getFor(anyObjectTO.getType())),
177 anyObjectTO.getRealm());
178
179 Set<String> groups = groups(anyObjectTO);
180 groups.removeAll(req.getMemberships().stream().filter(Objects::nonNull).
181 filter(m -> m.getOperation() == PatchOperation.DELETE).
182 map(MembershipUR::getGroup).filter(Objects::nonNull).
183 collect(Collectors.toSet()));
184
185 anyObjectDAO.securityChecks(
186 authRealms,
187 before.getLeft().getKey(),
188 anyObjectTO.getRealm(),
189 groups);
190
191 Pair<AnyObjectUR, List<PropagationStatus>> after = provisioningManager.update(
192 req, Set.of(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
193
194 ProvisioningResult<AnyObjectTO> result = afterUpdate(
195 binder.getAnyObjectTO(after.getLeft().getKey()),
196 after.getRight(),
197 before.getRight());
198
199 return result;
200 }
201
202 @Override
203 public ProvisioningResult<AnyObjectTO> delete(final String key, final boolean nullPriorityAsync) {
204 Pair<AnyObjectTO, List<LogicActions>> before = beforeDelete(binder.getAnyObjectTO(key));
205
206 Set<String> authRealms = RealmUtils.getEffective(
207 AuthContextUtils.getAuthorizations().get(AnyEntitlement.DELETE.getFor(before.getLeft().getType())),
208 before.getLeft().getRealm());
209 anyObjectDAO.securityChecks(
210 authRealms,
211 before.getLeft().getKey(),
212 before.getLeft().getRealm(),
213 groups(before.getLeft()));
214
215 List<PropagationStatus> statuses = provisioningManager.delete(
216 before.getLeft().getKey(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
217
218 AnyObjectTO deletedTO;
219 if (anyObjectDAO.find(before.getLeft().getKey()) == null) {
220 deletedTO = new AnyObjectTO();
221 deletedTO.setKey(before.getLeft().getKey());
222 } else {
223 deletedTO = binder.getAnyObjectTO(before.getLeft().getKey());
224 }
225
226 return afterDelete(deletedTO, statuses, before.getRight());
227 }
228
229 protected void updateChecks(final String key) {
230 AnyObject anyObject = anyObjectDAO.authFind(key);
231
232 Set<String> authRealms = RealmUtils.getEffective(
233 AuthContextUtils.getAuthorizations().get(AnyEntitlement.UPDATE.getFor(anyObject.getType().getKey())),
234 anyObject.getRealm().getFullPath());
235 anyObjectDAO.securityChecks(
236 authRealms,
237 anyObject.getKey(),
238 anyObject.getRealm().getFullPath(),
239 anyObject.getMemberships().stream().
240 map(m -> m.getRightEnd().getKey()).
241 collect(Collectors.toSet()));
242 }
243
244 @Override
245 public AnyObjectTO unlink(final String key, final Collection<String> resources) {
246 updateChecks(key);
247
248 AnyObjectUR req = new AnyObjectUR.Builder(key).
249 resources(resources.stream().
250 map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()).
251 collect(Collectors.toList())).
252 build();
253
254 return binder.getAnyObjectTO(provisioningManager.unlink(req, AuthContextUtils.getUsername(), REST_CONTEXT));
255 }
256
257 @Override
258 public AnyObjectTO link(final String key, final Collection<String> resources) {
259 updateChecks(key);
260
261 AnyObjectUR req = new AnyObjectUR.Builder(key).
262 resources(resources.stream().
263 map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()).
264 collect(Collectors.toList())).
265 build();
266
267 return binder.getAnyObjectTO(provisioningManager.link(req, AuthContextUtils.getUsername(), REST_CONTEXT));
268 }
269
270 @Override
271 public ProvisioningResult<AnyObjectTO> unassign(
272 final String key, final Collection<String> resources, final boolean nullPriorityAsync) {
273
274 updateChecks(key);
275
276 AnyObjectUR req = new AnyObjectUR.Builder(key).
277 resources(resources.stream().
278 map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()).
279 collect(Collectors.toList())).
280 build();
281
282 return update(req, nullPriorityAsync);
283 }
284
285 @Override
286 public ProvisioningResult<AnyObjectTO> assign(
287 final String key,
288 final Collection<String> resources,
289 final boolean changepwd,
290 final String password,
291 final boolean nullPriorityAsync) {
292
293 updateChecks(key);
294
295 AnyObjectUR req = new AnyObjectUR.Builder(key).
296 resources(resources.stream().
297 map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()).
298 collect(Collectors.toList())).
299 build();
300 return update(req, nullPriorityAsync);
301 }
302
303 @Override
304 public ProvisioningResult<AnyObjectTO> deprovision(
305 final String key,
306 final List<String> resources,
307 final boolean nullPriorityAsync) {
308
309 updateChecks(key);
310
311 List<PropagationStatus> statuses = provisioningManager.deprovision(
312 key, resources, nullPriorityAsync, AuthContextUtils.getUsername());
313
314 ProvisioningResult<AnyObjectTO> result = new ProvisioningResult<>();
315 result.setEntity(binder.getAnyObjectTO(key));
316 result.getPropagationStatuses().addAll(statuses);
317 result.getPropagationStatuses().sort(Comparator.comparing(item -> resources.indexOf(item.getResource())));
318 return result;
319 }
320
321 @Override
322 public ProvisioningResult<AnyObjectTO> provision(
323 final String key,
324 final List<String> resources,
325 final boolean changePwd,
326 final String password,
327 final boolean nullPriorityAsync) {
328
329 updateChecks(key);
330
331 List<PropagationStatus> statuses = provisioningManager.provision(
332 key, resources, nullPriorityAsync, AuthContextUtils.getUsername());
333
334 ProvisioningResult<AnyObjectTO> result = new ProvisioningResult<>();
335 result.setEntity(binder.getAnyObjectTO(key));
336 result.getPropagationStatuses().addAll(statuses);
337 result.getPropagationStatuses().sort(Comparator.comparing(item -> resources.indexOf(item.getResource())));
338 return result;
339 }
340
341 @Override
342 protected AnyObjectTO resolveReference(final Method method, final Object... args)
343 throws UnresolvedReferenceException {
344
345 String key = null;
346
347 if (ArrayUtils.isNotEmpty(args)) {
348 for (int i = 0; key == null && i < args.length; i++) {
349 if (args[i] instanceof String) {
350 key = (String) args[i];
351 } else if (args[i] instanceof AnyObjectTO) {
352 key = ((AnyObjectTO) args[i]).getKey();
353 } else if (args[i] instanceof AnyObjectUR) {
354 key = ((AnyObjectUR) args[i]).getKey();
355 }
356 }
357 }
358
359 if (key != null) {
360 try {
361 return binder.getAnyObjectTO(key);
362 } catch (Throwable ignore) {
363 LOG.debug("Unresolved reference", ignore);
364 throw new UnresolvedReferenceException(ignore);
365 }
366 }
367
368 throw new UnresolvedReferenceException();
369 }
370 }