blob: bd1a0a96f62c6ab0c4da3ede741cde03619a8674 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.logic;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
import org.apache.syncope.core.persistence.api.dao.DuplicateException;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.RealmDAO;
import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
protected final RealmDAO realmDAO;
protected final AnySearchDAO searchDAO;
protected final RealmDataBinder binder;
protected final PropagationManager propagationManager;
protected final PropagationTaskExecutor taskExecutor;
public RealmLogic(
final RealmDAO realmDAO,
final AnySearchDAO searchDAO,
final RealmDataBinder binder,
final PropagationManager propagationManager,
final PropagationTaskExecutor taskExecutor) {
this.realmDAO = realmDAO;
this.searchDAO = searchDAO;
this.binder = binder;
this.propagationManager = propagationManager;
this.taskExecutor = taskExecutor;
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_LIST + "')")
@Transactional(readOnly = true)
public Pair<Integer, List<RealmTO>> search(final String keyword, final String base) {
Realm baseRealm = base == null ? realmDAO.getRoot() : realmDAO.findByFullPath(base);
if (baseRealm == null) {
LOG.error("Could not find realm '" + base + "'");
throw new NotFoundException(base);
}
Set<String> roots = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.REALM_LIST).stream().
filter(auth -> auth.startsWith(baseRealm.getFullPath())).collect(Collectors.toSet());
Set<Realm> match = realmDAO.findMatching(keyword).stream().
filter(realm -> roots.stream().anyMatch(root -> realm.getFullPath().startsWith(root))).
collect(Collectors.toSet());
int descendants = Math.toIntExact(
match.stream().flatMap(realm -> realmDAO.findDescendants(realm).stream()).distinct().count());
return Pair.of(
descendants,
match.stream().map(realm -> binder.getRealmTO(realm, true)).
sorted(Comparator.comparing(RealmTO::getFullPath)).
collect(Collectors.toList()));
}
@PreAuthorize("isAuthenticated()")
@Transactional(readOnly = true)
public List<RealmTO> list(final String fullPath) {
Realm realm = realmDAO.findByFullPath(fullPath);
if (realm == null) {
LOG.error("Could not find realm '" + fullPath + '\'');
throw new NotFoundException(fullPath);
}
boolean admin = AuthContextUtils.getAuthorizations().keySet().contains(IdRepoEntitlement.REALM_LIST);
return realmDAO.findDescendants(realm).stream().
map(descendant -> binder.getRealmTO(descendant, admin)).collect(Collectors.toList());
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_CREATE + "')")
public ProvisioningResult<RealmTO> create(final String parentPath, final RealmTO realmTO) {
Realm parent;
if (StringUtils.isBlank(realmTO.getParent())) {
parent = realmDAO.findByFullPath(parentPath);
if (parent == null) {
LOG.error("Could not find parent realm " + parentPath);
throw new NotFoundException(parentPath);
}
realmTO.setParent(parent.getFullPath());
} else {
parent = realmDAO.find(realmTO.getParent());
if (parent == null) {
LOG.error("Could not find parent realm " + realmTO.getParent());
throw new NotFoundException(realmTO.getParent());
}
if (!parent.getFullPath().equals(parentPath)) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPath);
sce.getElements().add("Mismatching parent realm: " + parentPath + " Vs " + parent.getFullPath());
throw sce;
}
}
String fullPath = StringUtils.appendIfMissing(parent.getFullPath(), "/") + realmTO.getName();
if (realmDAO.findByFullPath(fullPath) != null) {
throw new DuplicateException(fullPath);
}
Realm realm = realmDAO.save(binder.create(parent, realmTO));
PropagationByResource<String> propByRes = new PropagationByResource<>();
propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
result.setEntity(binder.getRealmTO(realm, true));
result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
return result;
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_UPDATE + "')")
public ProvisioningResult<RealmTO> update(final RealmTO realmTO) {
Realm realm = realmDAO.findByFullPath(realmTO.getFullPath());
if (realm == null) {
LOG.error("Could not find realm '" + realmTO.getFullPath() + '\'');
throw new NotFoundException(realmTO.getFullPath());
}
PropagationByResource<String> propByRes = binder.update(realm, realmTO);
realm = realmDAO.save(realm);
List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
result.setEntity(binder.getRealmTO(realm, true));
result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
return result;
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_DELETE + "')")
public ProvisioningResult<RealmTO> delete(final String fullPath) {
Realm realm = realmDAO.findByFullPath(fullPath);
if (realm == null) {
LOG.error("Could not find realm '" + fullPath + '\'');
throw new NotFoundException(fullPath);
}
if (!realmDAO.findChildren(realm).isEmpty()) {
throw SyncopeClientException.build(ClientExceptionType.HasChildren);
}
Set<String> adminRealms = Set.of(realm.getFullPath());
AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
keyCond.setSchema("key");
SearchCond allMatchingCond = SearchCond.getLeaf(keyCond);
int users = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.USER);
int groups = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.GROUP);
int anyObjects = searchDAO.count(adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT);
if (users + groups + anyObjects > 0) {
SyncopeClientException containedAnys = SyncopeClientException.build(ClientExceptionType.AssociatedAnys);
containedAnys.getElements().add(users + " user(s)");
containedAnys.getElements().add(groups + " group(s)");
containedAnys.getElements().add(anyObjects + " anyObject(s)");
throw containedAnys;
}
PropagationByResource<String> propByRes = new PropagationByResource<>();
propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, false, AuthContextUtils.getUsername());
ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
result.setEntity(binder.getRealmTO(realm, true));
result.getPropagationStatuses().addAll(propagationReporter.getStatuses());
realmDAO.delete(realm);
return result;
}
@Override
protected RealmTO resolveReference(final Method method, final Object... args)
throws UnresolvedReferenceException {
String fullPath = null;
if (ArrayUtils.isNotEmpty(args)) {
for (int i = 0; fullPath == null && i < args.length; i++) {
if (args[i] instanceof String) {
fullPath = (String) args[i];
} else if (args[i] instanceof RealmTO) {
fullPath = ((RealmTO) args[i]).getFullPath();
}
}
}
if (fullPath != null) {
try {
return binder.getRealmTO(realmDAO.findByFullPath(fullPath), true);
} catch (Throwable e) {
LOG.debug("Unresolved reference", e);
throw new UnresolvedReferenceException(e);
}
}
throw new UnresolvedReferenceException();
}
}