From 88f52463fd0d3d8bf467b75798b525ce86886acc Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Wed, 26 Apr 2023 19:04:57 +0200 Subject: feat(core): add extension-point to central errorHandler service to set application specific information into error-GUI model --- .../idp/auth/services/DefaultErrorService.java | 14 +++++-- .../core/impl/idp/auth/services/IErrorService.java | 27 +++++++++--- .../auth/services/IErrorServiceModelHandler.java | 26 ++++++++++++ .../services/ProtocolAuthenticationService.java | 5 ++- .../impl/idp/auth/services/TicketErrorService.java | 48 +++++++++++++++++++-- .../idp/auth/dummy/DummyDefaultErrorService.java | 10 +++-- .../auth/service/DummyErrorGuiModelHandler.java | 24 +++++++++++ .../idp/auth/service/TicketErrorServiceTest.java | 49 +++++++++++++++------- .../SpringTest-authcommon-errorService.xml | 3 ++ 9 files changed, 176 insertions(+), 30 deletions(-) create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorServiceModelHandler.java create mode 100644 eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/DummyErrorGuiModelHandler.java (limited to 'eaaf_core') diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/DefaultErrorService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/DefaultErrorService.java index e41905a6..cd89f8a5 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/DefaultErrorService.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/DefaultErrorService.java @@ -1,13 +1,16 @@ package at.gv.egiz.eaaf.core.impl.idp.auth.services; import java.text.MessageFormat; +import java.util.Collections; import java.util.HashSet; +import java.util.Map; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; +import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; @@ -35,7 +38,7 @@ public class DefaultErrorService implements IErrorService { } @Override - public IHandleData createHandleData(Throwable throwable, boolean supportRedirctToSp) throws EaafException { + public IHandleData createHandleData(Throwable throwable, IRequest protocolRequest) throws EaafException { String internalErrorId = extractInternalErrorCode(throwable); return HandleData.builder() @@ -101,11 +104,16 @@ public class DefaultErrorService implements IErrorService { @Getter private LogLevel logLevel; + + @Override + public Map getAdditionalGuiModelElements() { + return Collections.emptyMap(); + + } public String getPreFormatedErrorMessage() { return MessageFormat.format(TECH_LOG_MSG, internalErrorCode, throwable.getMessage()); - + } - } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorService.java index b6bc1056..45b1cfe8 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorService.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorService.java @@ -1,8 +1,12 @@ package at.gv.egiz.eaaf.core.impl.idp.auth.services; +import java.util.Map; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; +import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafException; @@ -94,14 +98,19 @@ public interface IErrorService { /** * Creates error handling data. * - * @param throwable Error that should be handled - * @param supportRedirctToSp true if the current process-state supports redirect - * to Service-Provider, otherwise false - * @return Information how the error should be handled + *

+ * Redirect to Service-Provider is supported in case of an available + * pendingRequest. + *

+ * + * @param throwable Error that should be handled + * @param protocolRequest Current pendingRequest if available + * @return Information how the error should be handled * @throws EaafException In case of an internal error */ @Nonnull - IHandleData createHandleData(@Nonnull Throwable throwable, boolean supportRedirctToSp) throws EaafException; + IHandleData createHandleData(@Nonnull Throwable throwable, @Nullable IRequest protocolRequest) + throws EaafException; /** * Displays the error using suitable errordata. @@ -160,5 +169,13 @@ public interface IErrorService { * @return log message */ String getPreFormatedErrorMessage(); + + /** + * Get additional elements for error GUI model. + * + * @return Map of GUI model elements. + */ + Map getAdditionalGuiModelElements(); + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorServiceModelHandler.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorServiceModelHandler.java new file mode 100644 index 00000000..7faf173d --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/IErrorServiceModelHandler.java @@ -0,0 +1,26 @@ +package at.gv.egiz.eaaf.core.impl.idp.auth.services; + +import java.util.Map; + +import javax.annotation.Nonnull; + +import at.gv.egiz.eaaf.core.api.IRequest; + +/** + * Extension point to add GUI model parameters into error-handling. + * + * @author tlenz + * + */ +public interface IErrorServiceModelHandler { + + /** + * Get GUI model elements that should be added into error model. + * + * @param pendingReq Current pendingRequest, but never null + * @return Model elements to add, but never null + */ + @Nonnull + Map elementsForErrorModel(@Nonnull IRequest pendingReq); + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java index de0b6f6a..76687749 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java @@ -229,7 +229,8 @@ public class ProtocolAuthenticationService implements IProtocolAuthenticationSer final HttpServletResponse resp, final IRequest protocolRequest) throws EaafException, IOException { try { - final IErrorService.IHandleData errorData = errorTicketService.createHandleData(throwable, true); + final IErrorService.IHandleData errorData = + errorTicketService.createHandleData(throwable, protocolRequest); // log Error to technical log logExceptionToTechnicalLog(errorData); @@ -311,7 +312,7 @@ public class ProtocolAuthenticationService implements IProtocolAuthenticationSer final HttpServletResponse resp, final boolean writeExceptionToStatisticLog) throws EaafException, IOException { - final IErrorService.IHandleData errorData = errorTicketService.createHandleData(throwable, false); + final IErrorService.IHandleData errorData = errorTicketService.createHandleData(throwable, null); // log Exception into statistic database if (writeExceptionToStatisticLog) { 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 index 557614e6..e5f7a5ea 100644 --- 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 @@ -5,7 +5,11 @@ import java.io.InputStream; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; @@ -22,6 +26,7 @@ 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.IRequest; 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; @@ -58,6 +63,9 @@ public abstract class TicketErrorService implements IErrorService { @Autowired IPendingRequestIdGenerationStrategy requestIdValidationStragegy; + @Autowired(required = false) + Set modelExtensionHandlers; + @Override public String getExternalCodeFromInternal(String internalCode) { ErrorConfig errorConfig = findByInternalCode(internalCode); @@ -70,7 +78,7 @@ public abstract class TicketErrorService implements IErrorService { } @Override - public IHandleData createHandleData(Throwable throwable, boolean supportRedirctToSp) throws EaafException { + public IHandleData createHandleData(Throwable throwable, IRequest protocolRequest) throws EaafException { String internalErrorId = extractInternalErrorCode(throwable); ErrorConfig errorFlowConfig = findByInternalCode(internalErrorId); ActionType errorHandlingFlow = errorFlowConfig.getActionType(); @@ -82,7 +90,8 @@ public abstract class TicketErrorService implements IErrorService { .logLevel(LogLevel.fromString(errorFlowConfig.getLogLevel())) .supportTicket(ActionType.TICKET.equals(errorHandlingFlow) ? generateSupportTicket() : null) .errorIdTokenForRedirect( - supportRedirctToSp ? requestIdValidationStragegy.generateExternalPendingRequestId() : null) + protocolRequest != null ? requestIdValidationStragegy.generateExternalPendingRequestId() : null) + .additionalGuiModelElements(readAdditionalGuiModelInfos(protocolRequest)) .build(); } @@ -95,6 +104,11 @@ public abstract class TicketErrorService implements IErrorService { } var ed = (TicketErrorService.HandleData) errorData; + // inject all additional model parameters + errorData.getAdditionalGuiModelElements().entrySet().forEach( + el -> c.putCustomParameter( + AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, el.getKey(), el.getValue())); + // set SupportTicket c.putCustomParameter(AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_TICKET, ed.getSupportTicket()); @@ -134,6 +148,20 @@ public abstract class TicketErrorService implements IErrorService { } + private Map readAdditionalGuiModelInfos(IRequest protocolRequest) { + if (protocolRequest != null) { + log.trace("Searching for additional GUI model elements to inject ... "); + return modelExtensionHandlers.stream() + .map(el -> el.elementsForErrorModel(protocolRequest)) + .flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + + } + + return Collections.emptyMap(); + + } + private String extractInternalErrorCode(Throwable throwable) { Throwable originalException; if (throwable instanceof TaskExecutionException @@ -188,6 +216,17 @@ public abstract class TicketErrorService implements IErrorService { new Object[]{CONFIG_PROP_ERROR_HANDLING_CONFIG_PATH, "Error reading Configurations file"}); } + // log currently loaded GUI model extension handlers + if (modelExtensionHandlers != null) { + modelExtensionHandlers.forEach(el -> log.info( + "Register Consent Attribute-Parameter handler: {} into Consent-RequestedAttributes Handler", + el.getClass().getName())); + + } else { + modelExtensionHandlers = Collections.emptySet(); + + } + } private byte[] readFromFile(final String filePath) throws URISyntaxException, IOException { @@ -219,6 +258,9 @@ public abstract class TicketErrorService implements IErrorService { @Getter private LogLevel logLevel; + + @Getter + private Map additionalGuiModelElements; public String getPreFormatedErrorMessage() { if (supportTicket != null) { @@ -228,7 +270,7 @@ public abstract class TicketErrorService implements IErrorService { return MessageFormat.format(TECH_LOG_MSG, internalErrorCode, throwable.getMessage()); } - } + } } } diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyDefaultErrorService.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyDefaultErrorService.java index 347f9b5c..ad7e9bdc 100644 --- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyDefaultErrorService.java +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/dummy/DummyDefaultErrorService.java @@ -1,11 +1,13 @@ package at.gv.egiz.eaaf.core.impl.idp.auth.dummy; import java.text.MessageFormat; +import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; +import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; @@ -40,7 +42,7 @@ public class DummyDefaultErrorService implements IErrorService { } @Override - public IHandleData createHandleData(Throwable throwable, boolean supportRedirctToSp) throws EaafException { + public IHandleData createHandleData(Throwable throwable, IRequest pendingReq) throws EaafException { String internalErrorId = extractInternalErrorCode(throwable); return HandleData.builder() @@ -49,7 +51,7 @@ public class DummyDefaultErrorService implements IErrorService { .actionType(ticketType) .logLevel(logLevel) .errorIdTokenForRedirect(errorIdTokenForRedirect) - .allowSpRedirct(supportRedirctToSp) + .allowSpRedirct(pendingReq != null) .build(); } @@ -106,10 +108,12 @@ public class DummyDefaultErrorService implements IErrorService { @Getter private LogLevel logLevel; + @Getter + private Map additionalGuiModelElements; + public String getPreFormatedErrorMessage() { return MessageFormat.format(TECH_LOG_MSG, internalErrorCode, throwable.getMessage()); } - } } diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/DummyErrorGuiModelHandler.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/DummyErrorGuiModelHandler.java new file mode 100644 index 00000000..2c1da193 --- /dev/null +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/service/DummyErrorGuiModelHandler.java @@ -0,0 +1,24 @@ +package at.gv.egiz.eaaf.core.impl.idp.auth.service; + +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.lang3.RandomStringUtils; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.impl.idp.auth.services.IErrorServiceModelHandler; + +public class DummyErrorGuiModelHandler implements IErrorServiceModelHandler { + + @Override + public Map elementsForErrorModel(IRequest pendingReq) { + if (pendingReq.getSpEntityId() != null) { + return Collections.singletonMap("test", RandomStringUtils.randomAlphanumeric(10)); + + } else { + return Collections.emptyMap(); + + } + } + +} 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 index 2c89e49f..d30f5a95 100644 --- 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 @@ -11,6 +11,7 @@ import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -69,17 +70,20 @@ public class TicketErrorServiceTest { @Test public void coverDifferentExceptions() throws EaafException { - val pendingReq = new DummyPendingRequest(); + DummyPendingRequest pendingReq = new DummyPendingRequest(); pendingReq.setPendingRequestId("324"); + pendingReq.setSpEntityId(RandomStringUtils.randomAlphabetic(5)); - val errorData = ticketErrorService.createHandleData(new Exception("sl20.07.02"), true); + val errorData = ticketErrorService.createHandleData(new Exception("sl20.07.02"), pendingReq); val errorData1 = ticketErrorService.createHandleData( - new EaafException("sl20.07.02", new Object[]{"dummy"}), false); + new EaafException("sl20.07.02", new Object[] { "dummy" }), null); 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); + new TaskExecutionException(pendingReq, "dummy", new EaafException("auth.00", new Object[] { + "dummy" })), null); + val errorData3 = ticketErrorService.createHandleData(new EaafException("auth.21", null), null); + val errorData4 = ticketErrorService.createHandleData(new EaafException("internal.pendingreqid.01", null), + null); + val errorData5 = ticketErrorService.createHandleData(new EaafException("junit.01", null), null); Assert.assertNotNull(errorData); Assert.assertEquals(errorData.getActionType(), IErrorService.ActionType.TICKET); @@ -87,13 +91,17 @@ public class TicketErrorServiceTest { Assert.assertEquals(errorData.getLogLevel(), IErrorService.LogLevel.ERROR); Assert.assertEquals(ticketErrorService.getExternalCodeFromInternal(errorData.getInternalErrorCode()), "9199"); Assert.assertNotNull(errorData.getErrorIdTokenForRedirect()); - + Assert.assertNotNull(errorData.getAdditionalGuiModelElements()); + Assert.assertFalse(errorData.getAdditionalGuiModelElements().isEmpty()); + 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(errorData1.getAdditionalGuiModelElements()); + Assert.assertTrue(errorData1.getAdditionalGuiModelElements().isEmpty()); Assert.assertNotNull(errorData2); Assert.assertEquals(errorData2.getActionType(), IErrorService.ActionType.TICKET); @@ -130,7 +138,8 @@ public class TicketErrorServiceTest { IErrorService.IHandleData errorData = null; try { - errorData = ticketErrorService.createHandleData(new EaafException("auth.00", new Object[]{"dummy"}), false); + errorData = ticketErrorService.createHandleData(new EaafException("auth.00", new Object[] { "dummy" }), + null); } catch (EaafException e) { e.printStackTrace(); } @@ -155,7 +164,7 @@ public class TicketErrorServiceTest { IErrorService.IHandleData errorData = null; try { errorData = ticketErrorService.createHandleData( - new EaafException("internal.pendingreqid.00", new Object[]{"dummy"}), false); + new EaafException("internal.pendingreqid.00", new Object[] { "dummy" }), null); } catch (EaafException e) { e.printStackTrace(); } @@ -175,9 +184,13 @@ public class TicketErrorServiceTest { val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core"); logger.addAppender(appender); + val pendingReq = new DummyPendingRequest(); + pendingReq.setPendingRequestId("324"); + IErrorService.IHandleData errorData = null; try { - errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[]{"dummy"}), true); + errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[] { "dummy" }), + pendingReq); } catch (EaafException e) { e.printStackTrace(); } @@ -200,7 +213,7 @@ public class TicketErrorServiceTest { IErrorService.IHandleData errorData = null; try { errorData = ticketErrorService - .createHandleData(new EaafException("module.binding.14", new Object[]{"dummy"}), false); + .createHandleData(new EaafException("module.binding.14", new Object[] { "dummy" }), null); } catch (EaafException e) { e.printStackTrace(); } @@ -220,9 +233,13 @@ public class TicketErrorServiceTest { val logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("at.gv.egiz.eaaf.core"); logger.addAppender(appender); + val pendingReq = new DummyPendingRequest(); + pendingReq.setPendingRequestId("324"); + IErrorService.IHandleData errorData = null; try { - errorData = ticketErrorService.createHandleData(new EaafException("auth.21", new Object[]{"dummy"}), true); + errorData = ticketErrorService.createHandleData(new EaafException("auth.21", new Object[] { "dummy" }), + pendingReq); } catch (EaafException e) { e.printStackTrace(); } @@ -232,6 +249,7 @@ public class TicketErrorServiceTest { // Assert.assertEquals(errorData.getThrowable(), new EaafException("auth.00", new Object[] {"dummy"})); //TODO // wrong excepiton Assert.assertEquals(errorData.getInternalErrorCode(), "auth.21"); + Assert.assertTrue(errorData.getAdditionalGuiModelElements().isEmpty()); assertEquals("wrong errorLevel", LogLevel.INFO, errorData.getLogLevel()); assertFalse("ticket", errorData.getPreFormatedErrorMessage().contains("Ticket=")); @@ -239,9 +257,12 @@ public class TicketErrorServiceTest { @Test public void testErrorDataDisplay() throws EaafException { + val pendingReq = new DummyPendingRequest(); + pendingReq.setPendingRequestId("324"); IErrorService.IHandleData errorData = null; - errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[]{"dummy"}), true); + errorData = ticketErrorService.createHandleData(new EaafException("auth.01", new Object[] { "dummy" }), + pendingReq); Assert.assertNotNull(errorData); val guiBuilder = Mockito.mock(ModifyableGuiBuilderConfiguration.class); diff --git a/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml b/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml index 35bd9d6f..6264fac5 100644 --- a/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml +++ b/eaaf_core/src/test/resources/SpringTest-authcommon-errorService.xml @@ -22,4 +22,7 @@ + + -- cgit v1.2.3