/*
* 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.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.patch.AnyPatch;
import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.PropagationStatus;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.RealmDAO;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.provisioning.api.LogicActions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> extends AbstractResourceAssociator<TO> {
@Autowired
private RealmDAO realmDAO;
@Autowired
private AnyTypeDAO anyTypeDAO;
@Autowired
private TemplateUtils templateUtils;
private List<LogicActions> getActions(final Realm realm) {
List<LogicActions> actions = new ArrayList<>();
for (String className : realm.getActionsClassNames()) {
try {
Class<?> actionsClass = Class.forName(className);
LogicActions logicActions = (LogicActions) ApplicationContextProvider.getBeanFactory().
createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
actions.add(logicActions);
} catch (Exception e) {
LOG.warn("Class '{}' not found", className, e);
}
}
return actions;
}
protected Pair<TO, List<LogicActions>> beforeCreate(final TO input) {
Realm realm = realmDAO.findByFullPath(input.getRealm());
if (realm == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
sce.getElements().add(input.getRealm());
throw sce;
}
AnyType anyType = input instanceof UserTO
? anyTypeDAO.findUser()
: input instanceof GroupTO
? anyTypeDAO.findGroup()
: anyTypeDAO.find(input.getType());
if (anyType == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
sce.getElements().add(input.getType());
throw sce;
}
TO any = input;
templateUtils.apply(any, realm.getTemplate(anyType));
List<LogicActions> actions = getActions(realm);
for (LogicActions action : actions) {
any = action.beforeCreate(any);
}
LOG.debug("Input: {}\nOutput: {}\n", input, any);
return ImmutablePair.of(any, actions);
}
protected Pair<P, List<LogicActions>> beforeUpdate(final P input, final String realmPath) {
Realm realm = realmDAO.findByFullPath(realmPath);
if (realm == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
sce.getElements().add(realmPath);
throw sce;
}
P mod = input;
List<LogicActions> actions = getActions(realm);
for (LogicActions action : actions) {
mod = action.beforeUpdate(mod);
}
LOG.debug("Input: {}\nOutput: {}\n", input, mod);
return ImmutablePair.of(mod, actions);
}
protected Pair<TO, List<LogicActions>> beforeDelete(final TO input) {
Realm realm = realmDAO.findByFullPath(input.getRealm());
if (realm == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
sce.getElements().add(input.getRealm());
throw sce;
}
TO any = input;
List<LogicActions> actions = getActions(realm);
for (LogicActions action : actions) {
any = action.beforeDelete(any);
}
LOG.debug("Input: {}\nOutput: {}\n", input, any);
return ImmutablePair.of(any, actions);
}
protected ProvisioningResult<TO> after(
final TO input, final List<PropagationStatus> statuses, final List<LogicActions> actions) {
TO any = input;
for (LogicActions action : actions) {
any = action.afterCreate(any);
}
ProvisioningResult<TO> result = new ProvisioningResult<>();
result.setEntity(any);
result.getPropagationStatuses().addAll(statuses);
return result;
}
private static class StartsWithPredicate implements Predicate<String> {
private final Collection<String> targets;
StartsWithPredicate(final Collection<String> targets) {
this.targets = targets;
}
@Override
public boolean evaluate(final String realm) {
return IterableUtils.matchesAny(targets, new Predicate<String>() {
@Override
public boolean evaluate(final String target) {
return realm.startsWith(target);
}
});
}
}
protected Set<String> getEffectiveRealms(
final Set<String> allowedRealms, final String requestedRealm) {
Set<String> allowed = RealmUtils.normalize(allowedRealms);
Set<String> requested = new HashSet<>();
requested.add(requestedRealm);
Set<String> effective = new HashSet<>();
CollectionUtils.select(requested, new StartsWithPredicate(allowed), effective);
CollectionUtils.select(allowed, new StartsWithPredicate(requested), effective);
return effective;
}
protected void securityChecks(final Set<String> effectiveRealms, final String realm, final String key) {
if (!IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() {
@Override
public boolean evaluate(final String ownedRealm) {
return realm.startsWith(ownedRealm);
}
})) {
throw new DelegatedAdministrationException(
this instanceof UserLogic
? AnyTypeKind.USER
: this instanceof GroupLogic
? AnyTypeKind.GROUP
: AnyTypeKind.ANY_OBJECT,
key);
}
}
public abstract TO read(String key);
public abstract int count(String realm);
public abstract ProvisioningResult<TO> create(TO anyTO, boolean nullPriorityAsync);
public abstract ProvisioningResult<TO> update(P anyPatch, boolean nullPriorityAsync);
public abstract ProvisioningResult<TO> delete(String key, boolean nullPriorityAsync);
public abstract List<TO> list(
int page, int size, List<OrderByClause> orderBy,
String realm,
boolean details);
public abstract List<TO> search(
SearchCond searchCondition,
int page, int size, List<OrderByClause> orderBy,
String realm,
boolean details);
public abstract int searchCount(SearchCond searchCondition, String realm);
}