
de.fraunhofer.iese.ind2uce.reactive.RxPEPFactory Maven / Gradle / Ivy
/*-
* =================================LICENSE_START=================================
* IND2UCE
* %%
* Copyright (C) 2016 Fraunhofer IESE (www.iese.fraunhofer.de)
* %%
* 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.
* =================================LICENSE_END=================================
*/
package de.fraunhofer.iese.ind2uce.reactive;
import de.fraunhofer.iese.ind2uce.api.component.description.InputParameterDescription;
import de.fraunhofer.iese.ind2uce.api.component.description.ModifierInterfaceDescription;
import de.fraunhofer.iese.ind2uce.api.component.description.PepInterfaceDescription;
import de.fraunhofer.iese.ind2uce.api.component.identifier.ComponentId;
import de.fraunhofer.iese.ind2uce.api.policy.AuthorizationDecision;
import de.fraunhofer.iese.ind2uce.api.policy.Event;
import de.fraunhofer.iese.ind2uce.api.policy.identifier.ActionId;
import de.fraunhofer.iese.ind2uce.connectors.OAuthCredentials;
import de.fraunhofer.iese.ind2uce.json.schema.JsonSchemaGenerator;
import de.fraunhofer.iese.ind2uce.logger.LoggerFactory;
import de.fraunhofer.iese.ind2uce.pep.PolicyEnforcementPoint;
import de.fraunhofer.iese.ind2uce.pep.common.DecisionEnforcer;
import de.fraunhofer.iese.ind2uce.pep.common.ModifierMethod;
import de.fraunhofer.iese.ind2uce.pep.enforce.JsonPathDecisionEnforcer;
import de.fraunhofer.iese.ind2uce.reactive.common.EventParameter;
import de.fraunhofer.iese.ind2uce.reactive.common.EventSpecification;
import de.fraunhofer.iese.ind2uce.reactive.common.IncorrectPEPDescriptionError;
import de.fraunhofer.iese.ind2uce.reactive.common.PEPServiceDescription;
import de.fraunhofer.iese.ind2uce.reactive.common.PEPType;
import de.fraunhofer.iese.ind2uce.reactive.common.ProvidedModifiers;
import de.fraunhofer.iese.ind2uce.registry.ActionDescription;
import de.fraunhofer.iese.ind2uce.registry.ActionParameterDescription;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import rx.Observable;
/**
* Entry Point to define reactive Policy Enforcement Points.
* This Factory has the capability to create Reactive PEP.
*/
public class RxPEPFactory {
protected static Logger LOG = LoggerFactory.getLogger(PolicyEnforcementPoint.class);
/**
* Creates a new ReactivePEP object.
*
* @param the generic type
* @param pepDocumentationApi the pep documentation api
* @param pmpUri the pmp uri
* @param decisionEnforcer the decision enforcer
* @param aliveCheckUri the alive check uri
* @param credentials the credentials
* @return the rx PEP of type T
*/
public static ReactivePEP createRxPEP(final Class pepDocumentationApi, URI pmpUri, DecisionEnforcer decisionEnforcer, URI aliveCheckUri, OAuthCredentials credentials) {
RxPEPFactory.validateDocumentationApi(pepDocumentationApi);
RxPEPFactory.validateMethodReturnType(pepDocumentationApi);
final Collection allModifierMethods = getAllModifierActor(pepDocumentationApi);
try {
allModifierMethods.forEach((modifierActor -> decisionEnforcer.addModificationMethod(modifierActor)));
} catch (final Exception e) {
LOG.error("Error while adding modifiers");
throw new IncorrectPEPDescriptionError("Unknown PEP creation error happened!" + e.getMessage());
}
try {
final PolicyEnforcementPoint policyEnforcementPoint = new PolicyEnforcementPoint(decisionEnforcer, getComponentId(pepDocumentationApi), pmpUri, discoverPEPDocumentationApi(pepDocumentationApi),
discoverModifierInterfaceDescription(pepDocumentationApi), aliveCheckUri, false, credentials);
return new ReactivePEP<>(pepDocumentationApi, policyEnforcementPoint);
} catch (final IOException io) {
LOG.error("IOException must not happen!");
} catch (final ClassNotFoundException e) {
LOG.error("Reactive PEP is not created: documentation API not found");
throw new IncorrectPEPDescriptionError("PEP not created because of some classloading issue", e);
}
//
throw new IncorrectPEPDescriptionError("Unknown PEP creation error happened!");
}
/**
* Creates a new ReactivePEP object.
*
* @param the generic type
* @param pepDocumentationApi the pep documentation api
* @param pmpUri the pmp uri
* @param aliveCheckUri the alive check uri
* @param oauthClientCredentials the oauth client credentials
* @return the rx PEP of type T
*/
public static ReactivePEP createRxPEP(final Class pepDocumentationApi, URI pmpUri, URI aliveCheckUri, OAuthCredentials oauthClientCredentials) {
final JsonPathDecisionEnforcer jvmNativeDecisionEnforcer = new JsonPathDecisionEnforcer();
return createRxPEP(pepDocumentationApi, pmpUri, jvmNativeDecisionEnforcer, aliveCheckUri, oauthClientCredentials);
}
/**
* Creates an input parameter description.
*
* @param parameterType the parameter type
* @param annotations the annotations
* @return the input parameter description
*/
private static InputParameterDescription createInputParameterDescription(Class parameterType, Annotation[] annotations) {
final ActionParameterDescription annotation = getParameterDescriptionAnnotation(annotations);
if (annotation != null) {
final String parameterName = annotation.name();
final String description = annotation.description();
parameterType = annotation.type().equals(Void.class) ? parameterType : annotation.type();
return new InputParameterDescription(parameterName, description, annotation.pattern(), annotation.mandatory(), parameterType);
}
// TODO don't check validity of the parameter annotation. there is no
// constraints regarding it.
return null;
}
/**
* Discover modifier interface description.
*
* @param the generic type
* @param tClass the t class
* @return the list
*/
private static final List discoverModifierInterfaceDescription(final Class tClass) {
final List modifierInterfaceDescriptions = new LinkedList<>();
final Annotation requiredModifier[] = tClass.getAnnotationsByType(ProvidedModifiers.class);
if (requiredModifier.length > 0) {
// get all modifier required for the event
final Class extends ModifierMethod> modifierClasses[] = ((ProvidedModifiers)requiredModifier[0]).className();
for (final Class modifierClass : modifierClasses) {
final Method modifierMethods[] = modifierClass.getDeclaredMethods();
for (final Method modifierMethod : modifierMethods) {
// if method is annotated with ActionDescription then read action
// name and description
if (modifierMethod.isAnnotationPresent(ActionDescription.class)) {
// get the ActionDescription of the method
final ActionDescription actionDescription = modifierMethod.getAnnotation(ActionDescription.class);
// take the method name is name is not specified with
// ActionDescription
final String modifierName = readName(modifierMethod, actionDescription);
// read modifier successfully so there is no problem
final List modifierInputParameter = readModifierParameter(modifierMethod);
// get the return type of the method
final Class> returnType = actionDescription.pepSupportedType();
// read the description
final String modifierDescription = actionDescription.description();
// add the PepInterfaceDescription to interfaceDescriptions
modifierInterfaceDescriptions.add(new ModifierInterfaceDescription(modifierName, returnType, modifierDescription, returnType.getTypeName(), modifierInputParameter));
}
}
}
}
return modifierInterfaceDescriptions;
}
/**
* Discovers PEP interface description.
*
* @param the generic type
* @param pepDocumentationApi the pep documentation API
* @return the list of interface description
*/
private static final List discoverPEPDocumentationApi(final Class pepDocumentationApi) throws ClassNotFoundException {
// get all the method of the API documentation interface
final Method methods[] = pepDocumentationApi.getDeclaredMethods();
// list of PEP interface description
final List interfaceDescriptions = new LinkedList<>();
// iterate over all methods
for (final Method method : methods) {
// if method is
final Annotation eventDescriptionAnnotations[] = method.getAnnotationsByType(EventSpecification.class);
if (eventDescriptionAnnotations.length == 0) {
continue;
}
final EventSpecification eventDescriptionForMethod = (EventSpecification)eventDescriptionAnnotations[0];
// create the action id for the event
final ActionId actionId = new ActionId(eventDescriptionForMethod.scope(), eventDescriptionForMethod.action());
// get all the modifier required for event
final PepInterfaceDescription pepInterfaceDescription = new PepInterfaceDescription(actionId, true, eventDescriptionForMethod.description());
pepInterfaceDescription.setEventParameterDescription(readEventParameterDetails(method));
interfaceDescriptions.add(pepInterfaceDescription);
}
return interfaceDescriptions;
}
/**
* Filters annotations of type given annotation class.
*
* @param the generic type
* @param annotations the annotations
* @param annotationClass the annotation class
* @return the annotation of type given annotation class or it returns NULL if
* there is non.
*/
private static T filter(Annotation[] annotations, Class annotationClass) {
for (final Annotation annotation : annotations) {
if (annotationClass.isInstance(annotation)) {
return (T)annotation;
}
}
return null;
}
/**
* This method determines whether the documentation API is Valid or not
*
* @param pepDocumentationApi interface of documentation API
* @param the generic type
* @return PEPType
*/
public static PEPType findAPIDocumentationType(final Class pepDocumentationApi) {
if (isValidDocumentation(pepDocumentationApi).getKey()) {
return PEPType.REACTIVE;
}
return PEPType.INVALID;
}
/**
* Gets all modifier actors.
*
* @param pepDocumentationApi the PEP documentation API
* @param the generic type
* @return a collection of modifier actors
*/
private static final Collection getAllModifierActor(final Class pepDocumentationApi) {
// TODO test it
final Map toReturn = readModifierNewInstanceFromApiDocumentation(pepDocumentationApi);
final Annotation requiredModifier[] = pepDocumentationApi.getAnnotationsByType(ProvidedModifiers.class);
if (requiredModifier.length > 0) {
final Class extends ModifierMethod> modifierActors[] = ((ProvidedModifiers)requiredModifier[0]).className();
for (final Class extends ModifierMethod> modifierActor : modifierActors) {
if (!toReturn.containsKey(modifierActor.getCanonicalName())) {
try {
toReturn.put(modifierActor.getCanonicalName(), modifierActor.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new IncorrectPEPDescriptionError(modifierActor.getCanonicalName() + "does not have default contractor nor supplied with static method in PEP API documentation interface");
}
}
}
}
return toReturn.values();
}
/**
* Gets the component ID from the PEPServiceDescription annotation included in
* the given PEP documentation API.
*
* @param the generic type
* @param pepDocumentationApi the PEP documentation API
* @return the component ID
*/
private static final ComponentId getComponentId(final Class pepDocumentationApi) {
final Annotation classAnnotations[] = pepDocumentationApi.getAnnotationsByType(PEPServiceDescription.class);
final String componentIdStr = ((PEPServiceDescription)classAnnotations[0]).componentId();
final ComponentId componentId = new ComponentId(componentIdStr);
return componentId;
}
/**
* Gets the parameter description annotation.
*
* @param annotations the annotations
* @return the parameter description annotation
*/
private static ActionParameterDescription getParameterDescriptionAnnotation(Annotation[] annotations) {
for (final Annotation annotation : annotations) {
if (annotation instanceof ActionParameterDescription) {
return (ActionParameterDescription)annotation;
}
}
return null;
}
/***
* Checks whether documentation API is valid.
*
* @param the generic type
* @param pepDocumentationApi documentation API interface
* @return if correct or incorrect and reason as exception
*/
public static Pair isValidDocumentation(final Class pepDocumentationApi) {
final Method pepDocumentationApiMethods[] = pepDocumentationApi.getDeclaredMethods();
for (final Method pepDocumentationApiMethod : pepDocumentationApiMethods) {
if (Modifier.isStatic(pepDocumentationApiMethod.getModifiers())) {
continue;
}
final Type returnType = pepDocumentationApiMethod.getGenericReturnType();
if (pepDocumentationApiMethod.getReturnType() == Observable.class && returnType instanceof ParameterizedType && pepDocumentationApiMethod.isAnnotationPresent(EventSpecification.class)) {
final Type observableType[] = ((ParameterizedType)returnType).getActualTypeArguments();
if (observableType[0] == Event.class) {
continue;
} else if (observableType[0] instanceof ParameterizedType && ((ParameterizedType)observableType[0]).getRawType() == Pair.class
&& ((ParameterizedType)observableType[0]).getActualTypeArguments().length == 2 && ((ParameterizedType)observableType[0]).getActualTypeArguments()[0] == Event.class
&& ((ParameterizedType)observableType[0]).getActualTypeArguments()[1] == AuthorizationDecision.class) {
continue;
} else {
return new ImmutablePair(false, new IncorrectPEPDescriptionError("Observable only can have Event or Pair"));
}
} else {
return new ImmutablePair(false, new IncorrectPEPDescriptionError("PEP can't have void/anything else method, All methods should return ModifierMethod or Observable"));
}
}
return new ImmutablePair(true, null);
}
/**
* Filters parameters of type PEPParamKey from the given method and creates
* the input parameter descriptions.
*
* @param method the modifier
* @return the list of input parameter descriptions
*/
private static List readEventParameterDetails(Method method) throws ClassNotFoundException {
final List inputParameterDescriptions = new LinkedList<>();
final Parameter parameters[] = method.getParameters();
final Annotation annotation[][] = method.getParameterAnnotations();
for (int i = 0; i < annotation.length; i++) {
final EventParameter pepParamKey = filter(annotation[i], EventParameter.class);
if (pepParamKey != null) {
inputParameterDescriptions.add(new InputParameterDescription(pepParamKey.name(), pepParamKey.description(), null, true, parameters[i].getParameterizedType(),
JsonSchemaGenerator.getJsonType(parameters[i].getType()), JsonSchemaGenerator.createTypeDescription(parameters[i].getParameterizedType(), parameters[i].getType())));
}
}
return inputParameterDescriptions;
}
/**
* Gets modifier actors with their corresponding names from documentation API.
*
* @param the generic type
* @param pepDocumentationApi the PEP documentation API
* @return a map of modifier actors and their corresponding names
*/
private static final Map readModifierNewInstanceFromApiDocumentation(final Class pepDocumentationApi) {
final Method allMethod[] = pepDocumentationApi.getDeclaredMethods();
final Map toReturn = new HashMap<>();
for (final Method method : allMethod) {
if (Modifier.isStatic(method.getModifiers()) && ModifierMethod.class.isAssignableFrom(method.getReturnType())) {
try {
final ModifierMethod modifierActor = (ModifierMethod)method.invoke(null);
toReturn.put(method.getReturnType().getCanonicalName(), modifierActor);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IncorrectPEPDescriptionError("method to supply ModifierActor should not have any arguments.");
}
}
}
return toReturn;
}
/**
* Reads method parameters.
*
* @param method the modifier actor
* @return the list of input parameter description
*/
private static List readModifierParameter(Method method) {
final Class>[] parameters = method.getParameterTypes();
final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
final List toReturn = new ArrayList<>(parameters.length);
for (int i = 0; i < parameters.length; i++) {
final InputParameterDescription inputParameterDescription = createInputParameterDescription(parameters[i], parameterAnnotations[i]);
if (inputParameterDescription != null) {
toReturn.add(inputParameterDescription);
}
}
return toReturn;
}
/**
* Returns method name.
*
* @param method the modifier actor
* @param actionDescription the action description
* @return method name
*/
private static String readName(Method method, ActionDescription actionDescription) {
return StringUtils.isNotBlank(actionDescription.methodName()) ? actionDescription.methodName() : method.getName();
}
/**
* Validates that the PEP documentation API is an interface and does not
* extend other interfaces.
*
* @param the generic type
* @param pepDocumentationApi the PEP documentation API
*/
private static void validateDocumentationApi(final Class pepDocumentationApi) {
if (!pepDocumentationApi.isInterface()) {
throw new IllegalArgumentException("Reactive PEP API documentation must be interfaces.");
}
if (pepDocumentationApi.getInterfaces().length > 0) {
throw new IllegalArgumentException("Reactive PEP API documentation interfaces must not extend other interfaces.");
}
}
/**
* Validates method return type for reactive PEP.
*
* Enforcement method must have observable event or observable pair of event
* and authorization decision to get the response from PDP to use ReactivePEP
*
* @param the generic type
* @param pepDocumentationApi the pep documentation api
*/
private static void validateMethodReturnType(final Class pepDocumentationApi) {
final Pair booleanRuntimeExceptionPair = isValidDocumentation(pepDocumentationApi);
if (!booleanRuntimeExceptionPair.getKey()) {
throw booleanRuntimeExceptionPair.getValue();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy