From 27e53a6ea278d61f9ed545b1667c6d33347df1d0 Mon Sep 17 00:00:00 2001
From: Hari-stackroute <40484996+Hari-stackroute@users.noreply.github.com>
Date: Mon, 4 Oct 2021 14:39:16 +0530
Subject: [PATCH] SB-26961 changes done for validating token (#966)

* SB-26961 changes done for validating token
---
 .../auth/verifier/AccessTokenValidator.java   | 34 +++++++++++++++++--
 .../verifier/AccessTokenValidatorTest.java    | 31 +++++++++++++++++
 .../sunbird/actor/user/UserMergeActor.java    |  6 ++--
 .../actor/user/UserMergeActorTest.java        | 24 +++++++------
 4 files changed, 79 insertions(+), 16 deletions(-)

diff --git a/core/platform-common/src/main/java/org/sunbird/auth/verifier/AccessTokenValidator.java b/core/platform-common/src/main/java/org/sunbird/auth/verifier/AccessTokenValidator.java
index 331139f12..a3caf4248 100755
--- a/core/platform-common/src/main/java/org/sunbird/auth/verifier/AccessTokenValidator.java
+++ b/core/platform-common/src/main/java/org/sunbird/auth/verifier/AccessTokenValidator.java
@@ -77,7 +77,7 @@ public class AccessTokenValidator {
         }
       }
     } catch (Exception ex) {
-      logger.error("Exception in verifyManagedUserToken: Token : "+managedEncToken, ex);
+      logger.error("Exception in verifyManagedUserToken: Token : " + managedEncToken, ex);
     }
     return managedFor;
   }
@@ -96,10 +96,32 @@ public class AccessTokenValidator {
         }
       }
     } catch (Exception ex) {
-      logger.error("Exception in verifyUserAccessToken: Token : "+token, ex);
+      logger.error("Exception in verifyUserAccessToken: Token : " + token, ex);
     }
     if (JsonKey.UNAUTHORIZED.equalsIgnoreCase(userId)) {
-      logger.info("verifyUserAccessToken: Invalid User Token: "+token);
+      logger.info("verifyUserAccessToken: Invalid User Token: " + token);
+    }
+    return userId;
+  }
+
+  public static String verifySourceUserToken(String token, String url) {
+    String userId = JsonKey.UNAUTHORIZED;
+    try {
+      Map<String, Object> payload = validateToken(token);
+
+      logger.info("learner source access token validateToken() :" + payload.toString());
+      if (MapUtils.isNotEmpty(payload) && checkSourceIss((String) payload.get("iss"), url)) {
+        userId = (String) payload.get(JsonKey.SUB);
+        if (StringUtils.isNotBlank(userId)) {
+          int pos = userId.lastIndexOf(":");
+          userId = userId.substring(pos + 1);
+        }
+      }
+    } catch (Exception ex) {
+      logger.error("Exception in verifySourceUserToken: Token : " + token, ex);
+    }
+    if (JsonKey.UNAUTHORIZED.equalsIgnoreCase(userId)) {
+      logger.info("verifySourceUserToken: Invalid source user Token: " + token);
     }
     return userId;
   }
@@ -109,6 +131,12 @@ public class AccessTokenValidator {
     return (realmUrl.equalsIgnoreCase(iss));
   }
 
+  private static boolean checkSourceIss(String iss, String url) {
+    String ssoUrl = (url != null ? url : sso_url);
+    String realmUrl = ssoUrl + "realms/" + realm;
+    return (realmUrl.equalsIgnoreCase(iss));
+  }
+
   private static boolean isExpired(Integer expiration) {
     return (Time.currentTime() > expiration);
   }
diff --git a/core/platform-common/src/test/java/org/sunbird/auth/verifier/AccessTokenValidatorTest.java b/core/platform-common/src/test/java/org/sunbird/auth/verifier/AccessTokenValidatorTest.java
index f3e320c8d..9e26ab8ea 100644
--- a/core/platform-common/src/test/java/org/sunbird/auth/verifier/AccessTokenValidatorTest.java
+++ b/core/platform-common/src/test/java/org/sunbird/auth/verifier/AccessTokenValidatorTest.java
@@ -68,6 +68,37 @@ public class AccessTokenValidatorTest {
     assertNotNull(userId);
   }
 
+  @Test
+  public void verifySourceUserAccessToken() throws JsonProcessingException {
+    PowerMockito.mockStatic(CryptoUtil.class);
+    PowerMockito.mockStatic(Base64Util.class);
+    PowerMockito.mockStatic(KeyManager.class);
+    PropertiesCache propertiesCache = mock(PropertiesCache.class);
+    PowerMockito.when(propertiesCache.getProperty(Mockito.anyString())).thenReturn("anyString");
+    KeyData keyData = PowerMockito.mock(KeyData.class);
+    Mockito.when(KeyManager.getPublicKey(Mockito.anyString())).thenReturn(keyData);
+    PublicKey publicKey = PowerMockito.mock(PublicKey.class);
+    Mockito.when(keyData.getPublicKey()).thenReturn(publicKey);
+    Map<String, Object> payload = new HashMap<>();
+    int expTime = Time.currentTime() + 3600000;
+    payload.put("exp", expTime);
+    payload.put("iss", "urlrealms/master");
+    payload.put("kid", "kid");
+    payload.put("sub", "f:ca00376d-395f-aee687d7c8ad:10cca27c-2a13-443c-9e2b-c7d9589c1f5f");
+    ObjectMapper mapper = new ObjectMapper();
+    Mockito.when(Base64Util.decode(Mockito.any(String.class), Mockito.anyInt()))
+        .thenReturn(mapper.writeValueAsString(payload).getBytes());
+    Mockito.when(
+            CryptoUtil.verifyRSASign(
+                Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any()))
+        .thenReturn(true);
+    String userId =
+        AccessTokenValidator.verifySourceUserToken(
+            "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5emhhVnZDbl81OEtheHpldHBzYXNZQ2lEallkemJIX3U2LV93SDk4SEc0In0.eyJqdGkiOiI5ZmQzNzgzYy01YjZmLTQ3OWQtYmMzYy0yZWEzOGUzZmRmYzgiLCJleHAiOjE1MDUxMTQyNDYsIm5iZiI6MCwiaWF0IjoxNTA1MTEzNjQ2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoic2VjdXJpdHktYWRtaW4tY29uc29sZSIsInN1YiI6ImIzYTZkMTY4LWJjZmQtNDE2MS1hYzVmLTljZjYyODIyNzlmMyIsInR5cCI6IkJlYXJlciIsImF6cCI6InNlY3VyaXR5LWFkbWluLWNvbnNvbGUiLCJub25jZSI6ImMxOGVlMDM2LTAyMWItNGVlZC04NWVhLTc0MjMyYzg2ZmI4ZSIsImF1dGhfdGltZSI6MTUwNTExMzY0Niwic2Vzc2lvbl9zdGF0ZSI6ImRiZTU2NDlmLTY4MDktNDA3NS05Njk5LTVhYjIyNWMwZTkyMiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZXNvdXJjZV9hY2Nlc3MiOnt9LCJuYW1lIjoiTWFuemFydWwgaGFxdWUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0MTIzNDU2NyIsImdpdmVuX25hbWUiOiJNYW56YXJ1bCBoYXF1ZSIsImVtYWlsIjoidGVzdDEyM0B0LmNvbSJ9.Xdjqe16MSkiR94g-Uj_pVZ2L3gnIdKpkJ6aB82W_w_c3yEmx1mXYBdkxe4zMz3ks4OX_PWwSFEbJECHcnujUwF6Ula0xtXTfuESB9hFyiWHtVAhuh5UlCCwPnsihv5EqK6u-Qzo0aa6qZOiQK3Zo7FLpnPUDxn4yHyo3mRZUiWf76KTl8PhSMoXoWxcR2vGW0b-cPixILTZPV0xXUZoozCui70QnvTgOJDWqr7y80EWDkS4Ptn-QM3q2nJlw63mZreOG3XTdraOlcKIP5vFK992dyyHlYGqWVzigortS9Ah4cprFVuLlX8mu1cQvqHBtW-0Dq_JlcTMaztEnqvJ6XA",
+            "url");
+    assertNotNull(userId);
+  }
+
   @Test
   public void verifyUserAccessTokenInvalidToken() throws JsonProcessingException {
     PowerMockito.mockStatic(CryptoUtil.class);
diff --git a/service/src/main/java/org/sunbird/actor/user/UserMergeActor.java b/service/src/main/java/org/sunbird/actor/user/UserMergeActor.java
index 968f0da9a..026f89dda 100644
--- a/service/src/main/java/org/sunbird/actor/user/UserMergeActor.java
+++ b/service/src/main/java/org/sunbird/actor/user/UserMergeActor.java
@@ -14,6 +14,7 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import org.apache.kafka.clients.producer.Producer;
 import org.apache.kafka.clients.producer.ProducerRecord;
+import org.sunbird.auth.verifier.AccessTokenValidator;
 import org.sunbird.client.systemsettings.SystemSettingClient;
 import org.sunbird.client.systemsettings.impl.SystemSettingClientImpl;
 import org.sunbird.dao.user.UserDao;
@@ -268,10 +269,11 @@ public class UserMergeActor extends UserBaseActor {
     String sourceUserAuthToken = (String) headers.get(JsonKey.X_SOURCE_USER_TOKEN);
     String subDomainUrl = ProjectUtil.getConfigValue(JsonKey.SUNBIRD_SUBDOMAIN_KEYCLOAK_BASE_URL);
     logger.info(context, "UserMergeActor:checkTokenDetails sub domain url value " + subDomainUrl);
-    String userId = keyCloakService.verifyToken(userAuthToken, context);
+    String userId = AccessTokenValidator.verifyUserToken(userAuthToken);
     // Since source token is generated from subdomain , so verification also need with
     // same subdomain.
-    String sourceUserId = keyCloakService.verifyToken(sourceUserAuthToken, subDomainUrl, context);
+    String sourceUserId =
+        AccessTokenValidator.verifySourceUserToken(sourceUserAuthToken, subDomainUrl);
     if (!(mergeeId.equals(sourceUserId) && mergerId.equals(userId))) {
       throw new ProjectCommonException(
           ResponseCode.unAuthorized.getErrorCode(),
diff --git a/service/src/test/java/org/sunbird/actor/user/UserMergeActorTest.java b/service/src/test/java/org/sunbird/actor/user/UserMergeActorTest.java
index 260e29dcd..85fc71948 100644
--- a/service/src/test/java/org/sunbird/actor/user/UserMergeActorTest.java
+++ b/service/src/test/java/org/sunbird/actor/user/UserMergeActorTest.java
@@ -23,6 +23,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.sunbird.actor.core.BaseActor;
+import org.sunbird.auth.verifier.AccessTokenValidator;
 import org.sunbird.cassandraimpl.CassandraOperationImpl;
 import org.sunbird.dao.user.impl.UserDaoImpl;
 import org.sunbird.exception.ProjectCommonException;
@@ -30,15 +31,14 @@ import org.sunbird.exception.ResponseCode;
 import org.sunbird.helper.ServiceFactory;
 import org.sunbird.kafka.KafkaClient;
 import org.sunbird.keys.JsonKey;
-import org.sunbird.service.user.impl.UserServiceImpl;
-import org.sunbird.util.DataCacheHandler;
 import org.sunbird.model.user.User;
 import org.sunbird.operations.ActorOperations;
 import org.sunbird.request.Request;
 import org.sunbird.response.Response;
+import org.sunbird.service.user.impl.UserServiceImpl;
 import org.sunbird.sso.SSOServiceFactory;
-import org.sunbird.sso.impl.KeyCloakServiceImpl;
 import org.sunbird.util.ConfigUtil;
+import org.sunbird.util.DataCacheHandler;
 import org.sunbird.util.user.KafkaConfigConstants;
 
 @RunWith(PowerMockRunner.class)
@@ -52,7 +52,8 @@ import org.sunbird.util.user.KafkaConfigConstants;
   CassandraOperationImpl.class,
   ConfigUtil.class,
   Config.class,
-  KafkaClient.class
+  KafkaClient.class,
+  AccessTokenValidator.class
 })
 @PowerMockIgnore({
   "javax.management.*",
@@ -72,7 +73,7 @@ public class UserMergeActorTest {
   public static Producer producer;
   public static KafkaClient kafkaClient;
   public static CassandraOperationImpl cassandraOperation;
-  private static KeyCloakServiceImpl ssoManager;
+  private static AccessTokenValidator tokenValidator;
 
   @Before
   public void beforeEachTest() {
@@ -87,13 +88,13 @@ public class UserMergeActorTest {
     config = mock(Config.class);
     kafkaClient = mock(KafkaClient.class);
     producer = mock(Producer.class);
-    ssoManager = mock(KeyCloakServiceImpl.class);
+    tokenValidator = mock(AccessTokenValidator.class);
+    PowerMockito.mockStatic(AccessTokenValidator.class);
     when(ConfigUtil.getConfig()).thenReturn(config);
     when(config.getString(KafkaConfigConstants.SUNBIRD_USER_CERT_KAFKA_TOPIC)).thenReturn("topic");
     when(UserServiceImpl.getInstance()).thenReturn(userService);
     when(UserDaoImpl.getInstance()).thenReturn(userDao);
     when(KafkaClient.getProducer()).thenReturn(producer);
-    when(SSOServiceFactory.getInstance()).thenReturn(ssoManager);
     cassandraOperation = mock(CassandraOperationImpl.class);
     userCounter = 0;
   }
@@ -104,10 +105,11 @@ public class UserMergeActorTest {
         .thenReturn(getUserDetails(true))
         .thenReturn(getUserDetails(true));
     when(userDao.updateUser(Mockito.anyMap(), Mockito.any())).thenReturn(getSuccessResponse());
-    when(ssoManager.verifyToken(Mockito.anyString(), Mockito.any())).thenReturn("anyUserId");
-    when(ssoManager.verifyToken(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
+    when(tokenValidator.verifyUserToken(Mockito.anyString())).thenReturn("anyUserId");
+    when(tokenValidator.verifySourceUserToken(Mockito.anyString(), Mockito.anyString()))
         .thenReturn("anyUserId");
     when(DataCacheHandler.getConfigSettings()).thenReturn(configSettingsMap());
+    when(DataCacheHandler.getConfigSettings()).thenReturn(configSettingsMap());
     boolean result =
         testScenario(getRequest(ActorOperations.MERGE_USER), ResponseCode.invalidIdentifier);
     assertTrue(result);
@@ -119,8 +121,8 @@ public class UserMergeActorTest {
         .thenReturn(getUserDetails(false))
         .thenReturn(getUserDetails(false));
     when(userDao.updateUser(Mockito.anyMap(), Mockito.any())).thenReturn(getSuccessResponse());
-    when(ssoManager.verifyToken(Mockito.anyString(), Mockito.any())).thenReturn("anyUserId");
-    when(ssoManager.verifyToken(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
+    when(tokenValidator.verifyUserToken(Mockito.anyString())).thenReturn("anyUserId");
+    when(tokenValidator.verifySourceUserToken(Mockito.anyString(), Mockito.anyString()))
         .thenReturn("anyUserId");
     when(DataCacheHandler.getConfigSettings()).thenReturn(configSettingsMap());
     boolean result = testScenario(getRequest(ActorOperations.MERGE_USER), null);
-- 
GitLab