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