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 null);
180 PropagationReporter propagationReporter =
181 taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
182
183 ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
184 result.setEntity(binder.getRealmTO(realm, true));
185 result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
186
187 return result;
188 }
189
190 @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_DELETE + "')")
191 public ProvisioningResult<RealmTO> delete(final String fullPath) {
192 Realm realm = Optional.ofNullable(realmDAO.findByFullPath(fullPath)).
193 orElseThrow(() -> new NotFoundException(fullPath));
194
195 if (!realmDAO.findChildren(realm).isEmpty()) {
196 throw SyncopeClientException.build(ClientExceptionType.RealmContains);
197 }
198
199 Set<String> adminRealms = Set.of(realm.getFullPath());
200 AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
201 keyCond.setSchema("key");
202 SearchCond allMatchingCond = SearchCond.getLeaf(keyCond);
203 int users = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.USER);
204 int groups = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.GROUP);
205 int anyObjects = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT);
206 int macroTasks = taskDAO.findByRealm(realm).size();
207 int clientApps = casSPClientAppDAO.findByRealm(realm).size()
208 + saml2SPClientAppDAO.findByRealm(realm).size()
209 + oidcRPClientAppDAO.findByRealm(realm).size();
210
211 if (users + groups + anyObjects + macroTasks + clientApps > 0) {
212 SyncopeClientException realmContains = SyncopeClientException.build(ClientExceptionType.RealmContains);
213 realmContains.getElements().add(users + " user(s)");
214 realmContains.getElements().add(groups + " group(s)");
215 realmContains.getElements().add(anyObjects + " anyObject(s)");
216 realmContains.getElements().add(macroTasks + " command task(s)");
217 realmContains.getElements().add(clientApps + " client app(s)");
218 throw realmContains;
219 }
220
221 PropagationByResource<String> propByRes = new PropagationByResource<>();
222 propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
223 List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
224 PropagationReporter propagationReporter =
225 taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
226
227 ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
228 result.setEntity(binder.getRealmTO(realm, true));
229 result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
230
231 realmDAO.delete(realm);
232
233 return result;
234 }
235
236 @Override
237 protected RealmTO resolveReference(final Method method, final Object... args)
238 throws UnresolvedReferenceException {
239
240 String fullPath = null;
241
242 if (ArrayUtils.isNotEmpty(args)) {
243 for (int i = 0; fullPath == null && i < args.length; i++) {
244 if (args[i] instanceof String) {
245 fullPath = (String) args[i];
246 } else if (args[i] instanceof RealmTO) {
247 fullPath = ((RealmTO) args[i]).getFullPath();
248 }
249 }
250 }
251
252 if (fullPath != null) {
253 try {
254 return binder.getRealmTO(realmDAO.findByFullPath(fullPath), true);
255 } catch (Throwable e) {
256 LOG.debug("Unresolved reference", e);
257 throw new UnresolvedReferenceException(e);
258 }
259 }
260
261 throw new UnresolvedReferenceException();
262 }
263 }