diff options
author | Alexander Marsalek <amarsalek@iaik.tugraz.at> | 2021-01-18 10:57:38 +0100 |
---|---|---|
committer | Alexander Marsalek <amarsalek@iaik.tugraz.at> | 2021-01-18 12:04:52 +0100 |
commit | 9afa8f094712729b4486a408e12f4ab3027938b4 (patch) | |
tree | 3adbbf5d892452ea8b82404f28e95fdadd34ece8 /connector/src/main/java | |
parent | 09751b59f7e2da247c32324826607e5f1eef0f10 (diff) | |
parent | 68e9725d024ccef7b618f462dee5648ca288bdc0 (diff) | |
download | National_eIDAS_Gateway-9afa8f094712729b4486a408e12f4ab3027938b4.tar.gz National_eIDAS_Gateway-9afa8f094712729b4486a408e12f4ab3027938b4.tar.bz2 National_eIDAS_Gateway-9afa8f094712729b4486a408e12f4ab3027938b4.zip |
Merge branch 'base' into issue6
# Conflicts:
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/DummyErnpClient.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/IErnpClient.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/InitialSearchTask.java
# eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java
# eidas_modules/authmodule-eIDAS-v2/src/main/resources/eIDAS.Authentication.process.xml
# eidas_modules/authmodule-eIDAS-v2/src/main/resources/eidas_v2_auth.beans.xml
# eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/InitialSearchTaskFirstTest.java
# eidas_modules/authmodule-eIDAS-v2/src/test/resources/SpringTest-context_tasks_test.xml
Diffstat (limited to 'connector/src/main/java')
16 files changed, 649 insertions, 566 deletions
diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificEidasNodeSpringResourceProvider.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificEidasNodeSpringResourceProvider.java index 6e8e06ef..40ed283b 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificEidasNodeSpringResourceProvider.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificEidasNodeSpringResourceProvider.java @@ -32,11 +32,15 @@ public class MsSpecificEidasNodeSpringResourceProvider implements SpringResource @Override public Resource[] getResourcesToLoad() { - final ClassPathResource msEidasNode = new ClassPathResource("/specific_eIDAS_connector.beans.xml", - MsSpecificEidasNodeSpringResourceProvider.class); + final ClassPathResource generic = + new ClassPathResource("/applicationContext.xml", MsSpecificEidasNodeSpringResourceProvider.class); + + final ClassPathResource msEidasNode = new ClassPathResource( + "/specific_eIDAS_connector.beans.xml", MsSpecificEidasNodeSpringResourceProvider.class); + final ClassPathResource msEidasNodeStorage = new ClassPathResource( "/specific_eIDAS_connector.storage.beans.xml", MsSpecificEidasNodeSpringResourceProvider.class); - return new Resource[] { msEidasNode, msEidasNodeStorage }; + return new Resource[] { generic, msEidasNode, msEidasNodeStorage }; } @Override diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificSpringBootApplicationContextInitializer.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificSpringBootApplicationContextInitializer.java new file mode 100644 index 00000000..399d1286 --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/MsSpecificSpringBootApplicationContextInitializer.java @@ -0,0 +1,82 @@ +package at.asitplus.eidas.specific.connector; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; + +import at.gv.egiz.components.spring.api.SpringBootApplicationContextInitializer; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class MsSpecificSpringBootApplicationContextInitializer extends + SpringBootApplicationContextInitializer { + + private static final String SYSTEMD_PROP_NAME = "eidas.ms.configuration"; + private static final String PATH_FILE_PREFIX = "file:"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + String configPath = System.getProperty(SYSTEMD_PROP_NAME); + if (StringUtils.isNotEmpty(configPath)) { + log.debug("Find configuration-source from SystemD Property: '{}' ...", SYSTEMD_PROP_NAME); + if (configPath.startsWith(PATH_FILE_PREFIX)) { + configPath = configPath.substring(PATH_FILE_PREFIX.length()); + + } + injectConfiguration(configPath, applicationContext); + + } else { + log.info("Find NO SystemD Property: '{}' Maybe no configuration available", SYSTEMD_PROP_NAME); + + } + + super.initialize(applicationContext); + + } + + private void injectConfiguration(String configPath, ConfigurableApplicationContext applicationContext) { + InputStream is = null; + try { + Path path = Paths.get(configPath); + if (Files.exists(path)) { + File file = new File(configPath); + Properties props = new Properties(); + is = new FileInputStream(file); + props.load(is); + MutablePropertySources sources = applicationContext.getEnvironment().getPropertySources(); + sources.addFirst(new PropertiesPropertySource(SYSTEMD_PROP_NAME, props)); + log.info("Set configuration-source from SystemD-Property: {}", SYSTEMD_PROP_NAME); + + } else { + log.error("Configuration from SystemD Property: '{}' at Location: {} DOES NOT exist", + SYSTEMD_PROP_NAME, configPath); + + } + + } catch (IOException e) { + log.error("Configuration from SystemD Property: '{}' at Location: {} CAN NOT be loaded", + SYSTEMD_PROP_NAME, configPath, e); + + } finally { + try { + if (is != null) { + is.close(); + + } + } catch (IOException e) { + log.error("Can not close InputStream of configLoader: {}", configPath, e); + + } + } + } +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringBootApplicationInitializer.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringBootApplicationInitializer.java new file mode 100644 index 00000000..428f5f56 --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringBootApplicationInitializer.java @@ -0,0 +1,105 @@ +package at.asitplus.eidas.specific.connector; + +import org.opensaml.core.config.InitializationException; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.web.context.WebApplicationContext; + +import at.gv.egiz.eaaf.core.api.IStatusMessenger; +import at.gv.egiz.eaaf.core.impl.logging.LogMessageProviderFactory; +import at.gv.egiz.eaaf.core.impl.logging.SimpleStatusMessager; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafOpenSaml3xInitializer; +import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; + +@Slf4j +@SpringBootApplication(scanBasePackages = { + "at.asitplus.eidas.specific.connector", + "at.gv.egiz.eaaf.utils.springboot.ajp" + }) +public class SpringBootApplicationInitializer extends SpringBootServletInitializer { + + private static ConfigurableApplicationContext ctx; + + /** + * Starts MS-specific eIDAS-Implementation SpringBoot application. + * + * @param args Starting parameters + * @throws Throwable In case of a start-up error + */ + public static void main(final String[] args) throws Throwable { + try { + log.info("=============== Initializing Spring-Boot context! ==============="); + LogMessageProviderFactory.setStatusMessager(new SimpleStatusMessager()); + final SpringApplication springApp = + new SpringApplication(SpringBootApplicationInitializer.class); + springApp.addInitializers(new MsSpecificSpringBootApplicationContextInitializer()); + + log.info("Bootstrap openSAML .... "); + EaafOpenSaml3xInitializer.eaafInitialize(); + + log.debug("Run SpringBoot initialization process ... "); + ctx = springApp.run(args); + + // initialize status messenger + LogMessageProviderFactory.setStatusMessager(ctx.getBean(IStatusMessenger.class)); + + log.info("Initialization of MS-specific eIDAS-Implementation finished."); + + } catch (final Throwable e) { + log.error("MS-specific eIDAS-Implementation initialization FAILED!", e); + throw e; + + } + + } + + + protected SpringApplicationBuilder createSpringApplicationBuilder() { + try { + log.info("Bootstrap openSAML .... "); + EaafOpenSaml3xInitializer.eaafInitialize(); + + } catch (InitializationException | ComponentInitializationException e) { + throw new RuntimeException(e); + + } + + SpringApplicationBuilder builder = new SpringApplicationBuilder(); + builder.initializers(new MsSpecificSpringBootApplicationContextInitializer()); + return builder; + + } + + protected WebApplicationContext run(SpringApplication application) { + WebApplicationContext internalContext = (WebApplicationContext) application.run(); + + // initialize status messenger + LogMessageProviderFactory.setStatusMessager(internalContext.getBean(IStatusMessenger.class)); + + log.info("Initialization of MS-specific eIDAS-Implementation finished."); + + return internalContext; + } + + /** + * Stops SpringBoot application of MS-specific eIDAS-Implementation. + * + */ + public static void exit() { + if (ctx != null) { + log.info("Stopping SpringBoot application ... "); + SpringApplication.exit(ctx, () -> 0); + ctx = null; + + } else { + log.info("No SpringBoot context. Nothing todo"); + + } + + } + +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringContextCloseHandler.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringContextCloseHandler.java new file mode 100644 index 00000000..e884b5c6 --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringContextCloseHandler.java @@ -0,0 +1,170 @@ +package at.asitplus.eidas.specific.connector; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import at.gv.egiz.components.spring.api.IDestroyableObject; +import eu.eidas.auth.cache.IgniteInstanceInitializerSpecificCommunication; + +/** + * SpringContext CloseHandler. + * + * @author tlenz + * + */ + +public class SpringContextCloseHandler + implements ApplicationListener<ContextClosedEvent>, ApplicationContextAware, BeanPostProcessor { + + private static final Logger log = + org.slf4j.LoggerFactory.getLogger(SpringContextCloseHandler.class); + + private ApplicationContext context; + + /* + * (non-Javadoc) + * + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org. + * springframework.context. ApplicationEvent) + */ + @Override + @EventListener + public void onApplicationEvent(final ContextClosedEvent arg0) { + log.info("MS-specific eIDAS-Node shutdown process started ..."); + + try { + log.debug("CleanUp objects with implements the IDestroyable interface ... "); + final Map<String, IDestroyableObject> objectsToDestroy = + context.getBeansOfType(IDestroyableObject.class); + internalIDestroyableObject(objectsToDestroy); + log.info("Object cleanUp complete"); + + log.debug("Stopping Spring Thread-Pools ... "); + // shut-down task schedulers + final Map<String, ThreadPoolTaskScheduler> schedulers = + context.getBeansOfType(ThreadPoolTaskScheduler.class); + internalThreadPoolTaskScheduler(schedulers); + + // shut-down task executors + final Map<String, ThreadPoolTaskExecutor> executers = + context.getBeansOfType(ThreadPoolTaskExecutor.class); + internalThreadPoolTaskExecutor(executers); + log.debug("Spring Thread-Pools stopped"); + + + //clean-up eIDAS node + Map<String, IgniteInstanceInitializerSpecificCommunication> nodeIgnite = + context.getBeansOfType(IgniteInstanceInitializerSpecificCommunication.class); + log.info("Find #{} Apache Ignite instances from eIDAS Ref. impl.", nodeIgnite.size()); + for (Entry<String, IgniteInstanceInitializerSpecificCommunication> el : nodeIgnite.entrySet()) { + if (el.getValue().getInstance() != null) { + el.getValue().getInstance().close(); + el.getValue().destroyInstance(); + log.debug("Shutdown Apache-Ignite: {}", el.getKey()); + + } + } + + log.info("MS-specific eIDAS-Node shutdown process finished"); + + } catch (final Exception e) { + log.warn("MS-specific eIDAS-Node shutdown process has an error.", e); + + } + + } + + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.BeanPostProcessor# + * postProcessAfterInitialization(java. lang.Object, java.lang.String) + */ + @Override + public Object postProcessAfterInitialization(final Object arg0, final String arg1) + throws BeansException { + if (arg0 instanceof ThreadPoolTaskScheduler) { + ((ThreadPoolTaskScheduler) arg0).setWaitForTasksToCompleteOnShutdown(true); + } + if (arg0 instanceof ThreadPoolTaskExecutor) { + ((ThreadPoolTaskExecutor) arg0).setWaitForTasksToCompleteOnShutdown(true); + } + return arg0; + + } + + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.BeanPostProcessor# + * postProcessBeforeInitialization(java .lang.Object, java.lang.String) + */ + @Override + public Object postProcessBeforeInitialization(final Object arg0, final String arg1) + throws BeansException { + return arg0; + + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.context.ApplicationContextAware#setApplicationContext(org + * .springframework. context.ApplicationContext) + */ + @Override + public void setApplicationContext(final ApplicationContext arg0) throws BeansException { + this.context = arg0; + + } + + private void internalThreadPoolTaskExecutor(final Map<String, ThreadPoolTaskExecutor> executers) { + for (final ThreadPoolTaskExecutor executor : executers.values()) { + executor.shutdown(); + log.debug("Executer {} with active {} work has killed", executor.getThreadNamePrefix(), + executor.getActiveCount()); + + } + + } + + // Not required at the moment + private void internalThreadPoolTaskScheduler( + final Map<String, ThreadPoolTaskScheduler> schedulers) { + log.trace("Stopping #{} task-schedulers", schedulers.size()); + + } + + private void internalIDestroyableObject(final Map<String, IDestroyableObject> objectsToDestroy) { + if (objectsToDestroy != null) { + final Iterator<Entry<String, IDestroyableObject>> interator = + objectsToDestroy.entrySet().iterator(); + while (interator.hasNext()) { + final Entry<String, IDestroyableObject> object = interator.next(); + try { + object.getValue().fullyDestroy(); + log.debug("Object with ID: {} is destroyed", object.getKey()); + + } catch (final Exception e) { + log.warn("Destroing object with ID: {} FAILED!", object.getKey(), null, e); + + } + } + } + + } + +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringInitializer.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringInitializer.java deleted file mode 100644 index 417828a6..00000000 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/SpringInitializer.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2018 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, - * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "License"); - * You may not use this work except in compliance with the License. - * You may obtain a copy of the License at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * 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. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.asitplus.eidas.specific.connector; - -import java.util.Arrays; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.web.WebApplicationInitializer; -import org.springframework.web.context.ContextLoaderListener; -import org.springframework.web.context.request.RequestContextListener; -import org.springframework.web.context.support.GenericWebApplicationContext; -import org.springframework.web.context.support.ServletContextResource; -import org.springframework.web.servlet.DispatcherServlet; - -import at.gv.egiz.components.spring.api.SpringLoader; -import at.gv.egiz.eaaf.core.api.IStatusMessenger; -import at.gv.egiz.eaaf.core.impl.logging.LogMessageProviderFactory; -import at.gv.egiz.eaaf.core.impl.utils.Random; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafOpenSaml3xInitializer; - -/** - * Web application initializer. - * - * @author Thomas Lenz - */ -public class SpringInitializer implements WebApplicationInitializer { - - private static final Logger log = LoggerFactory.getLogger(SpringInitializer.class); - - private String[] rootServletContexts = null; - private String[] servletContexts = null; - - /** - * Application specific Spring initializer. - * - */ - public SpringInitializer() { - this.rootServletContexts = null; - this.servletContexts = new String[] { - "/applicationContext.xml", - - }; - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.web.WebApplicationInitializer#onStartup(javax.servlet. - * ServletContext) - */ - @Override - public void onStartup(ServletContext servletContext) throws ServletException { - try { - log.info("=============== Loading Config Root Context! ==============="); - final ApplicationContext cfgRootContext = - new ClassPathXmlApplicationContext(new String[] { - "/applicationContext.xml" - }); - - log.info("=============== Loading Root Context! ==============="); - final GenericWebApplicationContext rootContext = new GenericWebApplicationContext(); - rootContext.setServletContext(servletContext); - rootContext.setParent(cfgRootContext); - - log.info("Spring-context was initialized with active profiles: {}", - Arrays.asList(rootContext.getEnvironment().getActiveProfiles())); - - log.info("=============== Loading Local Contexts! ==============="); - final XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader( - rootContext); - if (rootServletContexts != null) { - for (final String rootServletContext : rootServletContexts) { - log.debug("Loading: " + rootServletContext); - xmlReader.loadBeanDefinitions(new ServletContextResource( - servletContext, rootServletContext)); - } - } - // Manage the lifecycle of the root application context - servletContext.addListener(new ContextLoaderListener(rootContext)); - - // log.debug("Beans after logAMQP in {}", rootContext); - // dumpBeanDefinitions(rootContext); - - log.info("=============== Loading SPI Context! ==============="); - log.debug("Loading modules and components"); - SpringLoader.loadSpringServices(rootContext); - - log.trace("Beans after SPI in " + rootContext); - dumpBeanDefinitions(rootContext); - - log.debug("Loading servlet config in " + rootContext); - if (servletContexts != null) { - for (final String servletContextString : servletContexts) { - xmlReader.loadBeanDefinitions(new ClassPathResource(servletContextString, SpringInitializer.class)); - } - - } - - log.debug("Refreshing context " + rootContext); - rootContext.refresh(); - - log.info("=============== Register Dispatcher Servlet! ==============="); - - log.trace("Final Beans in " + rootContext); - dumpBeanDefinitions(rootContext); - - log.info("Registering dispatcher configuration"); - final ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", - new DispatcherServlet(rootContext)); - if (dispatcher != null) { - dispatcher.setLoadOnStartup(1); - dispatcher.addMapping("/"); - dispatcher.setAsyncSupported(true); - - } else { - log.error("Failed to register dispatcher server in servlet context!"); - } - - log.info("=============== Register RequestContextListener! ==============="); - servletContext.addListener(new RequestContextListener()); - - // initialize status messenger - LogMessageProviderFactory.setStatusMessager(rootContext.getBean(IStatusMessenger.class)); - - log.info("Bootstrap openSAML .... "); - EaafOpenSaml3xInitializer.eaafInitialize(); - - log.info("Seed random number generator ... "); - Random.seedRandom(); - - log.info("Initialization of MS-specific eIDAS-connector finished."); - - } catch (final Throwable e) { - log.error("MS-specific eIDAS-connector initialization FAILED!", e); - - } - - } - - private void dumpBeanDefinitions(GenericApplicationContext context) { - log.trace("Registered Bean in context " + context.toString()); - - final String[] registeredBeans = context.getBeanDefinitionNames(); - for (final String registeredBean : registeredBeans) { - final BeanDefinition beanDefinition = context - .getBeanDefinition(registeredBean); - log.trace(registeredBean + " -> " + beanDefinition.getBeanClassName()); - - } - - log.trace("Registered Bean in context --" + context); - } -} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/attributes/AuthBlockAttributeBuilder.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/attributes/AuthBlockAttributeBuilder.java index be9f8862..1833f377 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/attributes/AuthBlockAttributeBuilder.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/attributes/AuthBlockAttributeBuilder.java @@ -22,6 +22,9 @@ package at.asitplus.eidas.specific.connector.attributes; import static at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.EID_AUTHBLOCK_SIGNED_FRIENDLY_NAME; import static at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.EID_AUTHBLOCK_SIGNED_NAME; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + import org.apache.commons.lang3.StringUtils; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; @@ -48,7 +51,8 @@ public class AuthBlockAttributeBuilder implements IPvpAttributeBuilder { String authBlock = authData.getGenericData(Constants.SZR_AUTHBLOCK, String.class); if (StringUtils.isNotEmpty(authBlock)) { - return g.buildStringAttribute(EID_AUTHBLOCK_SIGNED_FRIENDLY_NAME, EID_AUTHBLOCK_SIGNED_NAME, authBlock); + return g.buildStringAttribute(EID_AUTHBLOCK_SIGNED_FRIENDLY_NAME, EID_AUTHBLOCK_SIGNED_NAME, + Base64.getEncoder().encodeToString(authBlock.getBytes(StandardCharsets.UTF_8))); } else { throw new UnavailableAttributeException(EID_AUTHBLOCK_SIGNED_NAME); diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/builder/AuthenticationDataBuilder.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/builder/AuthenticationDataBuilder.java index 13cceafb..c41660ce 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/builder/AuthenticationDataBuilder.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/builder/AuthenticationDataBuilder.java @@ -25,70 +25,63 @@ package at.asitplus.eidas.specific.connector.builder; import java.util.Date; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import org.w3c.dom.DOMException; import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions; import at.gv.egiz.eaaf.core.api.idp.IAuthData; import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; -import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.exceptions.EaafParserException; -import at.gv.egiz.eaaf.core.exceptions.XPathException; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData; import at.gv.egiz.eaaf.core.impl.idp.auth.builder.AbstractAuthenticationDataBuilder; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; +import lombok.extern.slf4j.Slf4j; @Service("AuthenticationDataBuilder") +@Slf4j public class AuthenticationDataBuilder extends AbstractAuthenticationDataBuilder { - private static final Logger log = LoggerFactory.getLogger(AuthenticationDataBuilder.class); - + @Override - public IAuthData buildAuthenticationData(IRequest pendingReq) throws EaafAuthenticationException { - - final IAuthProcessDataContainer authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); + protected IAuthData buildDeprecatedAuthData(IRequest pendingReq) throws EaafException { + final IAuthProcessDataContainer authProcessData = + pendingReq.getSessionData(AuthProcessDataWrapper.class); AuthenticationData authData = new AuthenticationData(); - - boolean isEidModeNew = pendingReq.getServiceProviderConfiguration() - .isConfigurationValue(MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE, false); - - if (isEidModeNew) { - authData = (AuthenticationData) super.buildAuthenticationData(pendingReq); - } else { - try { - generateDeprecatedBasicAuthData(authData, pendingReq, authProcessData); - - // set specific informations - authData.setSsoSessionValidTo( - new Date(new Date().getTime() + MsEidasNodeConstants.DEFAULT_PVP_ASSERTION_VALIDITY * 60 * 1000)); - - } catch (EaafBuilderException | EaafParserException - | EaafConfigurationException | XPathException | DOMException e) { - log.warn("Can not build authentication data from auth. process information"); - throw new EaafAuthenticationException("builder.11", new Object[]{e.getMessage()}, e); - - } - } + + //set basis infos + super.generateDeprecatedBasicAuthData(authData, pendingReq, authProcessData); + + // set specific informations + authData.setSsoSessionValidTo( + new Date(new Date().getTime() + MsEidasNodeConstants.DEFAULT_PVP_ASSERTION_VALIDITY * 60 * 1000)); + return authData; - } - - @Override - protected IAuthData buildDeprecatedAuthData(IRequest arg0) throws EaafException { - return new AuthenticationData(); } @Override - protected void buildServiceSpecificAuthenticationData(IAuthData arg0, IRequest arg1) throws EaafException { - // TODO Auto-generated method stub - + protected void buildServiceSpecificAuthenticationData(IAuthData authData, IRequest pendingReq) + throws EaafException { + if (authData instanceof AuthenticationData) { + ((AuthenticationData)authData).setGenericData( + ExtendedPvpAttributeDefinitions.EID_PII_TRANSACTION_ID_NAME, + pendingReq.getUniquePiiTransactionIdentifier()); + log.trace("Inject piiTransactionId: {} into AuthData", pendingReq.getUniquePiiTransactionIdentifier()); + + // set specific informations + ((AuthenticationData)authData).setSsoSessionValidTo( + new Date(new Date().getTime() + MsEidasNodeConstants.DEFAULT_PVP_ASSERTION_VALIDITY * 60 * 1000)); + + + } else { + throw new RuntimeException("Can not inject PiiTransactionId because AuthData is of unknown type: " + + authData.getClass().getName()); + + } + } @Override @@ -100,8 +93,6 @@ public class AuthenticationDataBuilder extends AbstractAuthenticationDataBuilder @Override protected Pair<String, String> buildOAspecificbPK(IRequest pendingReq, AuthenticationData authData) throws EaafBuilderException { - // TODO: check if bPK already exists - return super.buildOAspecificbPK(pendingReq, authData); } diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/config/StaticResourceConfiguration.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/config/StaticResourceConfiguration.java index 2a10031b..a1e953f1 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/config/StaticResourceConfiguration.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/config/StaticResourceConfiguration.java @@ -24,6 +24,7 @@ package at.asitplus.eidas.specific.connector.config; import java.net.MalformedURLException; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -35,11 +36,11 @@ import org.springframework.context.support.ReloadableResourceBundleMessageSource import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.thymeleaf.templateresolver.FileTemplateResolver; import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; /** @@ -90,43 +91,63 @@ public class StaticResourceConfiguration implements WebMvcConfigurer { } /** - * Internal i18n message source. - * + * Get a message source with only internal message properties. + * + * @param ressourceLocations List of source-locations * @return */ @Bean - public ReloadableResourceBundleMessageSource internalMessageSource() { - final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + public ReloadableResourceBundleMessageSource internalMessageSource( + @Autowired(required = false) final List<IMessageSourceLocation> ressourceLocations) { + final ReloadableResourceBundleMessageSource messageSource = + new ReloadableResourceBundleMessageSource(); // add default message source messageSource.setBasename(DEFAULT_MESSAGE_SOURCE); + + if (ressourceLocations != null) { + // load more message sources + for (final IMessageSourceLocation el : ressourceLocations) { + if (el.getMessageSourceLocation() != null) { + for (final String source : el.getMessageSourceLocation()) { + messageSource.addBasenames(source); + log.debug("Add additional messageSources: {}", el.getMessageSourceLocation().toArray()); + + } + } + } + } + messageSource.setDefaultEncoding("UTF-8"); return messageSource; } /** - * External i18n message source. - * + * Get full message source with internal and external message-properties files. + * + * @param ressourceLocations List of source-locations * @return */ @Bean - public ReloadableResourceBundleMessageSource messageSource() { - final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + public ReloadableResourceBundleMessageSource messageSource( + @Autowired(required = false) final List<IMessageSourceLocation> ressourceLocations) { + final ReloadableResourceBundleMessageSource messageSource = + new ReloadableResourceBundleMessageSource(); messageSource.setDefaultEncoding("UTF-8"); - messageSource.setParentMessageSource(internalMessageSource()); + messageSource.setParentMessageSource(internalMessageSource(ressourceLocations)); - final String staticResources = basicConfig.getBasicConfiguration( - MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_PROPERTIES_PATH); + final String staticResources = basicConfig + .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_PROPERTIES_PATH); try { if (StringUtils.isNotEmpty(staticResources)) { - final String absPath = FileUtils.makeAbsoluteUrl(staticResources, basicConfig - .getConfigurationRootDirectory()); + final String absPath = + FileUtils.makeAbsoluteUrl(staticResources, basicConfig.getConfigurationRootDirectory()); messageSource.setBasename(absPath); - messageSource.setFallbackToSystemLocale(false); } else { log.debug("No Ressourcefolder for dynamic Web content templates"); + } } catch (final MalformedURLException e) { @@ -137,40 +158,28 @@ public class StaticResourceConfiguration implements WebMvcConfigurer { return messageSource; } - + /** - * Cookie based i18n language selector. - * - * @return - */ - @Bean - public CookieLocaleResolver localeResolver() { - final CookieLocaleResolver localeResolver = new CookieLocaleResolver(); - localeResolver.setCookieName("currentLanguage"); - localeResolver.setCookieMaxAge(3600); - return localeResolver; - } - - - /** - * Thymeleaf based template resolver. - * + * Get a Tyhmeleaf Template-Resolver with external configuration path. + * * @return */ @Bean(name = "templateResolver") public FileTemplateResolver templateResolver() { - final String staticResources = basicConfig.getBasicConfiguration( - MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_PATH); + final String staticResources = basicConfig + .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_PATH); try { if (StringUtils.isNotEmpty(staticResources)) { - String absPath = FileUtils.makeAbsoluteUrl(staticResources, basicConfig - .getConfigurationRootDirectory()); + String absPath = + FileUtils.makeAbsoluteUrl(staticResources, basicConfig.getConfigurationRootDirectory()); if (!absPath.endsWith("/")) { absPath += "/"; + } if (absPath.startsWith("file:")) { absPath = absPath.substring("file:".length()); + } final FileTemplateResolver viewResolver = new FileTemplateResolver(); @@ -179,11 +188,12 @@ public class StaticResourceConfiguration implements WebMvcConfigurer { viewResolver.setTemplateMode("HTML"); viewResolver.setCacheable(false); - log.info("Add Ressourcefolder: " + absPath + " for dynamic Web content templates"); + log.info("Add Ressourcefolder: {} for dynamic Web content templates", absPath); return viewResolver; } else { log.debug("No Ressourcefolder for dynamic Web content templates"); + } } catch (final MalformedURLException e) { @@ -191,8 +201,7 @@ public class StaticResourceConfiguration implements WebMvcConfigurer { } - // TODO: implement some backup solution - return null; + throw new RuntimeException("Can NOT initialize HTML template resolver"); } } diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/controller/MonitoringController.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/controller/MonitoringController.java deleted file mode 100644 index aa45c836..00000000 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/controller/MonitoringController.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2018 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, - * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "License"); - * You may not use this work except in compliance with the License. - * You may obtain a copy of the License at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * 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. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.asitplus.eidas.specific.connector.controller; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.xml.transform.TransformerFactoryConfigurationError; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.client.CloseableHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; -import at.gv.egiz.eaaf.core.api.data.EaafConstants; -import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP; -import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; -import at.gv.egiz.eaaf.core.impl.utils.DomUtils; -import at.gv.egiz.eaaf.core.impl.utils.Random; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataConfigurationFactory; -import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpMetadataBuilder; -import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; - -@Controller -public class MonitoringController { - private static final Logger log = LoggerFactory.getLogger(MonitoringController.class); - - private static final String MESSAGE_OK = "OK"; - private static final String MESSAGE_ERROR = "ERROR"; - private static final String MESSAGE_SKIPPED = "SKIPPED"; - - private static final String TEST_STORAGE = "Storage: "; - private static final String TEST_CONFIG = "Config: "; - private static final String TEST_PVPMETADATA = "PVP_metadata: "; - private static final String TEST_EIDASNODEMETADATA = "eIDASNode_metadata: "; - - @Autowired - private ITransactionStorage storage; - @Autowired - private IConfigurationWithSP config; - - @Autowired private IHttpClientFactory httpClientFactory; - - @Autowired - private PvpMetadataBuilder metadatabuilder; - @Autowired - private IPvpMetadataConfigurationFactory configFactory; - private AbstractCredentialProvider pvpIdpCredentials; - - /** - * Sets a specific credential provider for PVP S-Profile IDP component. - * - * @param pvpIdpCredentials credential provider - */ - public void setPvpIdpCredentials(AbstractCredentialProvider pvpIdpCredentials) { - this.pvpIdpCredentials = pvpIdpCredentials; - - } - - /** - * Generic exception handling that wrote an error-message to html response. - * - * @param resp Http response object - * @param exception Error - * @throws IOException In case of a html response error. - */ - @ExceptionHandler({ Throwable.class }) - public void genericExceptionHandler(HttpServletResponse resp, Exception exception) throws IOException { - log.error("Monitoring Servlet receives an error.", exception); - resp.setContentType(EaafConstants.CONTENTTYPE_HTML_UTF8); - resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - resp.getWriter().write("Reason: " - + StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(exception.getMessage()))); - - } - - /** - * MS-Connector status-monitoring end-point. - * - * @param req http request - * @param resp http response - * @throws IOException In case of a general processing error - */ - @RequestMapping(value = { MsEidasNodeConstants.ENDPOINT_MONITORING_MONITOR }, - method = { RequestMethod.GET }) - public void startFullTest(HttpServletRequest req, HttpServletResponse resp) throws IOException { - resp.setContentType(EaafConstants.CONTENTTYPE_HTML_UTF8); - - try { - testConfig(); - testStorage(); - testPvpMetadata(); - testEidasNodeMetadata(); - resp.setStatus(HttpServletResponse.SC_OK); - resp.getWriter().write(MESSAGE_OK); - - } catch (final Exception e) { - resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - resp.getWriter().write(MESSAGE_ERROR); - - } - - } - - /** - * MS-Connector internal verify monitoring end-point. - * - * @param req http request object - * @param resp http response object - * @throws IOException In case of an internal processing error - */ - @RequestMapping(value = { MsEidasNodeConstants.ENDPOINT_MONITORING_VERIFY }, - method = { RequestMethod.GET }) - - public void startSingleTests(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String result = StringUtils.EMPTY; - try { - result += testConfig() + "<br>"; - } catch (final Exception e) { - result += e.getMessage() + "<br>"; - } - - try { - result += testStorage() + "<br>"; - } catch (final Exception e) { - result += e.getMessage() + "<br>"; - } - - try { - result += testPvpMetadata() + "<br>"; - } catch (final Exception e) { - result += e.getMessage() + "<br>"; - } - - try { - result += testEidasNodeMetadata() + "<br>"; - } catch (final Exception e) { - result += e.getMessage() + "<br>"; - } - - resp.setContentType(EaafConstants.CONTENTTYPE_HTML_UTF8); - resp.setStatus(HttpServletResponse.SC_OK); - resp.getWriter().write(result); - - } - - private String testStorage() throws Exception { - try { - final String key = Random.nextHexRandom16(); - final String value = Random.nextHexRandom16(); - - storage.put(key, value, -1); - final String result = storage.get(key, String.class); - storage.remove(key); - - if (result != null && result.equals(value)) { - return TEST_STORAGE + MESSAGE_OK; - } else { - log.warn("Montioring: TestValue: " + value + " does NOT match in Storage test"); - } - - } catch (final EaafException e) { - log.warn("Montioring: Can not read/write to storage.", e); - - } - - throw new Exception(TEST_STORAGE + MESSAGE_ERROR); - - } - - private String testConfig() throws Exception { - try { - if (config.getBasicConfigurationWithPrefix(MsEidasNodeConstants.PROP_CONFIG_SP_LIST_PREFIX) != null - && config.getBasicConfigurationWithPrefix(MsEidasNodeConstants.PROP_CONFIG_SP_LIST_PREFIX) - .size() > 0) { - return TEST_CONFIG + MESSAGE_OK; - } else { - log.warn("Montioring: Can not read from configuration file."); - } - - } catch (final Exception e) { - log.warn("Montioring: Can not read from configuration file.", e); - } - - throw new Exception(TEST_CONFIG + MESSAGE_ERROR); - - } - - private String testPvpMetadata() throws Exception { - try { - // build metadata - final IPvpMetadataBuilderConfiguration metadataConfig = - configFactory.generateMetadataBuilderConfiguration( - "http://localhost/monitoring", - pvpIdpCredentials); - metadatabuilder.buildPvpMetadata(metadataConfig); - return TEST_PVPMETADATA + MESSAGE_OK; - - } catch (Exception | TransformerFactoryConfigurationError e) { - log.warn("Monitoring: Has an error in '" + TEST_PVPMETADATA + "': " + e.getMessage(), e); - throw new Exception(TEST_PVPMETADATA + MESSAGE_ERROR, e); - - } - - } - - private String testEidasNodeMetadata() throws Exception { - try { - final String urlString = config.getBasicConfiguration( - MsEidasNodeConstants.PROP_CONFIG_MONITORING_EIDASNODE_METADATAURL); - if (StringUtils.isEmpty(urlString)) { - log.debug("No eIDASNode metadata URL. Skipping test ... "); - return TEST_EIDASNODEMETADATA + MESSAGE_SKIPPED; - - } - - // create HTTP client - // TODO: update if we switch to openSAML3 - CloseableHttpClient httpClient = httpClientFactory.getHttpClient(); - HttpUriRequest request = new HttpGet(urlString); - - final CloseableHttpResponse respCode = httpClient.execute(request); - if (respCode.getStatusLine().getStatusCode() != 200) { - log.warn("Monitoring: Has an error in '" + TEST_EIDASNODEMETADATA + "': " + " HTTP responsecode: " - + respCode); - throw new Exception(TEST_EIDASNODEMETADATA + MESSAGE_ERROR); - - } - - // parse metadata - DomUtils.parseXmlNonValidating(respCode.getEntity().getContent()); - - return TEST_EIDASNODEMETADATA + MESSAGE_OK; - - } catch (Exception | TransformerFactoryConfigurationError e) { - log.warn("Monitoring: Has an error in '" + TEST_EIDASNODEMETADATA + "': " + e.getMessage(), e); - throw new Exception(TEST_EIDASNODEMETADATA + MESSAGE_ERROR, e); - - } - - } - -} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/health/EidasNodeMetadataHealthIndicator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/EidasNodeMetadataHealthIndicator.java new file mode 100644 index 00000000..f160916c --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/EidasNodeMetadataHealthIndicator.java @@ -0,0 +1,69 @@ +package at.asitplus.eidas.specific.connector.health; + +import java.io.ByteArrayInputStream; + +import javax.xml.transform.TransformerFactoryConfigurationError; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; + +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.impl.data.Triple; +import at.gv.egiz.eaaf.core.impl.http.HttpUtils; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; +import at.gv.egiz.eaaf.core.impl.utils.DomUtils; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EidasNodeMetadataHealthIndicator implements HealthIndicator { + + @Autowired IConfiguration config; + @Autowired IHttpClientFactory httpClientFactory; + + @Override + public Health health() { + try { + final String urlString = config.getBasicConfiguration( + MsEidasNodeConstants.PROP_CONFIG_MONITORING_EIDASNODE_METADATAURL); + if (StringUtils.isEmpty(urlString)) { + log.trace("No eIDASNode metadata URL. Skipping test ... "); + return Health.unknown().build(); + + } + + // create HTTP client + CloseableHttpClient httpClient = httpClientFactory.getHttpClient(); + URIBuilder uriBuilder = new URIBuilder(urlString); + HttpUriRequest request = new HttpGet(uriBuilder.build()); + + final Triple<StatusLine, ByteArrayInputStream, ContentType> respCode = httpClient.execute(request, + HttpUtils.bodyStatusCodeResponseHandler()); + if (respCode.getFirst().getStatusCode() != 200) { + log.warn("Monitoring: Get http StatusCode: {} from eIDAS-Node Metadata endpoint", + respCode.getFirst().getStatusCode()); + return Health.down().withDetail("http StatusCode", respCode.getFirst().getStatusCode()).build(); + + } + + // parse metadata + DomUtils.parseXmlNonValidating(respCode.getSecond()); + + return Health.up().build(); + + } catch (Exception | TransformerFactoryConfigurationError e) { + log.warn("Monitoring: Can not read SAML2 metadata from eIDAS-Node", e); + return Health.down().down(e).build(); + + } + } + +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/health/IgniteClusterHealthIndicator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/IgniteClusterHealthIndicator.java new file mode 100644 index 00000000..10517565 --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/IgniteClusterHealthIndicator.java @@ -0,0 +1,52 @@ +package at.asitplus.eidas.specific.connector.health; + +import org.apache.ignite.Ignite; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; + +import eu.eidas.auth.cache.IgniteInstanceInitializerSpecificCommunication; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * HealthCheck that validate Nodes in Apache-Ignite Cluster. + * + * @author tlenz + * + */ +@Slf4j +public class IgniteClusterHealthIndicator implements HealthIndicator { + + @Setter + protected IgniteInstanceInitializerSpecificCommunication igniteInstanceInitializerSpecificCommunication; + + @Override + public Health health() { + final Ignite instance = igniteInstanceInitializerSpecificCommunication.getInstance(); + + // check if Apache Ignite cluster is active + if (!instance.cluster().active()) { + return Health.outOfService().build(); + + } + + final Health.Builder healthBuilder; + // Status UP requires more than 1 node because MS-Connector and eIDAS-Node operations as + // micro-services + if (instance.cluster().nodes().size() > 1) { + healthBuilder = Health.up(); + + } else { + // Something looks wrong if only a single node was found because MS-Connector and eIDAS-Node + // operations as micro-services + healthBuilder = Health.outOfService(); + + } + + healthBuilder.withDetail("#Nodes", instance.cluster().nodes().size()); + log.trace("Ignite state. #Nodes: {}", instance.cluster().nodes().size()); + return healthBuilder.build(); + + } + +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/health/Saml2MetadataHealthIndicator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/Saml2MetadataHealthIndicator.java new file mode 100644 index 00000000..592231b0 --- /dev/null +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/health/Saml2MetadataHealthIndicator.java @@ -0,0 +1,44 @@ +package at.asitplus.eidas.specific.connector.health; + +import javax.xml.transform.TransformerFactoryConfigurationError; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; + +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataConfigurationFactory; +import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpMetadataBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Saml2MetadataHealthIndicator implements HealthIndicator { + + @Autowired + private PvpMetadataBuilder metadatabuilder; + @Autowired + private IPvpMetadataConfigurationFactory configFactory; + + @Setter + private AbstractCredentialProvider pvpIdpCredentials; + + @Override + public Health health() { + try { + // build metadata + final IPvpMetadataBuilderConfiguration metadataConfig = + configFactory.generateMetadataBuilderConfiguration( + "http://localhost/monitoring", + pvpIdpCredentials); + metadatabuilder.buildPvpMetadata(metadataConfig); + return Health.up().build(); + + } catch (Exception | TransformerFactoryConfigurationError e) { + return Health.down().down(e).build(); + + } + } + +} diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/processes/tasks/GenerateCountrySelectionFrameTask.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/processes/tasks/GenerateCountrySelectionFrameTask.java index 86808f01..d3b8116a 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/processes/tasks/GenerateCountrySelectionFrameTask.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/processes/tasks/GenerateCountrySelectionFrameTask.java @@ -26,8 +26,6 @@ package at.asitplus.eidas.specific.connector.processes.tasks; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -39,7 +37,6 @@ import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.exceptions.GuiBuildException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; @@ -51,7 +48,6 @@ import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; */ @Component("GenerateCountrySelectionFrameTask") public class GenerateCountrySelectionFrameTask extends AbstractAuthServletTask { - private static final Logger log = LoggerFactory.getLogger(GenerateCountrySelectionFrameTask.class); @Autowired ISpringMvcGuiFormBuilder guiBuilder; @@ -77,16 +73,11 @@ public class GenerateCountrySelectionFrameTask extends AbstractAuthServletTask { guiBuilder.build(request, response, config, "BKU-Selection form"); - } catch (final GuiBuildException e) { - log.warn("Can not build GUI:'BKU-Selection'. Msg:" + e.getMessage()); + } catch (final Exception e) { throw new TaskExecutionException(pendingReq, "Can not build GUI. Msg:" + e.getMessage(), new EaafException("gui.00", new Object[] { e.getMessage() }, e)); - } catch (final Exception e) { - log.warn("FinalizeAuthenticationTask has an internal error", e); - throw new TaskExecutionException(pendingReq, e.getMessage(), e); - } } diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/provider/StatusMessageProvider.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/provider/StatusMessageProvider.java index d38da6fe..073f7513 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/provider/StatusMessageProvider.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/provider/StatusMessageProvider.java @@ -94,8 +94,8 @@ public class StatusMessageProvider implements IStatusMessenger { public String getResponseErrorCode(Throwable throwable) { String errorCode = IStatusMessenger.CODES_EXTERNAL_ERROR_GENERIC; if (throwable instanceof EaafException) { - errorCode = ((EaafException) throwable).getErrorId(); - + errorCode = mapInternalErrorToExternalError(((EaafException) throwable).getErrorId()); + } // TODO: maybe more internal switches are required diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/EidasCacheTransactionStoreDecorator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/EidasCacheTransactionStoreDecorator.java index 557e245a..1ea5a280 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/EidasCacheTransactionStoreDecorator.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/EidasCacheTransactionStoreDecorator.java @@ -30,18 +30,47 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; +import at.gv.egiz.eaaf.core.impl.utils.Random; -public class EidasCacheTransactionStoreDecorator implements ITransactionStorage { +public class EidasCacheTransactionStoreDecorator implements ITransactionStorage, HealthIndicator { private static final Logger log = LoggerFactory.getLogger(EidasCacheTransactionStoreDecorator.class); @Autowired(required = true) private CacheWithEidasBackend storage; @Override + public Health health() { + try { + final String key = Random.nextHexRandom16(); + final String value = Random.nextHexRandom16(); + + this.put(key, value, -1); + final String result = this.get(key, String.class); + this.remove(key); + + if (result != null && result.equals(value)) { + return Health.up().build(); + + } else { + log.warn("Montioring: TestValue: " + value + " does NOT match in Storage test"); + return Health.down().build(); + + } + + } catch (final EaafException e) { + log.warn("Montioring: Can not read/write to storage.", e); + return Health.down().down(e).build(); + + } + } + + @Override public void changeKey(String oldKey, String newKey, Object value) throws EaafException { if (containsKey(oldKey)) { final TransactionStoreElement el = storage.get(oldKey); @@ -148,5 +177,4 @@ public class EidasCacheTransactionStoreDecorator implements ITransactionStorage } } - } diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java index 26d442cb..3bda2932 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java @@ -33,13 +33,11 @@ import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; -@Service("SimpleInMemoryTransactionStorage") public class SimpleInMemoryTransactionStorage implements ITransactionStorage { private static final Logger log = LoggerFactory.getLogger(SimpleInMemoryTransactionStorage.class); |