Inkers
Inkers

Reputation: 219

DestinationAccessException on keystore location for SuccessFactors destination

I am having a problem accessing a SuccessFactors destination in my application. I am trying to access the destination like:

 final HttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp();

where DESTINATION_NAME is "sfapi_dest". This destination is generated automatically using an instance of the SCP SuccessFactors Extensibility Service (Extension Factory) which creates an OAuth2SAMLBearerAssertion protected destination. The application is secured in the usual way using approuter and xsuaa service. However, when I run my application I get a DestinationAccessException on the keystore location. I am using version 3.10.0 of the SDK.

Here is the destination:

enter image description here

Earlier in the logs I can see:

"level":"DEBUG","categories":[],"msg":"Instantiated com.sap.cloud.sdk.cloudplatform.connectivity.DefaultDestination based on the following property keys: apiKey,audience,Authentication,authnContextClassRef,authTokens,certificates,clientKey,companyId,KeyStoreLocation,KeyStorePassword,Name,nameIdFormat,ProxyType,tokenServiceURL,Type,URL,WebIDEEnabled,XFSystemName" }

and then:

"log","logger":"com.sap.cloud.sdk.cloudplatform.connectivity.DestinationLoaderChain","thread":"http-nio-0.0.0.0-8080-exec-3","level":"DEBUG","categories":[],"msg":"Destination loader ScpCfDestinationLoader successfully loaded destination sfapi_dest." }

and finally:

"msg":"Servlet.service() for servlet [com.sap.cloud.sdk.sfcrud.CandidatesServlet] in context with path [] threw exception","stacktrace":["com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException: com.sap.cloud.sdk.cloudplatform.thread.exception.ThreadContextExecutionException: com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException: Failed to find key store 'sfapi_dest.p12' in destination 'sfapi_dest'.","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestAccessorFilter.doFilter(RequestAccessorFilter.java:74)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat com.sap.cloud.sdk.cloudplatform.security.servlet.HttpCachingHeaderFilter.doFilter(HttpCachingHeaderFilter.java:83)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat com.sap.cloud.sdk.cloudplatform.security.servlet.HttpSecurityHeadersFilter.doFilter(HttpSecurityHeadersFilter.java:41)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.catalina.filters.RestCsrfPreventionFilter.doFilter(RestCsrfPreventionFilter.java:125)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)","\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)","\tat org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)","\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:668)","\tat com.sap.xs.security.container.XSSecurityAuthenticator.invoke(XSSecurityAuthenticator.java:134)","\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)","\tat org.apache.tomee.catalina.OpenEJBSecurityListener$RequestCapturer.invoke(OpenEJBSecurityListener.java:97)","\tat com.sap.xs.java.valves.ErrorReportValve.invoke(ErrorReportValve.java:66)","\tat ch.qos.logback.access.tomcat.LogbackValve.invoke(LogbackValve.java:256)","\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)","\tat com.sap.xs.security.TenantIdValve.invoke(TenantIdValve.java:33)","\tat com.sap.xs.security.UserInfoValve.invoke(UserInfoValve.java:19)","\tat com.sap.xs.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:43)","\tat com.sap.xs.logging.catalina.RuntimeInfoValve.invoke(RuntimeInfoValve.java:40)","\tat org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:747)","\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)","\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:609)","\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)","\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818)","\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1623)","\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)","\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)","\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)","\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)","\tat java.lang.Thread.run(Thread.java:836)","Caused by: com.sap.cloud.sdk.cloudplatform.thread.exception.ThreadContextExecutionException: com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException: Failed to find key store 'sfapi_dest.p12' in destination 'sfapi_dest'.","\tat com.sap.cloud.sdk.cloudplatform.thread.AbstractThreadContextExecutor.execute(AbstractThreadContextExecutor.java:325)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestAccessorFilter.doFilter(RequestAccessorFilter.java:71)","\t... 36 more","Caused by: com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException: Failed to find key store 'sfapi_dest.p12' in destination 'sfapi_dest'.","\tat com.sap.cloud.sdk.cloudplatform.connectivity.ScpCfHttpDestinationPropertyFactory.getKeyStore(ScpCfHttpDestinationPropertyFactory.java:432)","\tat com.sap.cloud.sdk.cloudplatform.connectivity.ScpCfHttpDestination.lambda$new$0(ScpCfHttpDestination.java:146)","\tat io.vavr.control.Option.orElse(Option.java:321)","\tat com.sap.cloud.sdk.cloudplatform.connectivity.ScpCfHttpDestination.(ScpCfHttpDestination.java:145)","\tat com.sap.cloud.sdk.cloudplatform.connectivity.ScpCfHttpDestination.(ScpCfHttpDestination.java:79)","\tat com.sap.cloud.sdk.cloudplatform.connectivity.ScpCfDestination.asHttp(ScpCfDestination.java:49)","\tat com.sap.cloud.sdk.sfcrud.CandidatesServlet.doGet(CandidatesServlet.java:38)","\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:634)","\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.openejb.server.httpd.EEFilter.doFilter(EEFilter.java:65)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestAccessorFilter.lambda$doFilter$1(RequestAccessorFilter.java:71)","\tat com.sap.cloud.sdk.cloudplatform.thread.AbstractThreadContextExecutor.lambda$execute$0(AbstractThreadContextExecutor.java:317)","\tat com.sap.cloud.sdk.cloudplatform.thread.ThreadContextCallable.call(ThreadContextCallable.java:247)","\tat com.sap.cloud.sdk.cloudplatform.thread.AbstractThreadContextExecutor.execute(AbstractThreadContextExecutor.java:319)","\t... 37 more"] }

I can see the KeyStoreLocation in "sensitive information" of the instance of the SuccessFactors Extensibility Service but this information does not seem to be added to the automatically generated destination or at least it is not visible. Any idea why it fails to find the keystore location in the DestinationAccessor request? Thanks for your time.

Adding image: enter image description here

Upvotes: 1

Views: 767

Answers (2)

Alexander Dümont
Alexander Dümont

Reputation: 938

We did not expect the certificate "type" property to be empty (null) for a destination configuration, as described in your comment. The official documentation only ever mentioned "CERTIFICATE" as value.

Until the empty value can be handled in one of the next releases of the SAP Cloud SDK, please use the following workaround.

private static ScpCfHttpDestination fixKeyStore( final DestinationProperties destination )
{
    final URI uri =
        destination.get("URL", String.class).map(URI::create).getOrElseThrow(
            () -> new IllegalArgumentException("Invalid or missing \"URL\"."));

    final String name =
        destination.get("Name", String.class).getOrElseThrow(
            () -> new IllegalArgumentException("Invalid or missing \"Name\"."));

    final ScpCfHttpDestination.Builder builder = ScpCfHttpDestination.builder(name, uri);
    destination.getPropertyNames().forEach(
        propertyName -> builder.property(propertyName, destination.get(propertyName).getOrNull()));

    final Pattern pattern = Pattern.compile(".*name=(.*?)\\.(.*?), content=(.*?), type=(.*?)\\).*");
    final List<?> rawCertificates =
        destination.get("certificates", List.class).getOrElseThrow(
            () -> new IllegalArgumentException("Invalid or missing \"certificates\"."));

    rawCertificates
        .stream()
        .map(Object::toString)
        .map(pattern::matcher)
        .filter(Matcher::matches)

        // Sanity check: only apply fix in the use-case defined.
        .filter(m -> "null".equalsIgnoreCase(m.group(4)))
        .filter(m -> "p12".equalsIgnoreCase(m.group(2)))
        .map(
            m -> Try.withResources(() -> new ByteArrayInputStream(Base64.getDecoder().decode(m.group(3)))).of(
                inputStream -> {
                    final KeyStore ks = KeyStore.getInstance("PKCS12");
                    final Option<String> keyStorePassword = destination.get("KeyStorePassword", String.class);
                    ks.load(inputStream, keyStorePassword.map(String::toCharArray).getOrElse(new char[0]));
                    return ks;
                }))

        .filter(Try::isSuccess)
        .map(Try::get)
        .findFirst()
        .ifPresent(builder::keyStore);

    return builder.build();
}
  1. Put the static fixKeyStore method into your project, e.g. inside CandidatesServlet.
  2. Instead of calling asHttp use decorate(CandidatesServlet::fixKeyStore), e.g.
    final HttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).decorate(CandidatesServlet::fixKeyStore);
    

Upvotes: 1

Emdee
Emdee

Reputation: 1693

As per the error stack trace I can derive that the SDK tries to access a key store and fails. It does so as it sees the destination properties KeyStoreLocation and KeyStorePassword. Strange is that these properties appear to be present, even though we cannot see them on the screenshot somewhere.

Guessing: Do you have accidentally or sometime in the past provided a key store and a password and for some reason the destination still has these properties set?

Upvotes: 1

Related Questions