Extracting serviceID from Token in header for Creating Policy - v3 21/140121/4
authorlapentafd <francesco.lapenta@est.tech>
Thu, 16 Jan 2025 15:47:08 +0000 (15:47 +0000)
committerFrancesco Davide Lapenta <francesco.lapenta@est.tech>
Thu, 6 Feb 2025 11:47:31 +0000 (11:47 +0000)
Issue-ID: CCSDK-4041
Change-Id: Id25cff30e0c9a0e5e28c8b16f7a0ef5987ca39c7
Signed-off-by: lapentafd <francesco.lapenta@est.tech>
16 files changed:
a1-policy-management/api/offeredapis/openapitoolgen/offeredapis/pms-api/v3/custom/index.html
a1-policy-management/api/offeredapis/openapitoolgen/offeredapis/pms-api/v3/index.html
a1-policy-management/api/offeredapis/swagger/a1pms-api-v3.json
a1-policy-management/api/offeredapis/swagger/pms-api-v3.json
a1-policy-management/api/offeredapis/swagger/pms-api-v3.yaml
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/AsyncRestClient.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyService.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/TokenService.java [new file with mode: 0644]
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/Helper.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerV3Test.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyServiceTest.java
docs/offeredapis/openapitoolgen/offeredapis/pms-api/v3/custom/index.html
docs/offeredapis/openapitoolgen/offeredapis/pms-api/v3/index.html
docs/offeredapis/swagger/a1pms-api-v3.json
docs/offeredapis/swagger/pms-api-v3.json
docs/offeredapis/swagger/pms-api-v3.yaml

index db7407b..4ce5ef1 100644 (file)
@@ -958,7 +958,8 @@ ul.nav-tabs {
     "serviceId" : {\r
       "type" : "string",\r
       "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",\r
-      "example" : "rApp ID"\r
+      "example" : "rApp ID",\r
+      "default" : ""\r
     },\r
     "policyObject" : {\r
       "$ref" : "#/components/schemas/PolicyObject"\r
index 9528041..37f3436 100644 (file)
@@ -958,7 +958,8 @@ ul.nav-tabs {
     "serviceId" : {\r
       "type" : "string",\r
       "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",\r
-      "example" : "rApp ID"\r
+      "example" : "rApp ID",\r
+      "default" : ""\r
     },\r
     "policyObject" : {\r
       "$ref" : "#/components/schemas/PolicyObject"\r
index d8d3041..da9f457 100644 (file)
             "type" : "string"
           },
           "serviceId" : {
+            "default" : "",
             "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",
             "example" : "rApp ID",
             "type" : "string"
index 0fc875f..f923650 100644 (file)
             "type" : "string"
           },
           "serviceId" : {
+            "default" : "",
             "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",
             "example" : "rApp ID",
             "type" : "string"
index e21f679..6d61dfe 100644 (file)
@@ -974,6 +974,7 @@ components:
           type: string
           example:
             'rApp ID'
+          default: ""
         policyObject:
           $ref: '#/components/schemas/PolicyObject'
         policyTypeId:
index 476c160..1f5bce7 100644 (file)
@@ -3,7 +3,7 @@
  * ONAP : ccsdk oran
  * ======================================================================
  * Copyright (C) 2019-2022 Nordix Foundation. All rights reserved.
- * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
+ * Copyright (C) 2024-2025 OpenInfra Foundation Europe. 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.
@@ -90,6 +90,16 @@ public class AsyncRestClient {
                 .map(this::toBody);
     }
 
+    public Mono<ResponseEntity<String>> postWithToken(String uri, String body, String token) {
+        RequestHeadersSpec<?> request = getWebClient() //
+                .post() //
+                .uri(uri) //
+                .headers(headers -> headers.setBearerAuth(token)) //
+                .contentType(MediaType.APPLICATION_JSON) //
+                .bodyValue(body);
+        return retrieve(request);
+    }
+
     public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
         RequestHeadersSpec<?> request = getWebClient() //
                 .put() //
index d543287..f2ee6e1 100644 (file)
@@ -69,7 +69,7 @@ public class PolicyService {
                 return Mono.error(new ServiceException("Schema validation failed", HttpStatus.BAD_REQUEST));
             Ric ric = rics.getRic(policyObjectInfo.getNearRtRicId());
             PolicyType policyType = policyTypes.getType(policyObjectInfo.getPolicyTypeId());
-            Policy policy = helper.buildPolicy(policyObjectInfo, policyType, ric, helper.policyIdGeneration(policyObjectInfo));
+            Policy policy = helper.buildPolicy(policyObjectInfo, policyType, ric, helper.policyIdGeneration(policyObjectInfo), serverWebExchange);
             return helper.isPolicyAlreadyCreated(policy,policies)
                     .doOnError(errorHandlingService::handleError)
                     .flatMap(policyBuilt -> authorizationService.authCheck(serverWebExchange, policy, AccessType.WRITE)
@@ -104,7 +104,7 @@ public class PolicyService {
             Policy existingPolicy = policies.getPolicy(policyId);
             PolicyObjectInformation pos =
                     new PolicyObjectInformation(existingPolicy.getRic().getConfig().getRicId(), body, existingPolicy.getType().getId());
-            Policy updatedPolicy = helper.buildPolicy(pos, existingPolicy.getType(), existingPolicy.getRic(), policyId);
+            Policy updatedPolicy = helper.buildPolicy(pos, existingPolicy.getType(), existingPolicy.getRic(), policyId, exchange);
             Ric ric = existingPolicy.getRic();
             return authorizationService.authCheck(exchange, updatedPolicy, AccessType.WRITE)
                     .doOnError(errorHandlingService::handleError)
diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/TokenService.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/TokenService.java
new file mode 100644 (file)
index 0000000..bee29c8
--- /dev/null
@@ -0,0 +1,132 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2025 OpenInfra Foundation Europe. 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.oran.a1policymanagementservice.service.v3;
+
+ import java.lang.invoke.MethodHandles;
+ import java.util.Base64;
+
+ import org.onap.ccsdk.oran.a1policymanagementservice.models.v2.PolicyInfo;
+ import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.springframework.http.HttpHeaders;
+ import org.springframework.stereotype.Service;
+ import org.springframework.web.server.ServerWebExchange;
+
+ import com.google.gson.JsonObject;
+ import com.google.gson.JsonParser;
+
+ @Service
+ public class TokenService {
+     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+     // Prefix used to identify Bearer tokens in the Authorization header
+     private static final String BEARER_PREFIX = "Bearer ";
+
+
+     /**
+      * Retrieves the service ID for version 3 (v3) of the API, which uses PolicyObjectInformation.
+      *
+      * @param policyInfoValue The PolicyObjectInformation object containing the policy details.
+      * @param exchange The ServerWebExchange object that contains request and response information.
+      * @return The service ID, either from the policy information or derived from the client ID in the token.
+      */
+     public String getServiceId(PolicyObjectInformation policyInfoValue, ServerWebExchange exchange) {
+         String serviceId = policyInfoValue.getServiceId();
+         String clientId = extractClientIdFromToken(exchange);
+
+         // If the service ID from the policy is blank, use the client ID from the token instead
+         if (serviceId.isBlank()) {
+             if (clientId != null && !clientId.isBlank()) {
+                 serviceId = clientId;
+             }
+         }
+         // Return the determined service ID
+         logger.debug("ServiceID extracted from token: "  + serviceId);
+         return serviceId;
+     }
+
+     /**
+      * Retrieves the service ID for version 2 (v2) of the API, which uses PolicyInfo.
+      *
+      * @param policyInfoValue The PolicyInfo object containing the policy details.
+      * @param exchange The ServerWebExchange object that contains request and response information.
+      * @return The service ID, either from the policy information or derived from the client ID in the token.
+      */
+     public String getServiceId(PolicyInfo policyInfoValue, ServerWebExchange exchange) {
+         String serviceId = policyInfoValue.getServiceId();
+         String clientId = extractClientIdFromToken(exchange);
+
+         // If the service ID from the policy is blank, use the client ID from the token instead
+         if (serviceId.isBlank()) {
+             if (clientId != null && !clientId.isBlank()) {
+                 serviceId = clientId;
+             }
+         }
+         // Return the determined service ID
+         logger.debug("ServiceID extracted from token: "  + serviceId);
+         return serviceId;
+     }
+
+     /**
+      * Extracts the client ID from the Bearer token present in the Authorization header.
+      *
+      * @param exchange The ServerWebExchange object that contains request and response information.
+      * @return The client ID extracted from the token, or null if the token is invalid or missing.
+      */
+     private String extractClientIdFromToken(ServerWebExchange exchange) {
+         HttpHeaders headers = exchange.getRequest().getHeaders();
+         String authHeader = headers.getFirst(HttpHeaders.AUTHORIZATION);
+
+         // Check if the Authorization header exists and contains a Bearer token
+         if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
+             String token = authHeader.substring(BEARER_PREFIX.length());
+             return decodeClientId(token);
+         } else {
+             // Log a debug message if the Authorization header is missing or invalid
+             logger.debug("Authorization header is missing or does not contain a Bearer token");
+         }
+         return null;
+     }
+
+     /**
+      * Decodes the client ID from the JWT token.
+      *
+      * @param token The JWT token string.
+      * @return The client ID extracted from the token, or null if decoding fails.
+      */
+     private String decodeClientId(String token) {
+         try {
+             // Split the JWT token to get the payload part
+             String[] chunks = token.split("\\.");
+             Base64.Decoder decoder = Base64.getUrlDecoder();
+             String payload = new String(decoder.decode(chunks[1]));
+             JsonObject jsonObject = JsonParser.parseString(payload).getAsJsonObject();
+
+             // Return the client ID from the payload
+             return jsonObject.get("client_id").getAsString();
+         } catch (Exception e) {
+             // Log an error if decoding fails
+             logger.error("Error decoding client ID from token", e);
+             return null;
+         }
+     }
+ }
index 56bad57..3f301b7 100644 (file)
@@ -28,8 +28,10 @@ import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyInformation
 import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation;
 import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyTypeInformation;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.*;
+import org.onap.ccsdk.oran.a1policymanagementservice.service.v3.TokenService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
@@ -48,6 +50,9 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 public class Helper {
 
+    @Autowired
+    private TokenService tokenService;
+
     private final Services services;
 
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -83,14 +88,13 @@ public class Helper {
         return Mono.just(ric);
     }
 
-    public Policy buildPolicy(PolicyObjectInformation policyObjectInformation, PolicyType policyType, Ric ric, String policyId) {
+    public Policy buildPolicy(PolicyObjectInformation policyObjectInformation, PolicyType policyType, Ric ric, String policyId, ServerWebExchange exchange) {
         return Policy.builder()
                 .id(policyId)
                 .json(toJson(policyObjectInformation.getPolicyObject()))
                 .type(policyType)
                 .ric(ric)
-                .ownerServiceId(policyObjectInformation.getServiceId() == null ? ""
-                        : policyObjectInformation.getServiceId())
+                .ownerServiceId(tokenService.getServiceId(policyObjectInformation, exchange))
                 .lastModified(Instant.now())
                 .isTransient(policyObjectInformation.getTransient())
                 .build();
index 97695bc..26c10d2 100644 (file)
@@ -45,7 +45,9 @@ import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
 import org.springframework.util.FileSystemUtils;
 import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
 
+import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
 import java.util.Objects;
@@ -103,6 +105,14 @@ class PolicyControllerV3Test {
     @MockitoSpyBean
     private Helper helper;
 
+    private final String bearerToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
+    + "eyJpc3MiOiJleGFtcGxlX2lzc3VlciIsInN1YiI6IjEyMzQ1Njc4OTAiLCJhdWQiOiJteWNsaWVudCIs"
+    + "ImV4cCI6MzAwMDAwMDAwMCwiY2xpZW50X2lkIjoibXljbGllbnQiLCJyb2xlIjoidXNlciJ9."
+    + "O5QN_SWN4J1mWKyXk_-PCvOA6GF3ypv1rSdg2uTb_Ls";
+
+    private final String emptyBearerToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IiJ9."
+    + "eyJpYXQiOjE1MTYyMzkwMjJ9.uE72OfhNzhIFuyHhZyI0eYVPG6QJ7s7A-SVeKsLubCQ";
+
     @BeforeEach
     void init() {
         testHelperTest.port = port;
@@ -310,6 +320,57 @@ class PolicyControllerV3Test {
                 responseBody.contains("{\"scope\":{\"ueId\":\"ue5200\",\"qosId\":\"qos5200\"},\"qosObjectives\":{\"priorityLevel\":5200.0}"));
     }
 
+    private void postPolicyWithTokenAndVerify(String clientId, String serviceId, String result) throws IOException {
+        testHelperTest.addPolicyType("type1_1.2.3", "ric.1");
+        String policyBody = testHelperTest.postPolicyBody("ric.1", "type1_1.2.3", "1");
+
+        if (serviceId != null) {
+            policyBody = policyBody.replace("\"serviceId\":\"\"", "\"serviceId\":\"" + serviceId + "\"");
+        }
+
+        StepVerifier.create(testHelperTest.restClientV3().postWithToken("/policies", policyBody, clientId)
+                    .then(testHelperTest.restClientV3().getForEntity("/policies" + ((serviceId != null || clientId != null) ? "?serviceId=" + result : ""))))
+                    .expectNextMatches(response -> response.getBody().contains("\"policyId\":\"1\""))
+                    .expectComplete()
+                    .verify();
+    }
+
+    @Test
+    @DisplayName("client_id VALID + service_id NULL/EMPTY = client_id")
+    void testPostPolicyWithToken() throws IOException {
+        postPolicyWithTokenAndVerify(bearerToken, null, "myclient");
+    }
+
+    @Test
+    @DisplayName("client_id VALID + service_id VALID = service_id")
+    void testPostPolicyWithTokenAndServiceID() throws IOException {
+        postPolicyWithTokenAndVerify(bearerToken, "notmyclient", "notmyclient");
+    }
+
+    @Test
+    @DisplayName("client_id NULL + service_id EMPTY = empty")
+    void testClientIdNullServiceIdEmpty() throws Exception {
+        postPolicyWithTokenAndVerify(null, null, "");
+    }
+
+    @Test
+    @DisplayName("client_id NULL + service_id VALID = service_id")
+    void testClientIdNullServiceIdValid() throws Exception {
+        postPolicyWithTokenAndVerify(null, "validServiceId", "validServiceId");
+    }
+
+    @Test
+    @DisplayName("client_id EMPTY + service_id NULL/EMPTY = empty")
+    void testClientIdEmptyServiceIdEmpty() throws Exception {
+        postPolicyWithTokenAndVerify(emptyBearerToken, null, "");
+    }
+
+    @Test
+    @DisplayName("client_id EMPTY + service_id VALID = service_id")
+    void testEmptyClientIdServiceIdValid() throws Exception {
+        postPolicyWithTokenAndVerify(emptyBearerToken, "validServiceId", "validServiceId");
+    }
+
     @Test
     @DisplayName("test get Policy Status")
     void testGetPolicyStatus() throws Exception {
index 0266771..f2b74ef 100644 (file)
@@ -121,7 +121,7 @@ class PolicyServiceTest {
         ServerWebExchange serverWebExchange = Mockito.mock(DefaultServerWebExchange.class);
         Policy policy = testHelperTest.buidTestPolicy(testHelperTest.policyObjectInfo(nonRtRicId, policyTypeName), "122344-5674");
         when(helper.jsonSchemaValidation(any())).thenReturn(Boolean.TRUE);
-        when(helper.buildPolicy(any(),any(), any(), any())).thenReturn(policy);
+        when(helper.buildPolicy(any(),any(), any(), any(), any())).thenReturn(policy);
         when(helper.isPolicyAlreadyCreated(any(), any())).thenReturn(Mono.error(new ServiceException
                 ("Same policy content already created with policy ID: 122344-5674", HttpStatus.BAD_REQUEST)));
         Mono<ResponseEntity<PolicyObjectInformation>> responseMono = policyService.createPolicyService(testHelperTest.policyObjectInfo(nonRtRicId, policyTypeName), serverWebExchange);
@@ -186,7 +186,7 @@ class PolicyServiceTest {
                 "        }\n" +
                 "    }").getAsJsonObject().toString(), Map.class));
         Policy updatedPolicy = testHelperTest.buidTestPolicy(updatedPolicyObjectInfo, "122344-5674");
-        when(helper.buildPolicy(any(),any(), any(), any())).thenReturn(updatedPolicy);
+        when(helper.buildPolicy(any(),any(), any(), any(), any())).thenReturn(updatedPolicy);
         when(helper.checkRicStateIdle(any())).thenReturn(Mono.just(updatedPolicy.getRic()));
         when(helper.checkSupportedType(any(), any())).thenReturn(Mono.just(updatedPolicy.getRic()));
         when(authorizationService.authCheck(any(), any(), any())).thenReturn(Mono.just(updatedPolicy));
index db7407b..4ce5ef1 100644 (file)
@@ -958,7 +958,8 @@ ul.nav-tabs {
     "serviceId" : {\r
       "type" : "string",\r
       "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",\r
-      "example" : "rApp ID"\r
+      "example" : "rApp ID",\r
+      "default" : ""\r
     },\r
     "policyObject" : {\r
       "$ref" : "#/components/schemas/PolicyObject"\r
index 9528041..37f3436 100644 (file)
@@ -958,7 +958,8 @@ ul.nav-tabs {
     "serviceId" : {\r
       "type" : "string",\r
       "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",\r
-      "example" : "rApp ID"\r
+      "example" : "rApp ID",\r
+      "default" : ""\r
     },\r
     "policyObject" : {\r
       "$ref" : "#/components/schemas/PolicyObject"\r
index d8d3041..da9f457 100644 (file)
             "type" : "string"
           },
           "serviceId" : {
+            "default" : "",
             "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",
             "example" : "rApp ID",
             "type" : "string"
index 0fc875f..f923650 100644 (file)
             "type" : "string"
           },
           "serviceId" : {
+            "default" : "",
             "description" : "the identity of the service owning the policy. This can be used to group the policies (it is possible to get all policies associated to a service). Note that the service does not need to be registered.",
             "example" : "rApp ID",
             "type" : "string"
index e21f679..6d61dfe 100644 (file)
@@ -974,6 +974,7 @@ components:
           type: string
           example:
             'rApp ID'
+          default: ""
         policyObject:
           $ref: '#/components/schemas/PolicyObject'
         policyTypeId: