summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas <>2022-11-29 09:07:30 +0100
committerThomas <>2022-11-29 09:07:30 +0100
commit20f485434680151111cf7cc7eaf33ca3b92221cb (patch)
tree0bb3460ecdf55324ff2c4c7dc8a84325c6f716ee
parent49d02d8c7c19ab9bb763d3f7fe862273706cc73a (diff)
downloadEAAF-Components-20f485434680151111cf7cc7eaf33ca3b92221cb.tar.gz
EAAF-Components-20f485434680151111cf7cc7eaf33ca3b92221cb.tar.bz2
EAAF-Components-20f485434680151111cf7cc7eaf33ca3b92221cb.zip
feat(core): add ticket-based error-handling service as EAAF core functionality
-rw-r--r--eaaf_core/pom.xml27
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/data/ErrorConfig.java65
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/TicketErrorService.java234
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultJsonMapper.java106
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultYamlMapper.java38
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyAuthConfig.java100
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyPendingRequest.java10
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/JunitTicketErrorService.java21
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/TicketErrorServiceTest.java272
-rw-r--r--eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml25
-rw-r--r--eaaf_core/src/test/resources/config/error_conf.yaml140
-rw-r--r--pom.xml29
12 files changed, 1066 insertions, 1 deletions
diff --git a/eaaf_core/pom.xml b/eaaf_core/pom.xml
index d74bd9c2..89041b00 100644
--- a/eaaf_core/pom.xml
+++ b/eaaf_core/pom.xml
@@ -41,6 +41,28 @@
<artifactId>spring-webmvc</artifactId>
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jdk8</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jsr310</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-parameter-names</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -98,6 +120,11 @@
<!-- For testing -->
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/data/ErrorConfig.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/data/ErrorConfig.java
new file mode 100644
index 00000000..b9cacb1c
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/data/ErrorConfig.java
@@ -0,0 +1,65 @@
+package at.gv.egiz.eaaf.core.impl.data;
+
+import java.util.List;
+
+import at.gv.egiz.eaaf.core.impl.idp.auth.services.IErrorService.ActionType;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+
+@ToString
+@Builder
+@Slf4j
+@NoArgsConstructor
+@AllArgsConstructor
+public class ErrorConfig {
+
+ @Getter
+ @Setter
+ private String action;
+
+ @Getter
+ @Setter
+ private String externalCode;
+
+ @Getter
+ @Setter
+ private String logLevel;
+
+
+ @Getter
+ @Setter
+ private List<String> internalCode;
+
+ @Getter
+ @Setter
+ private Boolean defaultConfig;
+
+ @Getter
+ @Setter
+ @Builder.Default
+ private Boolean useInternalAsExternal = false;
+
+ /**
+ * Get type of error-handling flow.
+ *
+ * @return flow type
+ */
+ public ActionType getActionType() {
+ ActionType actionType = ActionType.fromString(action);
+ if (actionType == null) {
+ log.warn("Find unsupported Error-Handling-Flow: {}. Use: {} as backup",
+ action, ActionType.TICKET);
+ actionType = ActionType.TICKET;
+
+ }
+
+ return actionType;
+
+ }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/TicketErrorService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/TicketErrorService.java
new file mode 100644
index 00000000..557614e6
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/TicketErrorService.java
@@ -0,0 +1,234 @@
+package at.gv.egiz.eaaf.core.impl.idp.auth.services;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.PostConstruct;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+import at.gv.egiz.eaaf.core.api.IStatusMessenger;
+import at.gv.egiz.eaaf.core.api.data.EaafConstants;
+import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.data.ErrorConfig;
+import at.gv.egiz.eaaf.core.impl.gui.AbstractGuiFormBuilderConfiguration;
+import at.gv.egiz.eaaf.core.impl.idp.controller.ProtocolFinalizationController;
+import at.gv.egiz.eaaf.core.impl.utils.DefaultYamlMapper;
+import at.gv.egiz.eaaf.core.impl.utils.FileUtils;
+import at.gv.egiz.eaaf.core.impl.utils.ServletUtils;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.var;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public abstract class TicketErrorService implements IErrorService {
+ private static final String CONFIG_PROP_ERROR_HANDLING_CONFIG_PATH = "core.error.handling.config";
+ private static final String TECH_LOG_MSG = "errorCode={0} Message={1}";
+ private static final String TICKET_LOG_MSG = "Ticket={2} errorCode={0} Message={1}";
+
+ private final List<ErrorConfig> errorConfigList = new ArrayList<>();
+
+
+ @Autowired
+ IConfiguration basicConfig;
+
+ @Autowired
+ ResourceLoader resourceLoader;
+
+ @Autowired
+ IPendingRequestIdGenerationStrategy requestIdValidationStragegy;
+
+ @Override
+ public String getExternalCodeFromInternal(String internalCode) {
+ ErrorConfig errorConfig = findByInternalCode(internalCode);
+ return StringUtils.isNotEmpty(errorConfig.getExternalCode())
+ ? errorConfig.getExternalCode()
+ : errorConfig.getUseInternalAsExternal()
+ ? internalCode
+ : IStatusMessenger.CODES_EXTERNAL_ERROR_GENERIC;
+
+ }
+
+ @Override
+ public IHandleData createHandleData(Throwable throwable, boolean supportRedirctToSp) throws EaafException {
+ String internalErrorId = extractInternalErrorCode(throwable);
+ ErrorConfig errorFlowConfig = findByInternalCode(internalErrorId);
+ ActionType errorHandlingFlow = errorFlowConfig.getActionType();
+
+ return HandleData.builder()
+ .throwable(throwable)
+ .internalErrorCode(internalErrorId)
+ .actionType(errorHandlingFlow)
+ .logLevel(LogLevel.fromString(errorFlowConfig.getLogLevel()))
+ .supportTicket(ActionType.TICKET.equals(errorHandlingFlow) ? generateSupportTicket() : null)
+ .errorIdTokenForRedirect(
+ supportRedirctToSp ? requestIdValidationStragegy.generateExternalPendingRequestId() : null)
+ .build();
+
+ }
+
+ @Override
+ public void displayErrorData(ModifyableGuiBuilderConfiguration c, IErrorService.IHandleData errorData,
+ HttpServletRequest httpReq) throws EaafException {
+ if (!(errorData instanceof TicketErrorService.HandleData)) {
+ throw new EaafException(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC);
+ }
+ var ed = (TicketErrorService.HandleData) errorData;
+
+ // set SupportTicket
+ c.putCustomParameter(AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_TICKET, ed.getSupportTicket());
+
+ // set redirect to SP path
+ if (StringUtils.isNotEmpty(ed.getErrorIdTokenForRedirect())) {
+ c.putCustomParameterWithOutEscaption(
+ AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_REDIRECT,
+ generateRedirect(httpReq, ed.getErrorIdTokenForRedirect()));
+
+ }
+ }
+
+ /**
+ * Generate a application-specific support-ticket.
+ *
+ * @return Support ticket for error screen
+ */
+ protected abstract String generateSupportTicket();
+
+
+ @Nonnull
+ private ErrorConfig findByInternalCode(@Nonnull String seekedInternalCode) {
+ return errorConfigList.stream()
+ .filter(c -> c.getInternalCode() != null && c.getInternalCode().contains(seekedInternalCode))
+ .findFirst()
+ .orElse(
+ errorConfigList.stream()
+ .filter(c -> c.getDefaultConfig() != null && c.getDefaultConfig().equals(true))
+ .findFirst()
+ .orElse(ErrorConfig.builder()
+ .action(ActionType.TICKET.toString())
+ .externalCode(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC)
+ .logLevel("ERROR")
+ .defaultConfig(true)
+ .build())
+ );
+
+ }
+
+ private String extractInternalErrorCode(Throwable throwable) {
+ Throwable originalException;
+ if (throwable instanceof TaskExecutionException
+ && ((TaskExecutionException) throwable).getOriginalException() != null) {
+ originalException = ((TaskExecutionException) throwable).getOriginalException();
+
+ } else {
+ originalException = throwable;
+
+ }
+
+ if (!(originalException instanceof EaafException)) {
+ return IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC;
+
+ } else {
+ return ((EaafException) originalException).getErrorId();
+
+ }
+ }
+
+ private String generateRedirect(HttpServletRequest httpReq, String errorTokenId) {
+ String redirectUrl = ServletUtils.getBaseUrl(httpReq);
+ redirectUrl += ProtocolFinalizationController.ENDPOINT_ERROR_REDIRECT + "?"
+ + EaafConstants.PARAM_HTTP_ERROR_CODE + "=" + StringEscapeUtils
+ .escapeHtml4(errorTokenId);
+ return redirectUrl;
+
+ }
+
+ @PostConstruct
+ private void initialize() throws EaafException {
+ final String errorConfPath = basicConfig.getBasicConfiguration(CONFIG_PROP_ERROR_HANDLING_CONFIG_PATH);
+ log.info("Initializing error-handling service from configuration: {}", errorConfPath);
+
+ if (StringUtils.isEmpty(errorConfPath)) {
+ log.error("Error: Path to error handling config is not known");
+ throw new EaafException("internal.configuration.00", new Object[]{CONFIG_PROP_ERROR_HANDLING_CONFIG_PATH});
+ }
+
+ try {
+ final byte[] raw = readFromFile(errorConfPath);
+ ObjectMapper mapper = DefaultYamlMapper.getYamlMapper();
+ final TypeFactory typeFactory = mapper.getTypeFactory();
+ final CollectionType javaType = typeFactory.constructCollectionType(List.class, ErrorConfig.class);
+ errorConfigList.addAll(mapper.readValue(raw, javaType));
+
+ log.info("Found #{} configuration-elements for Error Handling", errorConfigList.size());
+
+ } catch (Exception e) {
+ log.error("Error reading Configurations file", e);
+ throw new EaafException("internal.configuration.01",
+ new Object[]{CONFIG_PROP_ERROR_HANDLING_CONFIG_PATH, "Error reading Configurations file"});
+ }
+
+ }
+
+ private byte[] readFromFile(final String filePath) throws URISyntaxException, IOException {
+ final String fullFilePath = FileUtils.makeAbsoluteUrl(filePath, basicConfig.getConfigurationRootDirectory());
+ final Resource ressource = resourceLoader.getResource(fullFilePath);
+ final InputStream is = ressource.getInputStream();
+ final byte[] result = IOUtils.toByteArray(is);
+ is.close();
+ return result;
+ }
+
+ @Builder
+ static class HandleData implements IHandleData {
+
+ @Getter
+ private String errorIdTokenForRedirect;
+
+ @Getter
+ private String supportTicket;
+
+ @Getter
+ private final Throwable throwable;
+
+ @Getter
+ private String internalErrorCode;
+
+ @Getter
+ private ActionType actionType;
+
+ @Getter
+ private LogLevel logLevel;
+
+ public String getPreFormatedErrorMessage() {
+ if (supportTicket != null) {
+ return MessageFormat.format(TICKET_LOG_MSG, internalErrorCode, throwable.getMessage(), supportTicket);
+
+ } else {
+ return MessageFormat.format(TECH_LOG_MSG, internalErrorCode, throwable.getMessage());
+
+ }
+ }
+ }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultJsonMapper.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultJsonMapper.java
new file mode 100644
index 00000000..8303e860
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultJsonMapper.java
@@ -0,0 +1,106 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import at.gv.egiz.eaaf.core.exceptions.EaafJsonMapperException;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Holder for Jackson JSON Mapper that sets some security features.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public final class DefaultJsonMapper {
+
+ @Getter
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
+ static {
+ // initialize JSON Mapper
+ jsonMapper.configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, true);
+ jsonMapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true);
+ jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
+ jsonMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
+ jsonMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
+ jsonMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
+
+ jsonMapper.registerModule(new JavaTimeModule());
+ jsonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+
+ }
+
+ /**
+ * private constructor for class with only static methods.
+ */
+ private DefaultJsonMapper() {
+
+ }
+
+ /**
+ * Serialize an object to a JSON string.
+ *
+ * @param value the object to serialize
+ * @return a JSON string
+ * @throws JsonProcessingException thrown when an error occurs during
+ * serialization
+ */
+ public static String serialize(final Object value) throws EaafJsonMapperException {
+ try {
+ return jsonMapper.writeValueAsString(value);
+
+ } catch (final JsonProcessingException e) {
+ log.warn("JSON mapping FAILED with error: {}", e.getMessage());
+ throw new EaafJsonMapperException(e.getMessage(), e);
+
+ }
+ }
+
+ /**
+ * Deserialize a JSON string.
+ *
+ * @param value the JSON to deserialize as {@link String}
+ * @param clazz optional parameter that determines the type of the returned
+ * object. If not set, an {@link Object} is returned.
+ * @param <T> Response class type
+ * @return the deserialized JSON string as an object of type {@code clazz} or
+ * {@link Object}
+ * @throws JsonMappingException if the input JSON structure does not match
+ * structure expected for result type
+ */
+ public static <T> Object deserialize(final String value, final Class<T> clazz)
+ throws EaafJsonMapperException {
+ try {
+ if (clazz != null) {
+ if (clazz.isAssignableFrom(TypeReference.class)) {
+ return jsonMapper.readValue(value, clazz);
+ } else {
+ final JavaType javaType = TypeFactory.defaultInstance().constructType(clazz);
+ return jsonMapper.readValue(value, javaType);
+ }
+ } else {
+ return jsonMapper.readValue(value, Object.class);
+ }
+
+ } catch (final IOException e) {
+ log.warn("JSON mapping FAILED with error: {}", e.getMessage());
+ throw new EaafJsonMapperException(e.getMessage(), e);
+
+ }
+
+ }
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultYamlMapper.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultYamlMapper.java
new file mode 100644
index 00000000..6e8c4540
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DefaultYamlMapper.java
@@ -0,0 +1,38 @@
+package at.gv.egiz.eaaf.core.impl.utils;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import lombok.Getter;
+
+/**
+ * Holder for Jackson JSON Mapper that sets some security features.
+ *
+ * @author tlenz
+ *
+ */
+public final class DefaultYamlMapper {
+
+ @Getter
+ private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
+
+ static {
+ // initialize JSON Mapper
+ yamlMapper.configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, true);
+ yamlMapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true);
+ yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
+ yamlMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
+ yamlMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
+ yamlMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
+
+ }
+
+ /**
+ * private constructor for class with only static methods.
+ */
+ private DefaultYamlMapper() {
+
+ }
+}
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyAuthConfig.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyAuthConfig.java
new file mode 100644
index 00000000..03f5b759
--- /dev/null
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyAuthConfig.java
@@ -0,0 +1,100 @@
+package at.gv.egiz.eaaf.core.impl.idp.auth.dummy;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.commons.lang3.StringUtils;
+
+import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP;
+import at.gv.egiz.eaaf.core.api.idp.IExtendedConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.idp.conf.AbstractSpringBootConfigurationImpl;
+
+
+public class DummyAuthConfig extends AbstractSpringBootConfigurationImpl
+ implements IConfigurationWithSP, IExtendedConfiguration {
+
+ private ISpConfiguration spconfig = null;
+
+ private String configPropPrefix = StringUtils.EMPTY;
+
+ private boolean useConfigRootDirFromConfig = false;
+
+ private URI internalConfigRootDirHolder = null;
+
+ public void setSpConfig(final ISpConfiguration spConfig) {
+ this.spconfig = spConfig;
+
+ }
+
+
+
+ @Override
+ public URI getConfigurationRootDirectory() {
+ if (useConfigRootDirFromConfig) {
+ if (internalConfigRootDirHolder == null) {
+ try {
+ internalConfigRootDirHolder = new URI(getBasicConfiguration("core.configRootDir"));
+ } catch (final URISyntaxException e) {
+ e.printStackTrace();
+
+ }
+ }
+ return internalConfigRootDirHolder;
+
+ } else {
+ return new java.io.File(".").toURI();
+
+ }
+ }
+
+ @Override
+ public ISpConfiguration getServiceProviderConfiguration(final String arg0)
+ throws EaafConfigurationException {
+ return spconfig;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getServiceProviderConfiguration(final String arg0, final Class<T> arg1)
+ throws EaafConfigurationException {
+ return (T) spconfig;
+
+ }
+
+ @Override
+ public String validateIdpUrl(final URL arg0) throws EaafException {
+ return arg0.toString();
+ }
+
+
+ @Override
+ protected String getBackupConfigPath() {
+ return null;
+ }
+
+
+ @Override
+ public String getApplicationSpecificKeyPrefix() {
+ return configPropPrefix;
+
+ }
+
+
+
+ public void setConfigPropPrefix(final String configPropPrefix) {
+ this.configPropPrefix = configPropPrefix;
+ }
+
+
+
+ public void setUseConfigRootDirFromConfig(boolean useConfigRootDirFromConfig) {
+ this.useConfigRootDirFromConfig = useConfigRootDirFromConfig;
+ }
+
+
+
+}
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyPendingRequest.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyPendingRequest.java
new file mode 100644
index 00000000..1a18ada2
--- /dev/null
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyPendingRequest.java
@@ -0,0 +1,10 @@
+package at.gv.egiz.eaaf.core.impl.idp.auth.dummy;
+
+import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl;
+
+public class DummyPendingRequest extends RequestImpl {
+
+
+ private static final long serialVersionUID = 8136280395622411505L;
+
+}
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/JunitTicketErrorService.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/JunitTicketErrorService.java
new file mode 100644
index 00000000..a8ccc54a
--- /dev/null
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/JunitTicketErrorService.java
@@ -0,0 +1,21 @@
+package at.gv.egiz.eaaf.core.impl.idp.auth.service;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+import at.gv.egiz.eaaf.core.impl.idp.auth.services.TicketErrorService;
+
+/**
+ * Ticket based error-handling service for jUnit tests.
+ *
+ * @author tlenz
+ *
+ */
+public class JunitTicketErrorService extends TicketErrorService {
+
+ @Override
+ protected String generateSupportTicket() {
+ return RandomStringUtils.randomAlphabetic(10);
+
+ }
+
+}
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/TicketErrorServiceTest.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/TicketErrorServiceTest.java
new file mode 100644
index 00000000..2c89e49f
--- /dev/null
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/TicketErrorServiceTest.java
@@ -0,0 +1,272 @@
+package at.gv.egiz.eaaf.core.impl.idp.auth.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.dummy.DummyPendingRequest;
+import at.gv.egiz.eaaf.core.impl.idp.auth.services.IErrorService;
+import at.gv.egiz.eaaf.core.impl.idp.auth.services.IErrorService.LogLevel;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import lombok.val;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("/SpringTest-authcommon-errorService.xml")
+@TestPropertySource(
+ properties = {"core.error.handling.config=src/test/resources/config/error_conf.yaml"})
+public class TicketErrorServiceTest {
+ @Autowired
+ private IErrorService ticketErrorService;
+
+ @Mock
+ ch.qos.logback.core.AppenderBase<ch.qos.logback.classic.spi.ILoggingEvent> appender;
+ @Captor
+ ArgumentCaptor<ILoggingEvent> logCaptor;
+
+
+ /**
+ * Setup jUnit test.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.openMocks(this);
+
+ }
+
+ @Test
+ public void cofigurationLodingAndExternalCodeMapping() {
+ String external = ticketErrorService.getExternalCodeFromInternal("auth.01");
+
+ Assert.assertNotNull(external);
+ Assert.assertEquals(external, "9199");
+ }
+
+ @Test
+ public void coverDifferentExceptions() throws EaafException {
+
+ val pendingReq = new DummyPendingRequest();
+ pendingReq.setPendingRequestId("324");
+
+ val errorData = ticketErrorService.createHandleData(new Exception("sl20.07.02"), true);
+ val errorData1 = ticketErrorService.createHandleData(
+ new EaafException("sl20.07.02", new Object[]{"dummy"}), false);
+ val errorData2 = ticketErrorService.createHandleData(
+ new TaskExecutionException(pendingReq, "dummy", new EaafException("auth.00", new Object[]{"dummy"})), false);
+ val errorData3 = ticketErrorService.createHandleData(new EaafException("auth.21", null), false);
+ val errorData4 = ticketErrorService.createHandleData(new EaafException("internal.pendingreqid.01", null), false);
+ val errorData5 = ticketErrorService.createHandleData(new EaafException("junit.01", null), false);
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(errorData.getActionType(), IErrorService.ActionType.TICKET);
+ Assert.assertEquals(errorData.getInternalErrorCode(), "internal.00");
+ Assert.assertEquals(errorData.getLogLevel(), IErrorService.LogLevel.ERROR);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData.getInternalErrorCode()), "9199");
+ Assert.assertNotNull(errorData.getErrorIdTokenForRedirect());
+
+ Assert.assertNotNull(errorData1);
+ Assert.assertEquals(errorData1.getActionType(), IErrorService.ActionType.NO_TICKET);
+ Assert.assertEquals(errorData1.getInternalErrorCode(), "sl20.07.02");
+ Assert.assertEquals(errorData1.getLogLevel(), IErrorService.LogLevel.WARN);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData1.getInternalErrorCode()), "1003");
+ Assert.assertNull(errorData1.getErrorIdTokenForRedirect());
+
+ Assert.assertNotNull(errorData2);
+ Assert.assertEquals(errorData2.getActionType(), IErrorService.ActionType.TICKET);
+ Assert.assertEquals(errorData2.getInternalErrorCode(), "auth.00");
+ Assert.assertEquals(errorData2.getLogLevel(), IErrorService.LogLevel.ERROR);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData2.getInternalErrorCode()), "9199");
+
+ Assert.assertNotNull(errorData3);
+ Assert.assertEquals(errorData3.getActionType(), IErrorService.ActionType.NO_TICKET);
+ Assert.assertEquals(errorData3.getInternalErrorCode(), "auth.21");
+ Assert.assertEquals(errorData3.getLogLevel(), IErrorService.LogLevel.INFO);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData3.getInternalErrorCode()), "1005");
+
+ Assert.assertNotNull(errorData4);
+ Assert.assertEquals(errorData4.getActionType(), IErrorService.ActionType.TICKET);
+ Assert.assertEquals(errorData4.getInternalErrorCode(), "internal.pendingreqid.01");
+ Assert.assertEquals(errorData4.getLogLevel(), IErrorService.LogLevel.WARN);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData4.getInternalErrorCode()),
+ "9199");
+
+ Assert.assertNotNull(errorData5);
+ Assert.assertEquals(errorData5.getActionType(), IErrorService.ActionType.ERRORPAGE);
+ Assert.assertEquals(errorData5.getInternalErrorCode(), "junit.01");
+ Assert.assertEquals(errorData5.getLogLevel(), IErrorService.LogLevel.ERROR);
+ Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData5.getInternalErrorCode()),
+ "junit.01");
+
+ }
+
+ @Test
+ public void handleDataTicketNoRedirect() {
+ val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core");
+ logger.addAppender(appender);
+
+ IErrorService.IHandleData errorData = null;
+ try {
+ errorData = ticketErrorService.createHandleData(new EaafException("auth.00", new Object[]{"dummy"}), false);
+ } catch (EaafException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(errorData.getActionType(), IErrorService.ActionType.TICKET);
+ // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO
+ // wrong excepiton
+ Assert.assertEquals(errorData.getInternalErrorCode(), "auth.00");
+
+ assertEquals("wrong errorLevel", LogLevel.ERROR, errorData.getLogLevel());
+ assertTrue("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket="));
+
+ }
+
+ @Test
+ public void handleDataNoTicketNoRedirect() {
+ val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core");
+ logger.addAppender(appender);
+
+
+ IErrorService.IHandleData errorData = null;
+ try {
+ errorData = ticketErrorService.createHandleData(
+ new EaafException("internal.pendingreqid.00", new Object[]{"dummy"}), false);
+ } catch (EaafException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(errorData.getActionType(), IErrorService.ActionType.NO_TICKET);
+ // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO
+ // wrong excepiton
+ Assert.assertEquals(errorData.getInternalErrorCode(), "internal.pendingreqid.00");
+
+ assertEquals("wrong errorLevel", LogLevel.WARN, errorData.getLogLevel());
+ assertFalse("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket="));
+ }
+
+ @Test
+ public void handleDataTicketRedirect() {
+ val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core");
+ logger.addAppender(appender);
+
+ IErrorService.IHandleData errorData = null;
+ try {
+ errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[]{"dummy"}), true);
+ } catch (EaafException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(IErrorService.ActionType.TICKET, errorData.getActionType());
+ // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO
+ // wrong excepiton
+ Assert.assertEquals(errorData.getInternalErrorCode(), "auth.01");
+
+ assertEquals("wrong errorLevel", LogLevel.ERROR, errorData.getLogLevel());
+ assertTrue("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket="));
+ }
+
+ @Test
+ public void handleDataNoTicketRedirect() {
+ val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core");
+ logger.addAppender(appender);
+
+ IErrorService.IHandleData errorData = null;
+ try {
+ errorData = ticketErrorService
+ .createHandleData(new EaafException("module.binding.14", new Object[]{"dummy"}), false);
+ } catch (EaafException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(IErrorService.ActionType.NO_TICKET, errorData.getActionType());
+ // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO
+ // wrong excepiton
+ Assert.assertEquals(errorData.getInternalErrorCode(), "module.binding.14");
+
+ assertEquals("wrong errorLevel", LogLevel.WARN, errorData.getLogLevel());
+ assertFalse("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket="));
+ }
+
+ @Test
+ public void handleDataNoTicketAutoRedirect() {
+ val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core");
+ logger.addAppender(appender);
+
+ IErrorService.IHandleData errorData = null;
+ try {
+ errorData = ticketErrorService.createHandleData(new EaafException("auth.21", new Object[]{"dummy"}), true);
+ } catch (EaafException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotNull(errorData);
+ Assert.assertEquals(IErrorService.ActionType.NO_TICKET, errorData.getActionType());
+ // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO
+ // wrong excepiton
+ Assert.assertEquals(errorData.getInternalErrorCode(), "auth.21");
+
+ assertEquals("wrong errorLevel", LogLevel.INFO, errorData.getLogLevel());
+ assertFalse("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket="));
+ }
+
+ @Test
+ public void testErrorDataDisplay() throws EaafException {
+
+ IErrorService.IHandleData errorData = null;
+ errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[]{"dummy"}), true);
+ Assert.assertNotNull(errorData);
+
+ val guiBuilder = Mockito.mock(ModifyableGuiBuilderConfiguration.class);
+ ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);
+ doNothing().when(guiBuilder).putCustomParameter(any(), any(), valueCapture.capture());
+
+ ArgumentCaptor<String> valueCapture2 = ArgumentCaptor.forClass(String.class);
+ doNothing().when(guiBuilder).putCustomParameterWithOutEscaption(any(), any(), valueCapture2.capture());
+
+ ticketErrorService.displayErrorData(guiBuilder, errorData, getMocReq());
+ Assert.assertTrue("Incorrect Ticketvalue",
+ Pattern.matches("[A-Za-z0-9]{10}", valueCapture.getAllValues().get(0)));
+ Assert.assertEquals(
+ "http://iaik.dummy/blub/public/secure/errorRedirect?errorid=" + errorData.getErrorIdTokenForRedirect(),
+ valueCapture2.getAllValues().get(0));
+ }
+
+ private HttpServletRequest getMocReq() {
+ val req = Mockito.mock(HttpServletRequest.class);
+ when(req.getServerPort()).thenReturn(80);
+ when(req.getScheme()).thenReturn("http");
+ when(req.getServerName()).thenReturn("iaik.dummy");
+ when(req.getContextPath()).thenReturn("/blub");
+ return req;
+
+ }
+
+}
diff --git a/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml b/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml
new file mode 100644
index 00000000..35bd9d6f
--- /dev/null
+++ b/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:tx="http://www.springframework.org/schema/tx"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
+
+ <bean id="DummyAuthConfig"
+ class="at.gv.egiz.eaaf.core.impl.idp.auth.dummy.DummyAuthConfig" />
+
+ <bean id="errorTicketService"
+ class="at.gv.egiz.eaaf.core.impl.idp.auth.service.JunitTicketErrorService">
+ </bean>
+
+ <bean id="junitStatusMessanger"
+ class="at.gv.egiz.eaaf.core.impl.logging.JUnitTestStatusMessenger" />
+
+ <bean id="simplePendingReqStrategy"
+ class="at.gv.egiz.eaaf.core.impl.utils.SimplePendingRequestIdGenerationStrategy"/>
+
+</beans>
diff --git a/eaaf_core/src/test/resources/config/error_conf.yaml b/eaaf_core/src/test/resources/config/error_conf.yaml
new file mode 100644
index 00000000..0e204e97
--- /dev/null
+++ b/eaaf_core/src/test/resources/config/error_conf.yaml
@@ -0,0 +1,140 @@
+## If no externalCode specified -> 9199 is used
+
+## internal errors with tickets ##
+- action: ticket
+ externalCode: 9199
+ logLevel: ERROR
+ defaultConfig: True
+
+- action: ticket
+ externalCode: 1002
+ logLevel: WARN
+ internalCode:
+ - auth.39
+ - auth.40
+ - auth.41
+ - auth.42
+ - builder.08
+ - builder.11
+ - builder.13
+ - builder.15
+ - builder.16
+ - builder.17
+
+- action: ticket
+ externalCode: 1003
+ logLevel: WARN
+ internalCode:
+ - sl20.07.00
+ - sl20.07.01
+ - sl20.07.04
+ - sl20.07.05
+ - sl20.07.99
+
+- action: ticket
+ externalCode: 1200
+ logLevel: WARN
+ internalCode:
+ - module.binding.16
+ - module.binding.21
+ - module.binding.32
+
+- action: ticket
+ externalCode: 9199
+ logLevel: WARN
+ internalCode:
+ - internal.pendingreqid.01
+ - internal.pendingreqid.03
+ - internal.pendingreqid.04
+ - internal.pendingreqid.05
+ - internal.token.01
+ - internal.token.04
+ - internal.token.08
+ - internal.token.09
+ - internal.token.10
+
+- action: ticket
+ useInternalAsExternal: true
+ logLevel: ERROR
+ internalCode:
+ - config.06
+
+## internal errors without tickets ##
+- action: no_ticket
+ externalCode: 1001
+ logLevel: INFO
+ internalCode:
+ - internal.pendingreqid.06
+ - internal.token.11
+
+- action: no_ticket
+ externalCode: 1003
+ logLevel: WARN
+ internalCode:
+ - sl20.07.02
+ - sl20.07.03
+
+- action: no_ticket
+ externalCode: 1005
+ internalCode:
+ - auth.21
+ logLevel: INFO
+
+- action: no_ticket
+ externalCode: 1201
+ logLevel: WARN
+ internalCode:
+ - module.binding.17
+ - module.binding.19
+ - module.binding.15
+
+- action: no_ticket
+ externalCode: 1200
+ logLevel: info
+ internalCode:
+ - module.binding.33
+ - module.binding.34
+ - module.binding.35
+
+- action: no_ticket
+ externalCode: 9199
+ logLevel: WARN
+ internalCode:
+ - module.binding.14
+ - module.binding.25
+ - module.binding.29
+ - module.binding.30
+ - module.eidasauth.06
+ - module.mis.04
+ - module.mis.05
+ - module.mis.06
+ - module.mis.07
+ - module.mis.08
+ - module.mis.vemo.01
+ - module.mis.vemo.02
+ - module.mis.vemo.03
+ - module.mis.vemo.gui.01
+ - auth.eidbind.02
+ - internal.pendingreqid.00
+ - module.binding.04
+ - module.binding.05
+ - shibboleth.00
+
+- action: no_ticket
+ externalCode: 9199
+ internalCode:
+ - module.binding.31
+ - module.binding.36
+ logLevel: INFO
+
+- action: no_ticket
+ useInternalAsExternal: true
+ logLevel: ERROR
+ internalCode:
+ - auth.28
+ - auth.26
+- action: errorpage
+ useInternalAsExternal: true
+ logLevel: ERROR
+ internalCode:
+ - junit.01 \ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d23eeb3f..5171afdc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,7 @@
<!-- jUnit testing -->
<surefire.version>2.22.2</surefire.version>
<junit-jupiter-api.version>5.8.2</junit-jupiter-api.version>
+ <mockito-junit-jupiter.version>4.9.0</mockito-junit-jupiter.version>
<com.squareup.okhttp3.version>4.9.3</com.squareup.okhttp3.version>
<org.powermock.version>2.0.9</org.powermock.version>
@@ -622,6 +623,26 @@
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.databind.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <version>${com.fasterxml.jackson.core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jdk8</artifactId>
+ <version>${com.fasterxml.jackson.core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jsr310</artifactId>
+ <version>${com.fasterxml.jackson.core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-parameter-names</artifactId>
+ <version>${com.fasterxml.jackson.core.version}</version>
+ </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -693,6 +714,12 @@
<version>${org.powermock.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>${mockito-junit-jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
<dependencies>
@@ -707,7 +734,7 @@
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>${junit-jupiter-api.version}</version>
<scope>test</scope>
- </dependency>
+ </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>