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