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.Comparator;
23 import java.util.List;
24 import java.util.Map;
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.StringUtils;
30 import org.apache.commons.lang3.tuple.Pair;
31 import org.apache.syncope.common.lib.SyncopeClientException;
32 import org.apache.syncope.common.lib.to.ProvisioningResult;
33 import org.apache.syncope.common.lib.to.RealmTO;
34 import org.apache.syncope.common.lib.types.AnyTypeKind;
35 import org.apache.syncope.common.lib.types.ClientExceptionType;
36 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
37 import org.apache.syncope.common.lib.types.ResourceOperation;
38 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
39 import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO;
40 import org.apache.syncope.core.persistence.api.dao.DuplicateException;
41 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
42 import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO;
43 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
44 import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO;
45 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
46 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
47 import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
48 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
49 import org.apache.syncope.core.persistence.api.entity.Realm;
50 import org.apache.syncope.core.provisioning.api.PropagationByResource;
51 import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
52 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
53 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
54 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
55 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
56 import org.apache.syncope.core.spring.security.AuthContextUtils;
57 import org.identityconnectors.framework.common.objects.Attribute;
58 import org.springframework.security.access.prepost.PreAuthorize;
59 import org.springframework.transaction.annotation.Transactional;
60
61 public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
62
63 protected final RealmDAO realmDAO;
64
65 protected final AnySearchDAO searchDAO;
66
67 protected final TaskDAO taskDAO;
68
69 protected final CASSPClientAppDAO casSPClientAppDAO;
70
71 protected final OIDCRPClientAppDAO oidcRPClientAppDAO;
72
73 protected final SAML2SPClientAppDAO saml2SPClientAppDAO;
74
75 protected final RealmDataBinder binder;
76
77 protected final PropagationManager propagationManager;
78
79 protected final PropagationTaskExecutor taskExecutor;
80
81 public RealmLogic(
82 final RealmDAO realmDAO,
83 final AnySearchDAO searchDAO,
84 final TaskDAO taskDAO,
85 final CASSPClientAppDAO casSPClientAppDAO,
86 final OIDCRPClientAppDAO oidcRPClientAppDAO,
87 final SAML2SPClientAppDAO saml2SPClientAppDAO,
88 final RealmDataBinder binder,
89 final PropagationManager propagationManager,
90 final PropagationTaskExecutor taskExecutor) {
91
92 this.realmDAO = realmDAO;
93 this.searchDAO = searchDAO;
94 this.taskDAO = taskDAO;
95 this.casSPClientAppDAO = casSPClientAppDAO;
96 this.oidcRPClientAppDAO = oidcRPClientAppDAO;
97 this.saml2SPClientAppDAO = saml2SPClientAppDAO;
98 this.binder = binder;
99 this.propagationManager = propagationManager;
100 this.taskExecutor = taskExecutor;
101 }
102
103 @PreAuthorize("isAuthenticated()")
104 @Transactional(readOnly = true)
105 public Pair<Integer, List<RealmTO>> search(
106 final String keyword,
107 final String base,
108 final int page,
109 final int size) {
110
111 Realm baseRealm = Optional.ofNullable(base == null ? realmDAO.getRoot() : realmDAO.findByFullPath(base)).
112 orElseThrow(() -> new NotFoundException(base));
113
114 int count = realmDAO.countDescendants(baseRealm.getFullPath(), keyword);
115
116 List<Realm> result = realmDAO.findDescendants(baseRealm.getFullPath(), keyword, page, size);
117
118 return Pair.of(
119 count,
120 result.stream().map(realm -> binder.getRealmTO(
121 realm,
122 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.REALM_SEARCH).stream().
123 anyMatch(auth -> realm.getFullPath().startsWith(auth)))).
124 sorted(Comparator.comparing(RealmTO::getFullPath)).
125 collect(Collectors.toList()));
126 }
127
128 @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_CREATE + "')")
129 public ProvisioningResult<RealmTO> create(final String parentPath, final RealmTO realmTO) {
130 Realm parent;
131 if (StringUtils.isBlank(realmTO.getParent())) {
132 parent = Optional.ofNullable(realmDAO.findByFullPath(parentPath)).
133 orElseThrow(() -> new NotFoundException(parentPath));
134
135 realmTO.setParent(parent.getFullPath());
136 } else {
137 parent = Optional.ofNullable(realmDAO.find(realmTO.getParent())).
138 orElseThrow(() -> new NotFoundException(realmTO.getParent()));
139
140 if (!parent.getFullPath().equals(parentPath)) {
141 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPath);
142 sce.getElements().add("Mismatching parent realm: " + parentPath + " Vs " + parent.getFullPath());
143 throw sce;
144 }
145 }
146
147 String fullPath = StringUtils.appendIfMissing(parent.getFullPath(), "/") + realmTO.getName();
148 if (realmDAO.findByFullPath(fullPath) != null) {
149 throw new DuplicateException(fullPath);
150 }
151
152 Realm realm = realmDAO.save(binder.create(parent, realmTO));
153 PropagationByResource<String> propByRes = new PropagationByResource<>();
154 propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
155 List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
156 PropagationReporter propagationReporter =
157 taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
158
159 ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
160 result.setEntity(binder.getRealmTO(realm, true));
161 result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
162
163 return result;
164 }
165
166 @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_UPDATE + "')")
167 public ProvisioningResult<RealmTO> update(final RealmTO realmTO) {
168 Realm realm = Optional.ofNullable(realmDAO.findByFullPath(realmTO.getFullPath())).
169 orElseThrow(() -> new NotFoundException(realmTO.getFullPath()));
170
171 Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(realm);
172
173 PropagationByResource<String> propByRes = binder.update(realm, realmTO);
174 realm = realmDAO.save(realm);
175
176 List<PropagationTaskInfo> taskInfos = propagationManager.setAttributeDeltas(
177 propagationManager.createTasks(realm, propByRes, null),
178 beforeAttrs);
179 PropagationReporter propagationReporter =
180 taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
181
182 ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
183 result.setEntity(binder.getRealmTO(realm, true));
184 result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
185
186 return result;
187 }
188
189 @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_DELETE + "')")
190 public ProvisioningResult<RealmTO> delete(final String fullPath) {
191 Realm realm = Optional.ofNullable(realmDAO.findByFullPath(fullPath)).
192 orElseThrow(() -> new NotFoundException(fullPath));
193
194 if (!realmDAO.findChildren(realm).isEmpty()) {
195 throw SyncopeClientException.build(ClientExceptionType.RealmContains);
196 }
197
198 Set<String> adminRealms = Set.of(realm.getFullPath());
199 AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
200 keyCond.setSchema("key");
201 SearchCond allMatchingCond = SearchCond.getLeaf(keyCond);
202 int users = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.USER);
203 int groups = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.GROUP);
204 int anyObjects = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT);
205 int macroTasks = taskDAO.findByRealm(realm).size();
206 int clientApps = casSPClientAppDAO.findByRealm(realm).size()
207 + saml2SPClientAppDAO.findByRealm(realm).size()
208 + oidcRPClientAppDAO.findByRealm(realm).size();
209
210 if (users + groups + anyObjects + macroTasks + clientApps > 0) {
211 SyncopeClientException realmContains = SyncopeClientException.build(ClientExceptionType.RealmContains);
212 realmContains.getElements().add(users + " user(s)");
213 realmContains.getElements().add(groups + " group(s)");
214 realmContains.getElements().add(anyObjects + " anyObject(s)");
215 realmContains.getElements().add(macroTasks + " command task(s)");
216 realmContains.getElements().add(clientApps + " client app(s)");
217 throw realmContains;
218 }
219
220 PropagationByResource<String> propByRes = new PropagationByResource<>();
221 propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
222 List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
223 PropagationReporter propagationReporter =
224 taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
225
226 ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
227 result.setEntity(binder.getRealmTO(realm, true));
228 result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
229
230 realmDAO.delete(realm);
231
232 return result;
233 }
234
235 @Override
236 protected RealmTO resolveReference(final Method method, final Object... args)
237 throws UnresolvedReferenceException {
238
239 String fullPath = null;
240
241 if (ArrayUtils.isNotEmpty(args)) {
242 for (int i = 0; fullPath == null && i < args.length; i++) {
243 if (args[i] instanceof String) {
244 fullPath = (String) args[i];
245 } else if (args[i] instanceof RealmTO) {
246 fullPath = ((RealmTO) args[i]).getFullPath();
247 }
248 }
249 }
250
251 if (fullPath != null) {
252 try {
253 return binder.getRealmTO(realmDAO.findByFullPath(fullPath), true);
254 } catch (Throwable e) {
255 LOG.debug("Unresolved reference", e);
256 throw new UnresolvedReferenceException(e);
257 }
258 }
259
260 throw new UnresolvedReferenceException();
261 }
262 }