/* * 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.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; 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.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 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() + "
"; } catch (final Exception e) { result += e.getMessage() + "
"; } try { result += testStorage() + "
"; } catch (final Exception e) { result += e.getMessage() + "
"; } try { result += testPvpMetadata() + "
"; } catch (final Exception e) { result += e.getMessage() + "
"; } try { result += testEidasNodeMetadata() + "
"; } catch (final Exception e) { result += e.getMessage() + "
"; } 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 final HttpClient httpClient = new HttpClient(); // set parameters final HttpClientParams params = new HttpClientParams(); params.setSoTimeout(5 * 1000); httpClient.setParams(params); // request URL final HttpMethod method = new GetMethod(urlString); final int respCode = httpClient.executeMethod(method); if (respCode != 200) { log.warn("Monitoring: Has an error in '" + TEST_EIDASNODEMETADATA + "': " + " HTTP responsecode: " + respCode); throw new Exception(TEST_EIDASNODEMETADATA + MESSAGE_ERROR); } // parse metadata DomUtils.parseXmlNonValidating(method.getResponseBodyAsStream()); 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); } } }