Add oauth for calls from VNFM to VNFM adapter 06/92506/1
authorMichaelMorris <michael.morris@est.tech>
Thu, 1 Aug 2019 21:57:24 +0000 (21:57 +0000)
committerMichaelMorris <michael.morris@est.tech>
Thu, 1 Aug 2019 21:57:24 +0000 (21:57 +0000)
Change-Id: I6ff1f9ed130b8331f6a001d3dcb64b9dcfb30eb5
Issue-ID: SO-2180
Signed-off-by: MichaelMorris <michael.morris@est.tech>
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/MessageConverterConfiguration.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/WebSecurityConfigImpl.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHelper.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java
vnfm-simulator/vnfm-service/src/main/java/org/onap/svnfm/simulator/services/OperationProgressor.java

index d99b688..32c2235 100644 (file)
 package org.onap.so.adapters.vnfmadapter;
 
 import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import java.util.ArrayList;
 import java.util.Collection;
-import org.onap.so.adapters.vnfmadapter.extclients.vnfm.lcn.JSON;
+import org.onap.so.adapters.vnfmadapter.oauth.OAuth2AccessTokenAdapter;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
 
 /**
  * Configures message converter
@@ -38,7 +40,8 @@ public class MessageConverterConfiguration {
     @Bean
     public HttpMessageConverters customConverters() {
         final Collection<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
-        final Gson gson = new JSON().getGson();
+        final Gson gson = new GsonBuilder()
+                .registerTypeHierarchyAdapter(OAuth2AccessToken.class, new OAuth2AccessTokenAdapter()).create();
         final GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter(gson);
         messageConverters.add(gsonHttpMessageConverter);
         return new HttpMessageConverters(true, messageConverters);
index f083013..2b33e8b 100644 (file)
@@ -36,11 +36,9 @@ public class WebSecurityConfigImpl extends WebSecurityConfig {
 
     @Override
     protected void configure(final HttpSecurity http) throws Exception {
-        http.csrf().disable().authorizeRequests()
-                .antMatchers("/manage/health", "/manage/info", Constants.BASE_URL + "/lcn/**",
-                        Constants.BASE_URL + "/grants/**")
-                .permitAll().antMatchers("/**").hasAnyRole(StringUtils.collectionToDelimitedString(getRoles(), ","))
-                .and().httpBasic();
+        http.csrf().disable().authorizeRequests().antMatchers("/manage/health", "/manage/info").permitAll()
+                .antMatchers("/**").hasAnyRole(StringUtils.collectionToDelimitedString(getRoles(), ",")).and()
+                .httpBasic();
     }
 
     @Override
index 249cf74..b4355ef 100644 (file)
@@ -41,6 +41,7 @@ import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.LccnSubscriptionRe
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthentication;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthentication.AuthTypeEnum;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsBasic;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsOauth2ClientCredentials;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilter;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilter.NotificationTypesEnum;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsFilterVnfInstanceSubscriptionFilter;
@@ -194,12 +195,21 @@ public class VnfmHelper {
     }
 
     private SubscriptionsAuthentication getSubscriptionsAuthentication() throws GeneralSecurityException {
-        final SubscriptionsAuthenticationParamsBasic basicAuthParams = new SubscriptionsAuthenticationParamsBasic();
+        final SubscriptionsAuthentication authentication = new SubscriptionsAuthentication();
+
         final String[] decrypedAuth = CryptoUtils.decrypt(vnfmAdapterAuth, msoEncryptionKey).split(":");
+
+        SubscriptionsAuthenticationParamsOauth2ClientCredentials oauthParams =
+                new SubscriptionsAuthenticationParamsOauth2ClientCredentials();
+        oauthParams.setTokenEndpoint(vnfmAdapterEndoint + "/oauth/token");
+        oauthParams.clientId(decrypedAuth[0]);
+        oauthParams.setClientPassword(decrypedAuth[1]);
+        authentication.addAuthTypeItem(AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS);
+        authentication.paramsOauth2ClientCredentials(oauthParams);
+
+        final SubscriptionsAuthenticationParamsBasic basicAuthParams = new SubscriptionsAuthenticationParamsBasic();
         basicAuthParams.setUserName(decrypedAuth[0]);
         basicAuthParams.setPassword(decrypedAuth[1]);
-
-        final SubscriptionsAuthentication authentication = new SubscriptionsAuthentication();
         authentication.addAuthTypeItem(AuthTypeEnum.BASIC);
         authentication.paramsBasic(basicAuthParams);
         return authentication;
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/AuthorizationServerConfig.java
new file mode 100644 (file)
index 0000000..7f71b2e
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import org.onap.so.utils.CryptoUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+
+@Configuration
+@EnableAuthorizationServer
+/**
+ * Configures the authorization server for oauth token based authentication.
+ */
+public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
+
+    private static final int ONE_DAY = 60 * 60 * 24;
+
+    @Value("${vnfmadapter.auth:E39823AAB2739CC654C4E92B52C05BC34149342D0A46451B00CA508C8EDC62242CE4E9DA9445D3C01A3F13}")
+    private String vnfmAdapterAuth;
+
+    @Value("${mso.key}")
+    private String msoEncryptionKey;
+
+    @Override
+    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
+        final String[] decrypedAuth = CryptoUtils.decrypt(vnfmAdapterAuth, msoEncryptionKey).split(":");
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        clients.inMemory().withClient(decrypedAuth[0]).secret(passwordEncoder.encode(decrypedAuth[1]))
+                .authorizedGrantTypes("client_credentials").scopes("write").accessTokenValiditySeconds(ONE_DAY)
+                .refreshTokenValiditySeconds(ONE_DAY);
+    }
+
+}
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2AccessTokenAdapter.java
new file mode 100644 (file)
index 0000000..2f51406
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+
+public class OAuth2AccessTokenAdapter implements JsonSerializer<OAuth2AccessToken> {
+
+    @Override
+    public JsonElement serialize(final OAuth2AccessToken src, final Type typeOfSrc,
+            final JsonSerializationContext context) {
+        final JsonObject obj = new JsonObject();
+        obj.addProperty(OAuth2AccessToken.ACCESS_TOKEN, src.getValue());
+        obj.addProperty(OAuth2AccessToken.TOKEN_TYPE, src.getTokenType());
+        if (src.getRefreshToken() != null) {
+            obj.addProperty(OAuth2AccessToken.REFRESH_TOKEN, src.getRefreshToken().getValue());
+        }
+        obj.addProperty(OAuth2AccessToken.EXPIRES_IN, src.getExpiresIn());
+        final JsonArray scopeObj = new JsonArray();
+        for (final String scope : src.getScope()) {
+            scopeObj.add(scope);
+        }
+        obj.add(OAuth2AccessToken.SCOPE, scopeObj);
+
+        return obj;
+    }
+}
diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/oauth/OAuth2ResourceServer.java
new file mode 100644 (file)
index 0000000..1f0594e
--- /dev/null
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.oauth;
+
+import javax.servlet.http.HttpServletRequest;
+import org.onap.so.adapters.vnfmadapter.Constants;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+@Configuration
+@EnableResourceServer
+/**
+ * Enforces oauth token based authentication when a token is provided in the request.
+ */
+public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
+
+    @Override
+    public void configure(final HttpSecurity http) throws Exception {
+        http.requestMatcher(new OAuth2ResourceServerRequestMatcher()).authorizeRequests()
+                .antMatchers(Constants.BASE_URL + "/grants/**", Constants.BASE_URL + "/lcn/**").authenticated();
+    }
+
+    private static class OAuth2ResourceServerRequestMatcher implements RequestMatcher {
+        @Override
+        public boolean matches(HttpServletRequest request) {
+            String auth = request.getHeader("Authorization");
+            String uri = request.getRequestURI();
+            return (auth != null && auth.startsWith("Bearer") && (uri.contains("/grants") || uri.contains("/lcn/")));
+        }
+    }
+}
index b45a133..6cdabb9 100644 (file)
@@ -133,7 +133,7 @@ public class VnfmAdapterControllerTest {
         setUpVimInMockAai();
 
         final String expectedsubscriptionRequest =
-                "{\"filter\":{\"vnfInstanceSubscriptionFilter\":{\"vnfInstanceIds\":[\"vnfId\"]},\"notificationTypes\":[\"VnfLcmOperationOccurrenceNotification\"]},\"callbackUri\":\"https://so-vnfm-adapter.onap:30406/so/vnfm-adapter/v1/lcn/VnfLcmOperationOccurrenceNotification\",\"authentication\":{\"authType\":[\"BASIC\"],\"paramsBasic\":{\"userName\":\"vnfm\",\"password\":\"password1$\"}}}";
+                "{\"filter\":{\"vnfInstanceSubscriptionFilter\":{\"vnfInstanceIds\":[\"vnfId\"]},\"notificationTypes\":[\"VnfLcmOperationOccurrenceNotification\"]},\"callbackUri\":\"https://so-vnfm-adapter.onap:30406/so/vnfm-adapter/v1/lcn/VnfLcmOperationOccurrenceNotification\",\"authentication\":{\"authType\":[\"OAUTH2_CLIENT_CREDENTIALS\", \"BASIC\"],\"paramsOauth2ClientCredentials\":{\"clientId\":\"vnfm\",\"clientPassword\":\"password1$\",\"tokenEndpoint\":\"https://so-vnfm-adapter.onap:30406/oauth/token\"},\"paramsBasic\":{\"userName\":\"vnfm\",\"password\":\"password1$\"}}}";
         final InlineResponse2001 subscriptionResponse = new InlineResponse2001();
 
         final InlineResponse201 createResponse = createCreateResponse();
index 83f079c..eed6278 100644 (file)
@@ -1,11 +1,17 @@
 package org.onap.svnfm.simulator.services;
 
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import javax.net.ssl.HttpsURLConnection;
 import javax.ws.rs.core.MediaType;
 import org.apache.commons.codec.binary.Base64;
 import org.modelmapper.ModelMapper;
@@ -30,6 +36,7 @@ import org.onap.so.adapters.vnfmadapter.extclients.vnfm.lcn.model.VnfLcmOperatio
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201InstantiatedVnfInfoVnfcResourceInfo;
 import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsBasic;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.SubscriptionsAuthenticationParamsOauth2ClientCredentials;
 import org.onap.svnfm.simulator.config.ApplicationConfig;
 import org.onap.svnfm.simulator.model.VnfOperation;
 import org.onap.svnfm.simulator.model.Vnfds;
@@ -187,7 +194,8 @@ public abstract class OperationProgressor implements Runnable {
             final String auth =
                     subscriptionAuthentication.getUserName() + ":" + subscriptionAuthentication.getPassword();
             final byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1));
-            final String authHeader = "Basic " + new String(encodedAuth);
+            String authHeader = "Basic " + new String(encodedAuth);
+
             notificationClient.lcnVnfLcmOperationOccurrenceNotificationPostWithHttpInfo(notification,
                     MediaType.APPLICATION_JSON, authHeader);
         } catch (final ApiException exception) {
@@ -235,8 +243,15 @@ public abstract class OperationProgressor implements Runnable {
     private InlineResponse201 sendGrantRequest(final GrantRequest grantRequest) {
         LOGGER.info("Sending grant request: {}", grantRequest);
         try {
+
+            final SubscriptionsAuthenticationParamsOauth2ClientCredentials subscriptionAuthentication =
+                    subscriptionService.getSubscriptions().iterator().next().getAuthentication()
+                            .getParamsOauth2ClientCredentials();
+            final String authHeader =
+                    "Bearer " + getToken(notificationClient.getApiClient(), subscriptionAuthentication);
+
             final ApiResponse<InlineResponse201> response = grantClient.grantsPostWithHttpInfo(grantRequest,
-                    MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON, "Basic dm5mbTpwYXNzd29yZDEk");
+                    MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON, authHeader);
             LOGGER.info("Grant Response: {}", response);
             return response.getData();
         } catch (final org.onap.so.adapters.vnfmadapter.extclients.vnfm.grant.ApiException exception) {
@@ -257,4 +272,46 @@ public abstract class OperationProgressor implements Runnable {
         return applicationConfig.getBaseUrl() + "/vnflcm/v1";
     }
 
+    private String getToken(final ApiClient apiClient,
+            final SubscriptionsAuthenticationParamsOauth2ClientCredentials oauthClientCredentials) {
+        final String basePath = apiClient.getBasePath().substring(0, apiClient.getBasePath().indexOf("/so/"));
+        final String tokenUrl = basePath + "/oauth/token?grant_type=client_credentials";
+
+        try {
+            URL url = new URL(tokenUrl);
+            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+            connection.setRequestMethod("POST");
+            final String authorizationHeader = getAuthorizationHeader(oauthClientCredentials);
+            connection.addRequestProperty("Authorization", authorizationHeader);
+
+            connection.connect();
+
+            return getResponse(connection).get("access_token").getAsString();
+
+        } catch (IOException exception) {
+            LOGGER.error("Error getting token", exception);
+            return null;
+        }
+    }
+
+    private String getAuthorizationHeader(
+            final SubscriptionsAuthenticationParamsOauth2ClientCredentials oauthClientCredentials) {
+        final String auth = oauthClientCredentials.getClientId() + ":" + oauthClientCredentials.getClientPassword();
+        final byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.UTF_8));
+        return "Basic " + new String(encodedAuth);
+    }
+
+    private JsonObject getResponse(HttpsURLConnection connection) throws IOException {
+        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        String line, data = "";
+        while ((line = in.readLine()) != null) {
+            data += line;
+        }
+        in.close();
+        connection.getInputStream().close();
+
+        JsonObject jsonObject = new JsonParser().parse(data).getAsJsonObject();
+        return jsonObject;
+    }
+
 }