From 69d612621915cf9793b5e8c5a4e9ec42187a557d Mon Sep 17 00:00:00 2001 From: Vidyashree Rama Date: Thu, 25 Oct 2018 14:50:31 +0530 Subject: [PATCH] Add authorization header in SSE request Add authorization header in SSE request to support https Issue-ID: CCSDK-628 Change-Id: I031cbed94e21e1f6d90bc0b00b2d70905d47bd69 Signed-off-by: Vidyashree Rama --- .../sli/plugins/restapicall/RestapiCallNode.java | 2 +- .../AdditionalHeaderWebTarget.java | 162 +++++++++++++++++++++ .../restconfdiscovery/RestconfDiscoveryNode.java | 75 +++++++++- .../plugins/restconfdiscovery/SseServerMock.java | 2 +- .../TestRestconfDiscoveryNode.java | 21 +++ 5 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java diff --git a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java index 9a89af6b..39399a25 100644 --- a/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java +++ b/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java @@ -543,7 +543,7 @@ public class RestapiCallNode implements SvcLogicJavaPlugin { return addAuthType(c, p); } - protected Client addAuthType(Client client, Parameters p) throws SvcLogicException { + public Client addAuthType(Client client, Parameters p) throws SvcLogicException { if (p.authtype == AuthType.Unspecified) { if (p.restapiUser != null && p.restapiPassword != null) { client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword)); diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java new file mode 100644 index 00000000..1a0ddbc5 --- /dev/null +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java @@ -0,0 +1,162 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.plugins.restconfdiscovery; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.util.Map; + +/** + * Adds additional headers for SSE request. + */ +class AdditionalHeaderWebTarget implements WebTarget { + private WebTarget base; + private String token; + + public AdditionalHeaderWebTarget(WebTarget target, String token) { + base = target; + this.token = token; + } + + @Override + public Invocation.Builder request() { + return base.request().header("X-ACCESS-TOKEN", token); + } + + @Override + public Invocation.Builder request(String... acceptedResponseTypes) { + return base.request().header("X-ACCESS-TOKEN", token); + } + + @Override + public Invocation.Builder request(MediaType... acceptedResponseTypes) { + return base.request().header("X-ACCESS-TOKEN", token); + } + + @Override + public Configuration getConfiguration() { + return base.getConfiguration(); + } + + @Override + public URI getUri() { + return base.getUri(); + } + + @Override + public UriBuilder getUriBuilder() { + return base.getUriBuilder(); + } + + @Override + public WebTarget path(String path) { + return base.path(path); + } + + @Override + public WebTarget resolveTemplate(String name, Object value) { + return base.resolveTemplate(name, value); + } + + @Override + public WebTarget resolveTemplate(String name, Object value, boolean encodeSlashInPath) { + return base.resolveTemplate(name, value, encodeSlashInPath); + } + + @Override + public WebTarget resolveTemplateFromEncoded(String name, Object value) { + return base.resolveTemplateFromEncoded(name, value); + } + + @Override + public WebTarget resolveTemplates(Map templateValues) { + return base.resolveTemplates(templateValues); + } + + @Override + public WebTarget resolveTemplates(Map templateValues, boolean encodeSlashInPath) { + return base.resolveTemplates(templateValues, encodeSlashInPath); + } + + @Override + public WebTarget resolveTemplatesFromEncoded(Map templateValues) { + return base.resolveTemplatesFromEncoded(templateValues); + } + + @Override + public WebTarget matrixParam(String name, Object... values) { + return base.matrixParam(name, values); + } + + @Override + public WebTarget queryParam(String name, Object... values) { + return base.queryParam(name, values); + } + + @Override + public WebTarget property(String name, Object value) { + return base.property(name, value); + } + + @Override + public WebTarget register(Class componentClass) { + return base.register(componentClass); + } + + @Override + public WebTarget register(Class componentClass, int priority) { + return base.register(componentClass, priority); + } + + @Override + public WebTarget register(Class componentClass, Class... contracts) { + return base.register(componentClass, contracts); + } + + @Override + public WebTarget register(Class componentClass, Map, Integer> contracts) { + return base.register(componentClass, contracts); + } + + @Override + public WebTarget register(Object component) { + return base.register(component); + } + + @Override + public WebTarget register(Object component, int priority) { + return base.register(component, priority); + } + + @Override + public WebTarget register(Object component, Class... contracts) { + return base.register(component, contracts); + } + + @Override + public WebTarget register(Object component, Map, Integer> contracts) { + return base.register(component, contracts); + } +} diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java index 111d6284..5c7d695d 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java @@ -24,12 +24,21 @@ import org.glassfish.jersey.media.sse.EventSource; import org.glassfish.jersey.media.sse.SseFeature; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.Parameters; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; import org.slf4j.Logger; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -115,9 +124,11 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { class PersistentConnection implements Runnable { private String url; private volatile boolean running = true; + private Map paramMap; - PersistentConnection(String url) { + PersistentConnection(String url, Map paramMap) { this.url = url; + this.paramMap = paramMap; } private void terminate() { @@ -126,15 +137,26 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { @Override public void run() { - Client client = ClientBuilder.newBuilder() - .register(SseFeature.class).build(); - WebTarget target = client.target(url); + Parameters p; + WebTarget target = null; + try { + RestapiCallNode restapi = restconfApiCallNode.getRestapiCallNode(); + p = restapi.getParameters(paramMap, new Parameters()); + Client client = ignoreSslClient().register(SseFeature.class); + target = restapi.addAuthType(client, p).target(url); + } catch (SvcLogicException e) { + log.error("Exception occured!", e); + Thread.currentThread().interrupt(); + } + + target = addToken(target, paramMap.get("customHttpHeaders")); EventSource eventSource = EventSource.target(target).build(); eventSource.register(new EventHandler(RestconfDiscoveryNode.this)); eventSource.open(); log.info("Connected to SSE source"); while (running) { try { + log.info("SSE state " + eventSource.isOpen()); Thread.sleep(5000); } catch (InterruptedException e) { log.error("Interrupted!", e); @@ -146,6 +168,49 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { } } + private Client ignoreSslClient() { + SSLContext sslcontext = null; + + try { + sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, new TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } }, new java.security.SecureRandom()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IllegalStateException(e); + } + + return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build(); + } + + protected String getTokenId(String customHttpHeaders) { + if (customHttpHeaders.contains("=")) { + String s[] = customHttpHeaders.split("="); + return s[1]; + } + return customHttpHeaders; + } + + protected WebTarget addToken(WebTarget target, String customHttpHeaders) { + if (customHttpHeaders == null) { + return target; + } + + return new AdditionalHeaderWebTarget( + target, getTokenId(customHttpHeaders)); + } + /** * Establishes a persistent between the client and server. * @@ -167,7 +232,7 @@ public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { subscriptionInfoMap.put(id, info); String url = paramMap.get(SSE_URL); - PersistentConnection connection = new PersistentConnection(url); + PersistentConnection connection = new PersistentConnection(url, paramMap); runnableInfo.put(id, connection); executor.execute(connection); } diff --git a/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java b/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java index 1b234a23..35ac221f 100644 --- a/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java +++ b/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java @@ -35,7 +35,7 @@ public class SseServerMock { @Produces(SseFeature.SERVER_SENT_EVENTS) public EventOutput getServerSentEvents() throws IOException { String data = "{" + - "\"ietf-notification:notification\" : {" + + "\"ietf-restconf:notification\" : {" + " \"eventTime\" : \"2017-10-25T08:22:33.44Z\"," + " \"ietf-yang-push:push-change-update\": {" + "\"subscription-id\":\"89\"," + diff --git a/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java b/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java index b2094ca7..af354e9b 100644 --- a/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java +++ b/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java @@ -57,6 +57,13 @@ public class TestRestconfDiscoveryNode { p.put("sseConnectURL", "http://localhost:8080/events"); p.put("subscriberId", "networkId"); p.put("responsePrefix", "restapi-result"); + p.put("restapiUser", "access"); + p.put("restapiPassword", "abc@123"); + p.put("customHttpHeaders", "X-ACCESS-TOKEN=x-ik2ps4ikvzupbx0486ft" + + "1ebzs7rt85futh9ho6eofy3wjsap7wqktemlqm4bbsmnar3vrtbyrzuk" + + "bv5itd6m1cftldpjarnyle3sdcqq9hftc4lebz464b5ffxmlbvg9"); + p.put("restapiUrl", "https://localhost:8080/restconf/operations/" + + "ietf-subscribed-notifications:establish-subscription"); RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( new RestconfApiCallNode(new RestapiCallNode())); rdn.establishPersistentConnection(p, ctx, "networkId"); @@ -111,4 +118,18 @@ public class TestRestconfDiscoveryNode { assertThat(rdn.getOutputIdentifier("restapi-result", ctx), is("89")); } + + @Test + public void testGetTokenId() { + String customHttpHeaders = "X-ACCESS-TOKEN=x-ik2ps4ikvzupbx0486ft1ebzs7rt85" + + "futh9ho6eofy3wjsap7wqktemlqm4bbsmnar3vrtbyrzukbv5itd6m1cftldpjarny" + + "le3sdcqq9hftc4lebz464b5ffxmlbvg9"; + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( + new RestconfApiCallNode(new RestapiCallNode())); + + assertThat(rdn.getTokenId(customHttpHeaders), + is("x-ik2ps4ikvzupbx0486ft1ebzs7rt85futh9ho6eofy3wjsap7wqkt" + + "emlqm4bbsmnar3vrtbyrzukbv5itd6m1cftldpjarnyle3sdcqq9h" + + "ftc4lebz464b5ffxmlbvg9")); + } } -- 2.16.6