io.vertigo.account.plugins.authorization.loaders.SecuredEntityDeserializer Maven / Gradle / Ivy
The newest version!
/*
* vertigo - application development platform
*
* Copyright (C) 2013-2024, Vertigo.io, [email protected]
*
* Licensed 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 io.vertigo.account.plugins.authorization.loaders;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.vertigo.account.authorization.definitions.Authorization;
import io.vertigo.account.authorization.definitions.SecuredEntity;
import io.vertigo.account.authorization.definitions.SecurityDimension;
import io.vertigo.account.authorization.definitions.SecurityDimensionType;
import io.vertigo.account.authorization.definitions.rulemodel.RuleMultiExpression;
import io.vertigo.account.impl.authorization.dsl.rules.DslParserUtil;
import io.vertigo.commons.peg.PegNoMatchFoundException;
import io.vertigo.core.lang.Assertion;
import io.vertigo.core.lang.WrappedException;
import io.vertigo.core.util.StringUtil;
import io.vertigo.datamodel.data.definitions.DataDefinition;
import io.vertigo.datamodel.data.definitions.DataField;
import io.vertigo.datamodel.data.util.DataModelUtil;
/**
* Deserializer json
*
* @author npiedeloup
*/
public final class SecuredEntityDeserializer implements JsonDeserializer {
private static final Set SECURED_ENTITY_SUPPORTED_ATTRIBUTES = Set.of("entity", "securityFields", "securityDimensions", "operations", "__comment");
private static final Set OPERATIONS_SUPPORTED_ATTRIBUTES = Set.of("name", "label", "grants", "overrides", "rules", "__comment");
/** {@inheritDoc} */
@Override
public SecuredEntity deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) {
final JsonObject jsonSecuredEntity = json.getAsJsonObject();
final DataDefinition entityDefinition = DataModelUtil.findDataDefinition(jsonSecuredEntity.get("entity").getAsString());
//----
asserUnsupportedAttributes("SecuredEntity " + entityDefinition.getClassSimpleName(), jsonSecuredEntity, SECURED_ENTITY_SUPPORTED_ATTRIBUTES);
//----
final List securityFields = new ArrayList<>();
for (final JsonElement securityField : jsonSecuredEntity.get("securityFields").getAsJsonArray()) {
securityFields.add(deserializeDataField(entityDefinition, securityField.getAsString()));
}
final List advancedDimensions = new ArrayList<>();
for (final JsonElement advancedDimension : jsonSecuredEntity.get("securityDimensions").getAsJsonArray()) {//TODO if null ?
advancedDimensions.add(deserializeSecurityDimensions(entityDefinition, advancedDimension.getAsJsonObject(), context));
}
final Map permissionPerOperations = new HashMap<>();// on garde la map des operations pour resoudre les grants
for (final JsonElement operation : jsonSecuredEntity.get("operations").getAsJsonArray()) { //TODO if null ?
final Authorization permission = deserializeOperations(entityDefinition, operation.getAsJsonObject(), context, permissionPerOperations);
Assertion.check().isFalse(permissionPerOperations.containsKey(permission.getOperation().get()),
"Operation {0} already declared on {1}", permission.getOperation().get(), entityDefinition.getName());
permissionPerOperations.put(permission.getOperation().get(), permission);
}
return new SecuredEntity(entityDefinition, securityFields, advancedDimensions, new ArrayList<>(permissionPerOperations.values()));
}
private static Authorization deserializeOperations(
final DataDefinition entityDefinition,
final JsonObject operation,
final JsonDeserializationContext context,
final Map permissionPerOperations) {
final String code = operation.get("name").getAsString();
//----
asserUnsupportedAttributes("Operation " + code, operation, OPERATIONS_SUPPORTED_ATTRIBUTES);
//----
final String label = operation.get("label").getAsString();
final Optional comment = Optional.ofNullable(operation.get("__comment"))
.map(JsonElement::getAsString);
Set overrides = context.deserialize(operation.get("overrides"), createParameterizedType(Set.class, String.class));
if (overrides == null) {
overrides = Collections.emptySet();
}
final Set grants = resolveAuthorizations(context.deserialize(operation.get("grants"), createParameterizedType(Set.class, String.class)),
permissionPerOperations, entityDefinition);
final List rules;
final List strRules = context.deserialize(operation.get("rules"), createParameterizedType(List.class, String.class));
if (!strRules.isEmpty()) {
rules = strRules.stream()
.map(SecuredEntityDeserializer::parseRule)
.toList();
} else {
rules = Collections.emptyList(); //if empty -> always true
}
return new Authorization(code, label, overrides, grants, entityDefinition, rules, comment);
}
private static void asserUnsupportedAttributes(final String objectName, final JsonObject jsonObject, final Set supportedAttributes) {
final Set unsupportedAttributes = jsonObject.entrySet().stream()
.map(e -> e.getKey())
.collect(Collectors.toSet());
unsupportedAttributes.removeAll(supportedAttributes);
Assertion.check().isTrue(unsupportedAttributes.isEmpty(), "Json declaration of {0} can't support some attribut(s) : {1}. You may use one of {2}", objectName, unsupportedAttributes,
supportedAttributes);
}
private static Set resolveAuthorizations(final Set authorizationNames, final Map permissionPerOperations, final DataDefinition entityDefinition) {
final Set authorizations;
if (authorizationNames == null) {
authorizations = Collections.emptySet();
} else {
authorizations = authorizationNames.stream()
.map((authorizationName) -> resolvePermission(authorizationName, permissionPerOperations, entityDefinition))
.collect(Collectors.toSet());
}
return authorizations;
}
private static Authorization resolvePermission(final String operationName, final Map permissionPerOperations, final DataDefinition entityDefinition) {
Assertion.check().isTrue(permissionPerOperations.containsKey(operationName),
"Operation {0} not declared on {1} (may check declaration order)", operationName, entityDefinition.getName());
//-----
return permissionPerOperations.get(operationName);
}
private static RuleMultiExpression parseRule(final String securityRule) {
Assertion.check().isNotNull(securityRule);
//-----
try {
return DslParserUtil.parseMultiExpression(securityRule);
} catch (final PegNoMatchFoundException e) {
final String message = StringUtil.format("Echec de lecture de la securityRule {0}\n{1}", securityRule, e.getFullMessage());
throw WrappedException.wrap(e, message);
} catch (final Exception e) {
final String message = StringUtil.format("Echec de lecture de la securityRule {0}\n{1}", securityRule, e.getMessage());
throw WrappedException.wrap(e, message);
}
}
private static SecurityDimension deserializeSecurityDimensions(
final DataDefinition entityDefinition,
final JsonObject advancedDimension,
final JsonDeserializationContext context) {
final String name = advancedDimension.get("name").getAsString();
final SecurityDimensionType type = SecurityDimensionType.valueOf(advancedDimension.get("type").getAsString());
final List fieldNames = deserializeList(advancedDimension.get("fields"), String.class, context);
final List fields = fieldNames.stream()
.map(fieldName -> deserializeDataField(entityDefinition, fieldName))
.toList();
final List values = deserializeList(advancedDimension.get("values"), String.class, context);
return new SecurityDimension(name, type, fields, values);
}
private static Type createParameterizedType(final Class> rawClass, final Type paramType) {
final Type[] typeArguments = { paramType };
return new KnownParameterizedType(rawClass, typeArguments);
}
private static DataField deserializeDataField(final DataDefinition entityDefinition, final String fieldName) {
return entityDefinition.getField(fieldName);
}
private static List deserializeList(
final JsonElement jsonElement,
final Class elementClass,
final JsonDeserializationContext context) {
if (jsonElement == null) {
return Collections.emptyList();
}
return context.deserialize(jsonElement, createParameterizedType(List.class, elementClass));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy