blob: 6fabaed785816d00770caa3ccec706e5ee875f63 [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.provisioning.java.data;
import java.util.stream.Collectors;
import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeClientCompositeException;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.DerSchemaTO;
import org.apache.syncope.common.lib.to.PlainSchemaTO;
import org.apache.syncope.common.lib.to.SchemaTO;
import org.apache.syncope.common.lib.to.VirSchemaTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.Schema;
import org.apache.syncope.core.persistence.api.entity.SchemaLabel;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SchemaDataBinderImpl implements SchemaDataBinder {
protected static final Logger LOG = LoggerFactory.getLogger(SchemaDataBinder.class);
protected final AnyTypeClassDAO anyTypeClassDAO;
protected final PlainSchemaDAO plainSchemaDAO;
protected final DerSchemaDAO derSchemaDAO;
protected final VirSchemaDAO virSchemaDAO;
protected final ExternalResourceDAO resourceDAO;
protected final AnyTypeDAO anyTypeDAO;
protected final ImplementationDAO implementationDAO;
protected final EntityFactory entityFactory;
protected final AnyUtilsFactory anyUtilsFactory;
public SchemaDataBinderImpl(
final AnyTypeClassDAO anyTypeClassDAO,
final PlainSchemaDAO plainSchemaDAO,
final DerSchemaDAO derSchemaDAO,
final VirSchemaDAO virSchemaDAO,
final ExternalResourceDAO resourceDAO,
final AnyTypeDAO anyTypeDAO,
final ImplementationDAO implementationDAO,
final EntityFactory entityFactory,
final AnyUtilsFactory anyUtilsFactory) {
this.anyTypeClassDAO = anyTypeClassDAO;
this.plainSchemaDAO = plainSchemaDAO;
this.derSchemaDAO = derSchemaDAO;
this.virSchemaDAO = virSchemaDAO;
this.resourceDAO = resourceDAO;
this.anyTypeDAO = anyTypeDAO;
this.implementationDAO = implementationDAO;
this.entityFactory = entityFactory;
this.anyUtilsFactory = anyUtilsFactory;
}
protected <S extends Schema, T extends SchemaTO> void labels(final T src, final S dst) {
src.getLabels().forEach((locale, display) -> {
SchemaLabel label = dst.getLabel(locale).orElse(null);
if (label == null) {
label = entityFactory.newEntity(SchemaLabel.class);
label.setLocale(locale);
label.setSchema(dst);
dst.add(label);
}
label.setDisplay(display);
});
dst.getLabels().removeIf(label -> !src.getLabels().containsKey(label.getLocale()));
}
protected static <S extends Schema, T extends SchemaTO> void labels(final S src, final T dst) {
dst.getLabels().putAll(src.getLabels().stream().
collect(Collectors.toMap(SchemaLabel::getLocale, SchemaLabel::getDisplay)));
}
// --------------- PLAIN -----------------
protected PlainSchema fill(final PlainSchema schema, final PlainSchemaTO schemaTO) {
if (!JexlUtils.isExpressionValid(schemaTO.getMandatoryCondition())) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidValues);
sce.getElements().add(schemaTO.getMandatoryCondition());
throw sce;
}
schema.setKey(schemaTO.getKey());
schema.setType(schemaTO.getType());
schema.setCipherAlgorithm(schemaTO.getCipherAlgorithm());
schema.setConversionPattern(schemaTO.getConversionPattern());
schema.setEnumerationKeys(schemaTO.getEnumerationKeys());
schema.setEnumerationValues(schemaTO.getEnumerationValues());
schema.setMandatoryCondition(schemaTO.getMandatoryCondition());
schema.setMimeType(schemaTO.getMimeType());
schema.setMultivalue(schemaTO.isMultivalue());
schema.setReadonly(schemaTO.isReadonly());
schema.setSecretKey(schemaTO.getSecretKey());
schema.setUniqueConstraint(schemaTO.isUniqueConstraint());
labels(schemaTO, schema);
if (schemaTO.getValidator() == null) {
schema.setValidator(null);
} else {
Implementation validator = implementationDAO.find(schemaTO.getValidator());
if (validator == null) {
LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...",
schemaTO.getValidator());
} else {
schema.setValidator(validator);
}
}
PlainSchema merged = plainSchemaDAO.save(schema);
if (schemaTO.getAnyTypeClass() != null
&& (merged.getAnyTypeClass() == null
|| !schemaTO.getAnyTypeClass().equals(merged.getAnyTypeClass().getKey()))) {
AnyTypeClass anyTypeClass = anyTypeClassDAO.find(schemaTO.getAnyTypeClass());
if (anyTypeClass == null) {
LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName()
+ "{}, ignoring...", schemaTO.getAnyTypeClass());
} else {
anyTypeClass.add(merged);
merged.setAnyTypeClass(anyTypeClass);
}
} else if (schemaTO.getAnyTypeClass() == null && merged.getAnyTypeClass() != null) {
merged.getAnyTypeClass().getPlainSchemas().remove(merged);
merged.setAnyTypeClass(null);
}
return merged;
}
@Override
public PlainSchema create(final PlainSchemaTO schemaTO) {
return fill(entityFactory.newEntity(PlainSchema.class), schemaTO);
}
@Override
public PlainSchema update(final PlainSchemaTO schemaTO, final PlainSchema schema) {
SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
boolean hasAttrs = false;
for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
hasAttrs |= plainSchemaDAO.hasAttrs(schema, anyUtils.plainAttrClass());
}
if (hasAttrs) {
if (schema.getType() != schemaTO.getType()) {
SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema);
e.getElements().add("Cannot change type since " + schema.getKey() + " has attributes");
scce.addException(e);
}
if (schema.isUniqueConstraint() != schemaTO.isUniqueConstraint()) {
SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema);
e.getElements().add("Cannot alter unique contraint since " + schema.getKey() + " has attributes");
scce.addException(e);
}
}
if (scce.hasExceptions()) {
throw scce;
}
return fill(schema, schemaTO);
}
@Override
public PlainSchemaTO getPlainSchemaTO(final String key) {
PlainSchema schema = plainSchemaDAO.find(key);
if (schema == null) {
throw new NotFoundException("Schema '" + key + '\'');
}
PlainSchemaTO schemaTO = new PlainSchemaTO();
schemaTO.setKey(schema.getKey());
schemaTO.setType(schema.getType());
schemaTO.setCipherAlgorithm(schema.getCipherAlgorithm());
schemaTO.setConversionPattern(schema.getConversionPattern());
schemaTO.setEnumerationKeys(schema.getEnumerationKeys());
schemaTO.setEnumerationValues(schema.getEnumerationValues());
schemaTO.setMandatoryCondition(schema.getMandatoryCondition());
schemaTO.setMimeType(schema.getMimeType());
schemaTO.setMultivalue(schema.isMultivalue());
schemaTO.setReadonly(schema.isReadonly());
schemaTO.setSecretKey(schema.getSecretKey());
schemaTO.setUniqueConstraint(schema.isUniqueConstraint());
labels(schema, schemaTO);
schemaTO.setAnyTypeClass(schema.getAnyTypeClass() == null ? null : schema.getAnyTypeClass().getKey());
if (schema.getValidator() != null) {
schemaTO.setValidator(schema.getValidator().getKey());
}
return schemaTO;
}
// --------------- DERIVED -----------------
protected DerSchema fill(final DerSchema schema, final DerSchemaTO schemaTO) {
SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
if (StringUtils.isBlank(schemaTO.getExpression())) {
SyncopeClientException requiredValuesMissing =
SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
requiredValuesMissing.getElements().add("expression");
scce.addException(requiredValuesMissing);
} else if (!JexlUtils.isExpressionValid(schemaTO.getExpression())) {
SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidValues);
e.getElements().add(schemaTO.getExpression());
scce.addException(e);
}
if (scce.hasExceptions()) {
throw scce;
}
schema.setKey(schemaTO.getKey());
schema.setExpression(schemaTO.getExpression());
labels(schemaTO, schema);
DerSchema merged = derSchemaDAO.save(schema);
if (schemaTO.getAnyTypeClass() != null
&& (merged.getAnyTypeClass() == null
|| !schemaTO.getAnyTypeClass().equals(merged.getAnyTypeClass().getKey()))) {
AnyTypeClass anyTypeClass = anyTypeClassDAO.find(schemaTO.getAnyTypeClass());
if (anyTypeClass == null) {
LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName()
+ "{}, ignoring...", schemaTO.getAnyTypeClass());
} else {
anyTypeClass.add(merged);
merged.setAnyTypeClass(anyTypeClass);
}
} else if (schemaTO.getAnyTypeClass() == null && merged.getAnyTypeClass() != null) {
merged.getAnyTypeClass().getDerSchemas().remove(merged);
merged.setAnyTypeClass(null);
}
return merged;
}
@Override
public DerSchema create(final DerSchemaTO schemaTO) {
return fill(entityFactory.newEntity(DerSchema.class), schemaTO);
}
@Override
public DerSchema update(final DerSchemaTO schemaTO, final DerSchema schema) {
return fill(schema, schemaTO);
}
@Override
public DerSchemaTO getDerSchemaTO(final String key) {
DerSchema schema = derSchemaDAO.find(key);
if (schema == null) {
throw new NotFoundException("Derived schema '" + key + '\'');
}
DerSchemaTO schemaTO = new DerSchemaTO();
schemaTO.setKey(schema.getKey());
schemaTO.setExpression(schema.getExpression());
labels(schema, schemaTO);
schemaTO.setAnyTypeClass(schema.getAnyTypeClass() == null ? null : schema.getAnyTypeClass().getKey());
return schemaTO;
}
// --------------- VIRTUAL -----------------
protected VirSchema fill(final VirSchema schema, final VirSchemaTO schemaTO) {
schema.setKey(schemaTO.getKey());
schema.setExtAttrName(schemaTO.getExtAttrName());
schema.setReadonly(schema.isReadonly());
labels(schemaTO, schema);
if (schemaTO.getAnyTypeClass() != null
&& (schema.getAnyTypeClass() == null
|| !schemaTO.getAnyTypeClass().equals(schema.getAnyTypeClass().getKey()))) {
AnyTypeClass anyTypeClass = anyTypeClassDAO.find(schemaTO.getAnyTypeClass());
if (anyTypeClass == null) {
LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName()
+ "{}, ignoring...", schemaTO.getAnyTypeClass());
} else {
anyTypeClass.add(schema);
schema.setAnyTypeClass(anyTypeClass);
}
} else if (schemaTO.getAnyTypeClass() == null && schema.getAnyTypeClass() != null) {
schema.getAnyTypeClass().getVirSchemas().remove(schema);
schema.setAnyTypeClass(null);
}
ExternalResource resource = resourceDAO.find(schemaTO.getResource());
if (resource == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSchemaDefinition);
sce.getElements().add("Resource " + schemaTO.getResource() + " not found");
throw sce;
}
AnyType anyType = anyTypeDAO.find(schemaTO.getAnyType());
if (anyType == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSchemaDefinition);
sce.getElements().add("AnyType " + schemaTO.getAnyType() + " not found");
throw sce;
}
Provision provision = resource.getProvision(anyType).orElse(null);
if (provision == null) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSchemaDefinition);
sce.getElements().add("Provision for AnyType" + schemaTO.getAnyType()
+ " not found in " + schemaTO.getResource());
throw sce;
}
schema.setProvision(provision);
return virSchemaDAO.save(schema);
}
@Override
public VirSchema create(final VirSchemaTO schemaTO) {
return fill(entityFactory.newEntity(VirSchema.class), schemaTO);
}
@Override
public VirSchema update(final VirSchemaTO schemaTO, final VirSchema schema) {
return fill(schema, schemaTO);
}
@Override
public VirSchemaTO getVirSchemaTO(final String key) {
VirSchema schema = virSchemaDAO.find(key);
if (schema == null) {
throw new NotFoundException("Virtual Schema '" + key + '\'');
}
VirSchemaTO schemaTO = new VirSchemaTO();
schemaTO.setKey(schema.getKey());
schemaTO.setExtAttrName(schema.getExtAttrName());
schemaTO.setReadonly(schema.isReadonly());
labels(schema, schemaTO);
schemaTO.setAnyTypeClass(schema.getAnyTypeClass() == null ? null : schema.getAnyTypeClass().getKey());
schemaTO.setResource(schema.getProvision().getResource().getKey());
schemaTO.setAnyType(schema.getProvision().getAnyType().getKey());
return schemaTO;
}
}