Skip to content

Commit

Permalink
FDP-94: Processed Sonar comments
Browse files Browse the repository at this point in the history
Signed-off-by: Sander Verbruggen <[email protected]>
  • Loading branch information
sanderv committed Nov 15, 2023
1 parent 24037a1 commit c9fb945
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class SigningService {

private static final Logger LOGGER = LoggerFactory.getLogger(SigningService.class);

private SigningService(final SecurityConfigurationProperties securityConfiguration) {
public SigningService(final SecurityConfigurationProperties securityConfiguration) {
signingConfiguration = securityConfiguration.getSigning();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,30 @@
// SPDX-License-Identifier: Apache-2.0
package org.gxf.soapbridge.soap.endpoints;

import static org.springframework.security.web.context.RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME;

import jakarta.annotation.PostConstruct;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.ldap.Rdn;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.*;
import org.gxf.soapbridge.application.services.ConnectionCacheService;
import org.gxf.soapbridge.application.services.SigningService;
import org.gxf.soapbridge.configuration.properties.SoapConfigurationProperties;
import org.gxf.soapbridge.kafka.senders.ProxyRequestKafkaSender;
import org.gxf.soapbridge.soap.clients.Connection;
import org.gxf.soapbridge.soap.exceptions.ConnectionNotFoundInCacheException;
import org.gxf.soapbridge.soap.exceptions.ProxyServerException;
import org.gxf.soapbridge.soap.valueobjects.ClientCertificate;
import org.gxf.soapbridge.valueobjects.ProxyServerRequestMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestHandler;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
* This {@link @Component} class is the endpoint for incoming SOAP requests from client
Expand All @@ -51,36 +37,35 @@ public class SoapEndpoint implements HttpRequestHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(SoapEndpoint.class);

private static final String SOAP_HEADER_KEY_APPLICATION_NAME = "ApplicationName";
private static final String SOAP_HEADER_KEY_ORGANISATION_IDENTIFICATION =
"OrganisationIdentification";
private static final String SOAP_HEADER_KEY_USER_NAME = "UserName";
private static final List<String> SOAP_HEADER_KEYS =
List.of(
SOAP_HEADER_KEY_APPLICATION_NAME,
SOAP_HEADER_KEY_ORGANISATION_IDENTIFICATION,
SOAP_HEADER_KEY_USER_NAME);

private static final String URL_PROXY_SERVER = "/proxy-server";

private static final int INVALID_CUSTOM_TIME_OUT = -1;
public static final String X_509_CERTIFICATE_REQUEST_ATTRIBUTE =
"jakarta.servlet.request.X509Certificate";

/** Service used to cache incoming connections from client applications. */
@Autowired private ConnectionCacheService connectionCacheService;
private final ConnectionCacheService connectionCacheService;

@Autowired private SoapConfigurationProperties soapConfiguration;
private final SoapConfigurationProperties soapConfiguration;

/** Message sender which can send a webapp request message to ActiveMQ. */
@Autowired private ProxyRequestKafkaSender proxyRequestsSender;
private final ProxyRequestKafkaSender proxyRequestsSender;

/** Service used to sign the content of a message. */
@Autowired private SigningService signingService;
private final SigningService signingService;

/** Map of time outs for specific functions. */
/** Map of time-outs for specific functions. */
private final Map<String, Integer> customTimeOutsMap = new HashMap<>();

public SoapEndpoint(
final ConnectionCacheService connectionCacheService,
final SoapConfigurationProperties soapConfiguration,
final ProxyRequestKafkaSender proxyRequestsSender,
final SigningService signingService) {
this.connectionCacheService = connectionCacheService;
this.soapConfiguration = soapConfiguration;
this.proxyRequestsSender = proxyRequestsSender;
this.signingService = signingService;
}

@PostConstruct
public void init() {
final String[] split = soapConfiguration.getCustomTimeouts().split(",");
Expand Down Expand Up @@ -117,11 +102,10 @@ public void handleRequest(final HttpServletRequest request, final HttpServletRes
}

String organisationName = null;
if (request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME)
instanceof final SecurityContext securityContext) {
if (securityContext.getAuthentication().getPrincipal() instanceof final User organisation) {
organisationName = organisation.getUsername();
}
if (request.getAttribute(DEFAULT_REQUEST_ATTR_NAME)
instanceof final SecurityContext securityContext
&& securityContext.getAuthentication().getPrincipal() instanceof final User organisation) {
organisationName = organisation.getUsername();
}
if (organisationName == null) {
LOGGER.error("Unable to find client certificate, returning 500.");
Expand Down Expand Up @@ -166,10 +150,11 @@ public void handleRequest(final HttpServletRequest request, final HttpServletRes
connectionCacheService.removeConnection(connectionId);
return;
}
} catch (final Exception e) {
LOGGER.info("Error while waiting for response", e);
} catch (final InterruptedException e) {
LOGGER.error("Error while waiting for response", e);
createErrorResponse(response);
connectionCacheService.removeConnection(connectionId);
Thread.currentThread().interrupt();
return;
}

Expand All @@ -186,10 +171,6 @@ public void handleRequest(final HttpServletRequest request, final HttpServletRes
"End of SoapEndpoint.handleRequest() --> incoming request handled and response returned.");
}

public String[] sander() {
return new String[] {"a", "poipoi", "frats"};
}

private void printHeaderValues(final HttpServletRequest request) {
if (LOGGER.isDebugEnabled()) {
for (final Enumeration<String> headerNames = request.getHeaderNames();
Expand Down Expand Up @@ -237,155 +218,6 @@ private String readSoapPayload(final HttpServletRequest request) {
return soapPayload;
}

private Map<String, String> getSoapHeaderValues(
final String soapPayload, final List<String> soapHeaderKeys) {
final Map<String, String> values = new HashMap<>();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
factory.setNamespaceAware(false);
final InputStream inputStream =
new ByteArrayInputStream(soapPayload.getBytes(StandardCharsets.UTF_8));
// Try to find the desired XML elements in the document.
final Document document = factory.newDocumentBuilder().parse(inputStream);
for (final String soapHeaderKey : soapHeaderKeys) {
final String value = evaluateXPathExpression(document, soapHeaderKey);
values.put(soapHeaderKey, value);
}
inputStream.close();
} catch (final Exception e) {
LOGGER.error("Exception", e);
}
return values;
}

/**
* Search an XML element using an XPath expression.
*
* @param document The XML document.
* @param element The name of the desired XML element.
* @return The content of the XML element, or null if the element is not found.
* @throws XPathExpressionException In case the expression fails to compile or evaluate, an
* exception will be thrown.
*/
private String evaluateXPathExpression(final Document document, final String element)
throws XPathExpressionException {
final String expression = String.format("//*[contains(local-name(), '%s')]", element);

final XPathFactory xFactory = XPathFactory.newInstance();
final XPath xPath = xFactory.newXPath();

final XPathExpression xPathExpression = xPath.compile(expression);
final NodeList nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);

if (nodeList != null && nodeList.getLength() > 0) {
return nodeList.item(0).getTextContent();
} else {
return null;
}
}

private ClientCertificate tryToFindClientCertificate(
final HttpServletRequest request, final String soapPayload) {
final X509Certificate[] x509Certificates = getX509CertificatesFromServlet(request);
ClientCertificate clientCertificate = null;

if (x509Certificates.length == 0) {
LOGGER.error(" HTTPServletRequest's attribute was an empty array of X509Certificates.");
} else if (x509Certificates.length == 1) {
LOGGER.debug(" HTTPServletRequest's attribute was array of X509Certificates of length 1.");

// Get the client certificate.
clientCertificate = getClientCertificate(x509Certificates[0]);
} else {
LOGGER.debug(
" HTTPServletRequest's attribute was array of X509Certificates of length {}.",
x509Certificates.length);

final Map<String, String> soapHeaderValues =
getSoapHeaderValues(soapPayload, SOAP_HEADER_KEYS);
LOGGER.debug(
" SOAP Header ApplicationName: {}",
soapHeaderValues.get(SOAP_HEADER_KEY_APPLICATION_NAME));
LOGGER.debug(
" SOAP Header OrganisationIdentification: {}",
soapHeaderValues.get(SOAP_HEADER_KEY_ORGANISATION_IDENTIFICATION));
LOGGER.debug(" SOAP Header UserName: {}", soapHeaderValues.get(SOAP_HEADER_KEY_USER_NAME));

// Try to extract the client certificate for the organization
// identification.
clientCertificate =
getClientCertificateByOrganisationIdentification(
x509Certificates, soapHeaderValues.get(SOAP_HEADER_KEY_ORGANISATION_IDENTIFICATION));
}

return clientCertificate;
}

private X509Certificate[] getX509CertificatesFromServlet(final HttpServletRequest request) {
final Object x509CertificateAttribute =
request.getAttribute(X_509_CERTIFICATE_REQUEST_ATTRIBUTE);
LOGGER.debug(X_509_CERTIFICATE_REQUEST_ATTRIBUTE + ": {}", x509CertificateAttribute);
if (x509CertificateAttribute instanceof X509Certificate[]) {
LOGGER.debug(" x509CertificateAttribute instanceof X509Certificate[]");
return (X509Certificate[]) x509CertificateAttribute;
} else {
return new X509Certificate[0];
}
}

private ClientCertificate getClientCertificateByOrganisationIdentification(
final X509Certificate[] array, final String organisationCommonName) {

for (final X509Certificate x509Certificate : array) {
final Principal principal = x509Certificate.getSubjectDN();
LOGGER.debug(" principal: {}", principal);
final String subjectDn = principal.getName();
LOGGER.debug(" subjectDn: {}", subjectDn);
try {
final String commonName = getCommonName(subjectDn);
if (commonName.equals(organisationCommonName)) {
LOGGER.debug(
"Found client certificate for right organisation {}", organisationCommonName);
return new ClientCertificate(x509Certificate, commonName);
}
} catch (final NamingException e) {
LOGGER.info("Failed to extract CommonName from this ClientCertificate", e);
}
}
return null;
}

private ClientCertificate getClientCertificate(final X509Certificate x509Certificate) {
ClientCertificate clientCertificate = null;

final Principal principal = x509Certificate.getSubjectDN();
LOGGER.debug(" principal: {}", principal);
final String subjectDn = principal.getName();
LOGGER.debug(" subjectDn: {}", subjectDn);
try {
final String commonName = getCommonName(subjectDn);
clientCertificate = new ClientCertificate(x509Certificate, commonName);
} catch (final NamingException e) {
LOGGER.info("Failed to extract CommonName from ClientCertificate", e);
}

return clientCertificate;
}

private String getCommonName(final String subjectDn) throws NamingException {
final Rdn rdn = new Rdn(subjectDn);
LOGGER.debug(" rdn: {}", rdn);
final Attributes attributes = rdn.toAttributes();
LOGGER.debug(" attributes: {}", attributes);
final Attribute attribute = attributes.get("cn");
LOGGER.debug(" attribute: {}", attribute);
final String commonName = (String) attribute.get();
LOGGER.debug(" common name: {}", commonName);
return commonName;
}

private Integer shouldUseCustomTimeOut(final String soapPayload) {
final Set<String> keys = customTimeOutsMap.keySet();
for (final String key : keys) {
Expand Down

0 comments on commit c9fb945

Please sign in to comment.