diff --git a/components/org.wso2.carbon.identity.notification.push.common/src/main/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidator.java b/components/org.wso2.carbon.identity.notification.push.common/src/main/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidator.java index f680766..f8f8b04 100644 --- a/components/org.wso2.carbon.identity.notification.push.common/src/main/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidator.java +++ b/components/org.wso2.carbon.identity.notification.push.common/src/main/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025-2026, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -36,6 +36,7 @@ import java.text.ParseException; import java.util.Base64; import java.util.Date; +import java.util.Map; /** * JWT token validator for Push notification scenarios. @@ -81,6 +82,21 @@ public static JWTClaimsSet getValidatedClaimSet(String jwt, String publicKey) th } } + /** + * Validate the JWT token and return the claim values. + * + * @param jwt JWT token to be validated + * @param publicKey Public key used for signing the JWT + * @return Map of claim values + * @throws PushTokenValidationException Error when validating the JWT token + */ + public static Map getValidatedClaims(String jwt, String publicKey) + throws PushTokenValidationException { + + JWTClaimsSet claimsSet = getValidatedClaimSet(jwt, publicKey); + return claimsSet.getClaims(); + } + /** * Validate the legitimacy of JWT token. * diff --git a/components/org.wso2.carbon.identity.notification.push.common/src/test/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidatorTest.java b/components/org.wso2.carbon.identity.notification.push.common/src/test/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidatorTest.java index 167ae31..229e420 100644 --- a/components/org.wso2.carbon.identity.notification.push.common/src/test/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidatorTest.java +++ b/components/org.wso2.carbon.identity.notification.push.common/src/test/java/org/wso2/carbon/identity/notification/push/common/PushChallengeValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025-2026, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -31,6 +31,8 @@ import java.text.ParseException; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -141,6 +143,89 @@ public void testGetValidatedClaimSetWithExpiredToken() throws Exception { } } + @Test + public void testGetValidatedClaimsWithValidToken() throws Exception { + + try (MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class)) { + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + Map expectedClaims = new HashMap<>(); + expectedClaims.put("chg", "e0c3d04c-750b-4301-8f76-e07ebf02e53a"); + expectedClaims.put("td", "carbon.super"); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + when(mockClaimsSet.getClaims()).thenReturn(expectedClaims); + + Map claims = PushChallengeValidator.getValidatedClaims(validJwt, publicKey); + assertNotNull(claims); + } + } + + @Test(expectedExceptions = PushTokenValidationException.class) + public void testGetValidatedClaimsWithBlankToken() throws Exception { + + PushChallengeValidator.getValidatedClaims("", publicKey); + } + + @Test(expectedExceptions = PushTokenValidationException.class) + public void testGetValidatedClaimsWithInvalidJwtToken() throws Exception { + + PushChallengeValidator.getValidatedClaims(invalidJwt, publicKey); + } + + @Test(expectedExceptions = PushTokenValidationException.class) + public void testGetValidatedClaimsWithExpiredToken() throws Exception { + + try (MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class)) { + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + PushChallengeValidator.getValidatedClaims(validJwt, publicKey); + } + } + + @Test(expectedExceptions = PushTokenValidationException.class) + public void testGetValidatedClaimsWithInvalidSignature() throws Exception { + + try (MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class)) { + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(null); + PushChallengeValidator.getValidatedClaims(validJwt, invalidPublicKey); + } + } + + @Test + public void testGetValidatedClaimsReturnsCorrectClaimValues() throws Exception { + + try (MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class)) { + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + Map expectedClaims = new HashMap<>(); + expectedClaims.put("td", "carbon.super"); + expectedClaims.put("pid", "f5ae6a0d-390a-4eea-a380-8bcc86e4a148"); + expectedClaims.put("chg", "e0c3d04c-750b-4301-8f76-e07ebf02e53a"); + expectedClaims.put("res", "APPROVED"); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + when(mockClaimsSet.getClaims()).thenReturn(expectedClaims); + + Map claims = PushChallengeValidator.getValidatedClaims(validJwt, publicKey); + assertNotNull(claims); + Assert.assertEquals(claims.get("td"), "carbon.super"); + Assert.assertEquals(claims.get("pid"), "f5ae6a0d-390a-4eea-a380-8bcc86e4a148"); + Assert.assertEquals(claims.get("chg"), "e0c3d04c-750b-4301-8f76-e07ebf02e53a"); + Assert.assertEquals(claims.get("res"), "APPROVED"); + Assert.assertEquals(claims.size(), 4); + } + } + @Test public void testValidateChallengeWithEmptyClaimsSet() { diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/DeviceHandlerService.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/DeviceHandlerService.java index dbab23e..4d5ce01 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/DeviceHandlerService.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/DeviceHandlerService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025-2026, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -92,6 +92,15 @@ Device registerDevice(RegistrationRequest registrationRequest, String tenantDoma */ void editDevice(String deviceId, String path, String value) throws PushDeviceHandlerException; + /** + * Edit the device from mobile. + * + * @param deviceId Device ID. + * @param token Token. + * @throws PushDeviceHandlerException Push Device Handler Exception. + */ + default void editDeviceMobile(String deviceId, String token) throws PushDeviceHandlerException{} + /** * Get registration discovery data. * diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/constant/PushDeviceHandlerConstants.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/constant/PushDeviceHandlerConstants.java index 7a6f958..72be7d8 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/constant/PushDeviceHandlerConstants.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/constant/PushDeviceHandlerConstants.java @@ -30,6 +30,8 @@ public class PushDeviceHandlerConstants { public static final String DEVICE_REGISTRATION_CONTEXT_VALIDITY_PERIOD = "PushAuthenticator.DeviceRegistrationContext.ValidityPeriod"; public static final int DEFAULT_DEVICE_REGISTRATION_CONTEXT_VALIDITY_PERIOD = 180; + public static final String DEVICE_NAME = "name"; + public static final String DEVICE_TOKEN = "deviceToken"; /** * Private constructor to prevent initialization of the class. @@ -56,7 +58,7 @@ public static class SQLQueries { public static final String GET_PUBLIC_KEY_BY_ID = "SELECT PUBLIC_KEY FROM IDN_PUSH_DEVICE_STORE " + "WHERE ID = :ID;"; public static final String UNREGISTER_DEVICE = "DELETE FROM IDN_PUSH_DEVICE_STORE WHERE ID = :ID;"; - public static final String EDIT_DEVICE = "UPDATE IDN_PUSH_DEVICE_STORE SET DEVICE_NAME = :DEVICE_NAME; " + + public static final String EDIT_DEVICE = "UPDATE IDN_PUSH_DEVICE_STORE SET DEVICE_NAME = :DEVICE_NAME;, " + "DEVICE_TOKEN = :DEVICE_TOKEN; WHERE ID = :ID;"; } @@ -136,6 +138,10 @@ public enum ErrorMessages { ERROR_CODE_FAILED_TO_RESOLVE_PUSH_PROVIDER( "PDH-15013", "Failed to resolve the correct push provider for the request." + ), + ERROR_CODE_DEVICE_EDIT_FAILED( + "PDH-150014", + "Error occurred while updating the device for the device ID: %s." ); private final String code; diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImpl.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImpl.java index 320092b..94d68d9 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImpl.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImpl.java @@ -71,7 +71,10 @@ import java.util.UUID; import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.DEFAULT_PUSH_PROVIDER; +import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.DEVICE_NAME; +import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.DEVICE_TOKEN; import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.ErrorMessages.ERROR_CODE_DEVICE_ALREADY_REGISTERED; +import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.ErrorMessages.ERROR_CODE_DEVICE_EDIT_FAILED; import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.ErrorMessages.ERROR_CODE_DEVICE_NOT_FOUND; import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.ErrorMessages.ERROR_CODE_DEVICE_NOT_FOUND_FOR_USER_ID; import static org.wso2.carbon.identity.notification.push.device.handler.constant.PushDeviceHandlerConstants.ErrorMessages.ERROR_CODE_DEVICE_REGISTRATION_FAILED; @@ -135,6 +138,11 @@ public Device registerDevice(RegistrationRequest registrationRequest, String ten device = handleDeviceRegistration(registrationRequest, context); if (context.isRegistered()) { deviceRegistrationContextManager.clearContext(registrationRequest.getDeviceId(), tenantDomain); + AUDIT_LOGGER.printAuditLog( + DeviceHandlerAuditLogger.Operation.REGISTER_DEVICE, + deviceId, + device.getUserId() + ); } else { throw new PushDeviceHandlerClientException(ERROR_CODE_DEVICE_REGISTRATION_FAILED.getCode(), String.format(ERROR_CODE_DEVICE_REGISTRATION_FAILED.getMessage(), deviceId)); @@ -245,6 +253,62 @@ public void editDevice(String deviceId, String path, String value) throws PushDe Device device = getDevice(deviceId); handleEditDevice(device, path, value); + AUDIT_LOGGER.printAuditLog( + DeviceHandlerAuditLogger.Operation.UPDATE_DEVICE, + deviceId, + device.getUserId() + ); + } + + @Override + public void editDeviceMobile(String deviceId, String token) throws PushDeviceHandlerException { + + Optional deviceOptional = deviceDAO.getDevice(deviceId); + if (!deviceOptional.isPresent()) { + throw new PushDeviceHandlerClientException(ERROR_CODE_DEVICE_NOT_FOUND.getCode(), + String.format(ERROR_CODE_DEVICE_NOT_FOUND.getMessage(), deviceId)); + } + Device device = deviceOptional.get(); + + Map claims; + try { + claims = PushChallengeValidator.getValidatedClaims(token, device.getPublicKey()); + } catch (PushTokenValidationException e) { + throw new PushDeviceHandlerClientException(ERROR_CODE_TOKEN_CLAIM_VERIFICATION_FAILED.getCode(), + String.format(ERROR_CODE_TOKEN_CLAIM_VERIFICATION_FAILED.getMessage(), deviceId), e); + } + + validateDeviceEditClaims(claims, deviceId); + + String deviceToken = null; + String deviceName = null; + + if (claims.containsKey(DEVICE_TOKEN)) { + deviceToken = (String) claims.get(DEVICE_TOKEN); + } + + if (claims.containsKey(DEVICE_NAME)) { + deviceName = (String) claims.get(DEVICE_NAME); + } + + if (deviceToken == null && deviceName == null) { + throw new PushDeviceHandlerClientException(ERROR_CODE_DEVICE_EDIT_FAILED.getCode(), + String.format(ERROR_CODE_DEVICE_EDIT_FAILED.getMessage(), deviceId)); + } + if (deviceToken != null) { + device.setDeviceToken(deviceToken); + } + if (deviceName != null) { + device.setDeviceName(deviceName); + } + + handleUpdateDeviceForProvider(device); + deviceDAO.editDevice(device.getDeviceId(), device); + AUDIT_LOGGER.printAuditLog( + DeviceHandlerAuditLogger.Operation.UPDATE_DEVICE, + deviceId, + device.getUserId() + ); } @Override @@ -673,4 +737,25 @@ public static PushSenderData buildPushSenderData(PushSenderDTO pushSenderDTO) { pushSenderData.setProviderId(pushSenderDTO.getProviderId()); return pushSenderData; } + + /** + * Validate the claims in the edit device token. + * + * @param claims Claims to validate. + * @param deviceId Device ID for error messages. + * @throws PushDeviceHandlerClientException If the claims are invalid. + */ + private static void validateDeviceEditClaims(Map claims, String deviceId) + throws PushDeviceHandlerClientException { + + if (claims.containsKey(DEVICE_TOKEN) && !(claims.get(DEVICE_TOKEN) instanceof String)) { + throw new PushDeviceHandlerClientException(ERROR_CODE_DEVICE_EDIT_FAILED.getCode(), + String.format(ERROR_CODE_DEVICE_EDIT_FAILED.getMessage(), deviceId)); + } + + if (claims.containsKey(DEVICE_NAME) && !(claims.get(DEVICE_NAME) instanceof String)) { + throw new PushDeviceHandlerClientException(ERROR_CODE_DEVICE_EDIT_FAILED.getCode(), + String.format(ERROR_CODE_DEVICE_EDIT_FAILED.getMessage(), deviceId)); + } + } } diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLogger.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLogger.java index b3b2d33..d252301 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLogger.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/main/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLogger.java @@ -45,7 +45,7 @@ public class DeviceHandlerAuditLogger { */ public void printAuditLog(Operation operation, String deviceId, String userId) { - JSONObject data = createAuditLogEntry(userId); + JSONObject data = createAuditLogEntry(operation, userId); buildAuditLog(operation, deviceId, data); } @@ -72,11 +72,21 @@ private void buildAuditLog(Operation operation, String targetId, JSONObject data * * @return Audit log data. */ - private JSONObject createAuditLogEntry(String userId) { + private JSONObject createAuditLogEntry(Operation operation, String userId) { JSONObject data = new JSONObject(); data.put(LogConstants.END_USER_ID, userId != null ? userId : JSONObject.NULL); - data.put(LogConstants.UNREGISTERED_AT, System.currentTimeMillis()); + switch (operation) { + case REGISTER_DEVICE: + data.put(LogConstants.REGISTERED_AT, System.currentTimeMillis()); + break; + case UPDATE_DEVICE: + data.put(LogConstants.UPDATED_AT, System.currentTimeMillis()); + break; + case UNREGISTER_DEVICE: + data.put(LogConstants.UNREGISTERED_AT, System.currentTimeMillis()); + break; + } return data; } @@ -128,6 +138,8 @@ private String getInitiatorId() { */ public enum Operation { + REGISTER_DEVICE("Register-Push-Auth-Device"), + UPDATE_DEVICE("Update-Push-Auth-Device"), UNREGISTER_DEVICE("Unregister-Push-Auth-Device"); private final String logAction; @@ -150,6 +162,8 @@ private static class LogConstants { public static final String TARGET_TYPE_FIELD = "Push-Auth-Device"; public static final String END_USER_ID = "UserId"; + public static final String REGISTERED_AT = "RegisteredAt"; + public static final String UPDATED_AT = "UpdatedAt"; public static final String UNREGISTERED_AT = "UnregisteredAt"; } } diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/dao/DeviceDAOImplTest.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/dao/DeviceDAOImplTest.java index 8376fbe..8dc9a8e 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/dao/DeviceDAOImplTest.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/dao/DeviceDAOImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025-2026, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -42,7 +42,7 @@ public class DeviceDAOImplTest { "DEVICE_MODEL, DEVICE_TOKEN, DEVICE_HANDLE, PROVIDER, PUBLIC_KEY, TENANT_ID) VALUES " + "( ? , ? , ? , ? , ? , ? , ? , ? , ? )"; public static final String UNREGISTER_DEVICE_TEST = "DELETE FROM IDN_PUSH_DEVICE_STORE WHERE ID = ? "; - public static final String EDIT_DEVICE_TEST = "UPDATE IDN_PUSH_DEVICE_STORE SET DEVICE_NAME = ? " + + public static final String EDIT_DEVICE_TEST = "UPDATE IDN_PUSH_DEVICE_STORE SET DEVICE_NAME = ? , " + "DEVICE_TOKEN = ? WHERE ID = ? "; private DeviceDAOImpl deviceDAO; diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImplTest.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImplTest.java index ed63812..98b5d36 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImplTest.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/impl/DeviceHandlerServiceImplTest.java @@ -1010,6 +1010,515 @@ public void testEditDeviceWhenPushProviderServerExceptionThrown() } } + @Test + public void testEditDeviceMobileWithBothFields() + throws PushDeviceHandlerException, NotificationSenderManagementException, PushProviderException, + JOSEException, ParseException { + + try ( + MockedStatic mockedIdentityTenantUtil = + Mockito.mockStatic(IdentityTenantUtil.class); + MockedStatic mockedPushDeviceHandlerDataHolder = + Mockito.mockStatic(PushDeviceHandlerDataHolder.class); + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(any())).thenReturn(-1234); + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + claims.put("deviceToken", "newDeviceToken"); + claims.put("name", "newDeviceName"); + when(mockClaimsSet.getClaims()).thenReturn(claims); + when(mockClaimsSet.getStringClaim("deviceToken")).thenReturn("newDeviceToken"); + when(mockClaimsSet.getStringClaim("name")).thenReturn("newDeviceName"); + + PushDeviceHandlerDataHolder pushDeviceHandlerDataHolder = mock(PushDeviceHandlerDataHolder.class); + mockedPushDeviceHandlerDataHolder.when(PushDeviceHandlerDataHolder::getInstance) + .thenReturn(pushDeviceHandlerDataHolder); + + NotificationSenderManagementService notificationSenderManagementService = + mock(NotificationSenderManagementService.class); + when(pushDeviceHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO pushSenderDTO = new PushSenderDTO(); + pushSenderDTO.setName("FCM_PushPublisher"); + pushSenderDTO.setProvider("FCM"); + pushSenderDTO.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(pushSenderDTO); + when(notificationSenderManagementService.getPushSenders(anyBoolean())) + .thenReturn(pushSenders); + + FCMPushProvider fcmPushProvider = mock(FCMPushProvider.class); + when(pushDeviceHandlerDataHolder.getPushProvider(eq("FCM"))).thenReturn(fcmPushProvider); + when(fcmPushProvider.getName()).thenReturn("FCM"); + doNothing().when(fcmPushProvider).updateDevice(any(), any()); + + doNothing().when(deviceDAO).editDevice(anyString(), any()); + Mockito.clearInvocations(deviceDAO); + + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + + verify(fcmPushProvider, times(1)).updateDevice(any(), any()); + verify(deviceDAO, times(1)).editDevice(anyString(), any()); + } + } + + @Test + public void testEditDeviceMobileWithTokenOnly() + throws PushDeviceHandlerException, NotificationSenderManagementException, PushProviderException, + JOSEException, ParseException { + + try ( + MockedStatic mockedIdentityTenantUtil = + Mockito.mockStatic(IdentityTenantUtil.class); + MockedStatic mockedPushDeviceHandlerDataHolder = + Mockito.mockStatic(PushDeviceHandlerDataHolder.class); + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(any())).thenReturn(-1234); + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + claims.put("deviceToken", "newDeviceToken"); + when(mockClaimsSet.getClaims()).thenReturn(claims); + when(mockClaimsSet.getStringClaim("deviceToken")).thenReturn("newDeviceToken"); + + PushDeviceHandlerDataHolder pushDeviceHandlerDataHolder = mock(PushDeviceHandlerDataHolder.class); + mockedPushDeviceHandlerDataHolder.when(PushDeviceHandlerDataHolder::getInstance) + .thenReturn(pushDeviceHandlerDataHolder); + + NotificationSenderManagementService notificationSenderManagementService = + mock(NotificationSenderManagementService.class); + when(pushDeviceHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO pushSenderDTO = new PushSenderDTO(); + pushSenderDTO.setName("FCM_PushPublisher"); + pushSenderDTO.setProvider("FCM"); + pushSenderDTO.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(pushSenderDTO); + when(notificationSenderManagementService.getPushSenders(anyBoolean())) + .thenReturn(pushSenders); + + FCMPushProvider fcmPushProvider = mock(FCMPushProvider.class); + when(pushDeviceHandlerDataHolder.getPushProvider(eq("FCM"))).thenReturn(fcmPushProvider); + when(fcmPushProvider.getName()).thenReturn("FCM"); + doNothing().when(fcmPushProvider).updateDevice(any(), any()); + + doNothing().when(deviceDAO).editDevice(anyString(), any()); + Mockito.clearInvocations(deviceDAO); + + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + + verify(fcmPushProvider, times(1)).updateDevice(any(), any()); + verify(deviceDAO, times(1)).editDevice(anyString(), any()); + } + } + + @Test + public void testEditDeviceMobileWithNameOnly() + throws PushDeviceHandlerException, NotificationSenderManagementException, PushProviderException, + JOSEException, ParseException { + + try ( + MockedStatic mockedIdentityTenantUtil = + Mockito.mockStatic(IdentityTenantUtil.class); + MockedStatic mockedPushDeviceHandlerDataHolder = + Mockito.mockStatic(PushDeviceHandlerDataHolder.class); + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(any())).thenReturn(-1234); + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + claims.put("name", "newDeviceName"); + when(mockClaimsSet.getClaims()).thenReturn(claims); + when(mockClaimsSet.getStringClaim("name")).thenReturn("newDeviceName"); + + PushDeviceHandlerDataHolder pushDeviceHandlerDataHolder = mock(PushDeviceHandlerDataHolder.class); + mockedPushDeviceHandlerDataHolder.when(PushDeviceHandlerDataHolder::getInstance) + .thenReturn(pushDeviceHandlerDataHolder); + + NotificationSenderManagementService notificationSenderManagementService = + mock(NotificationSenderManagementService.class); + when(pushDeviceHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO pushSenderDTO = new PushSenderDTO(); + pushSenderDTO.setName("FCM_PushPublisher"); + pushSenderDTO.setProvider("FCM"); + pushSenderDTO.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(pushSenderDTO); + when(notificationSenderManagementService.getPushSenders(anyBoolean())) + .thenReturn(pushSenders); + + FCMPushProvider fcmPushProvider = mock(FCMPushProvider.class); + when(pushDeviceHandlerDataHolder.getPushProvider(eq("FCM"))).thenReturn(fcmPushProvider); + when(fcmPushProvider.getName()).thenReturn("FCM"); + doNothing().when(fcmPushProvider).updateDevice(any(), any()); + + doNothing().when(deviceDAO).editDevice(anyString(), any()); + Mockito.clearInvocations(deviceDAO); + + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + + verify(fcmPushProvider, times(1)).updateDevice(any(), any()); + verify(deviceDAO, times(1)).editDevice(anyString(), any()); + } + } + + @Test + public void testEditDeviceMobileDeviceNotFound() throws PushDeviceHandlerException { + + Optional device = Optional.empty(); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + + @Test + public void testEditDeviceMobileWithInvalidDeviceTokenClaimType() + throws PushDeviceHandlerException, JOSEException, ParseException { + + try ( + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + // Set deviceToken claim as a non-String type (Integer) to trigger validation failure. + Map claims = new HashMap<>(); + claims.put("deviceToken", 12345); + when(mockClaimsSet.getClaims()).thenReturn(claims); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileWithInvalidDeviceNameClaimType() + throws PushDeviceHandlerException, JOSEException, ParseException { + + try ( + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + // Set name claim as a non-String type (Boolean) to trigger validation failure. + Map claims = new HashMap<>(); + claims.put("name", true); + when(mockClaimsSet.getClaims()).thenReturn(claims); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileWithBothInvalidClaimTypes() + throws PushDeviceHandlerException, JOSEException, ParseException { + + try ( + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + // Set both claims as non-String types. The deviceToken check runs first. + Map claims = new HashMap<>(); + claims.put("deviceToken", 12345); + claims.put("name", 67890); + when(mockClaimsSet.getClaims()).thenReturn(claims); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileTokenValidationFailed() + throws PushDeviceHandlerException, JOSEException, ParseException { + + try ( + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(false); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileNoEditableFields() + throws PushDeviceHandlerException, JOSEException, ParseException { + + try ( + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + when(mockClaimsSet.getClaims()).thenReturn(claims); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileProviderClientException() + throws PushDeviceHandlerException, NotificationSenderManagementException, PushProviderException, + JOSEException, ParseException { + + try ( + MockedStatic mockedIdentityTenantUtil = + Mockito.mockStatic(IdentityTenantUtil.class); + MockedStatic mockedPushDeviceHandlerDataHolder = + Mockito.mockStatic(PushDeviceHandlerDataHolder.class); + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(any())).thenReturn(-1234); + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + claims.put("deviceToken", "newDeviceToken"); + when(mockClaimsSet.getClaims()).thenReturn(claims); + when(mockClaimsSet.getStringClaim("deviceToken")).thenReturn("newDeviceToken"); + + PushDeviceHandlerDataHolder pushDeviceHandlerDataHolder = mock(PushDeviceHandlerDataHolder.class); + mockedPushDeviceHandlerDataHolder.when(PushDeviceHandlerDataHolder::getInstance) + .thenReturn(pushDeviceHandlerDataHolder); + + NotificationSenderManagementService notificationSenderManagementService = + mock(NotificationSenderManagementService.class); + when(pushDeviceHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO pushSenderDTO = new PushSenderDTO(); + pushSenderDTO.setName("FCM_PushPublisher"); + pushSenderDTO.setProvider("FCM"); + pushSenderDTO.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(pushSenderDTO); + when(notificationSenderManagementService.getPushSenders(anyBoolean())) + .thenReturn(pushSenders); + + FCMPushProvider fcmPushProvider = mock(FCMPushProvider.class); + when(pushDeviceHandlerDataHolder.getPushProvider(eq("FCM"))).thenReturn(fcmPushProvider); + when(fcmPushProvider.getName()).thenReturn("FCM"); + PushProviderClientException ppce = new PushProviderClientException( + "ERR_DEVICE_UPDATE", "Failed to update device at provider"); + doThrow(ppce).when(fcmPushProvider).updateDevice(any(), any()); + + Assert.assertThrows(PushDeviceHandlerClientException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + + @Test + public void testEditDeviceMobileProviderServerException() + throws PushDeviceHandlerException, NotificationSenderManagementException, PushProviderException, + JOSEException, ParseException { + + try ( + MockedStatic mockedIdentityTenantUtil = + Mockito.mockStatic(IdentityTenantUtil.class); + MockedStatic mockedPushDeviceHandlerDataHolder = + Mockito.mockStatic(PushDeviceHandlerDataHolder.class); + MockedStatic mockedStatic = Mockito.mockStatic(SignedJWT.class) + ) { + + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(any())).thenReturn(-1234); + + Device deviceObj = new Device(); + deviceObj.setDeviceId("1234567890"); + deviceObj.setProvider("FCM"); + deviceObj.setDeviceToken(deviceToken); + deviceObj.setPublicKey(publicKey); + Optional device = Optional.of(deviceObj); + when(deviceDAO.getDevice(anyString())).thenReturn(device); + + mockedStatic.when(() -> SignedJWT.parse(validJwt)).thenReturn(mockSignedJWT); + + when(mockSignedJWT.getJWTClaimsSet()).thenReturn(mockClaimsSet); + when(mockClaimsSet.getExpirationTime()).thenReturn(new Date(System.currentTimeMillis() + 3000000)); + when(mockClaimsSet.getNotBeforeTime()).thenReturn(new Date(System.currentTimeMillis() - 3000000)); + when(mockSignedJWT.verify(any())).thenReturn(true); + + Map claims = new HashMap<>(); + claims.put("deviceToken", "newDeviceToken"); + when(mockClaimsSet.getClaims()).thenReturn(claims); + when(mockClaimsSet.getStringClaim("deviceToken")).thenReturn("newDeviceToken"); + + PushDeviceHandlerDataHolder pushDeviceHandlerDataHolder = mock(PushDeviceHandlerDataHolder.class); + mockedPushDeviceHandlerDataHolder.when(PushDeviceHandlerDataHolder::getInstance) + .thenReturn(pushDeviceHandlerDataHolder); + + NotificationSenderManagementService notificationSenderManagementService = + mock(NotificationSenderManagementService.class); + when(pushDeviceHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO pushSenderDTO = new PushSenderDTO(); + pushSenderDTO.setName("FCM_PushPublisher"); + pushSenderDTO.setProvider("FCM"); + pushSenderDTO.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(pushSenderDTO); + when(notificationSenderManagementService.getPushSenders(anyBoolean())) + .thenReturn(pushSenders); + + PushProviderServerException ppse = new PushProviderServerException( + "ERR_PROVIDER_SERVER", "Provider service temporarily unavailable"); + org.wso2.carbon.identity.notification.push.provider.PushProvider pushProvider = + mock(org.wso2.carbon.identity.notification.push.provider.PushProvider.class); + when(pushDeviceHandlerDataHolder.getPushProvider(eq("FCM"))).thenReturn(pushProvider); + when(pushProvider.getName()).thenReturn("FCM"); + doThrow(ppse).when(pushProvider).updateDevice(any(), any()); + + Assert.assertThrows(PushDeviceHandlerServerException.class, () -> { + deviceHandlerService.editDeviceMobile("1234567890", validJwt); + }); + } + } + @Test public void testGetRegistrationDiscoveryData() throws PushDeviceHandlerException { diff --git a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLoggerTest.java b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLoggerTest.java index 863fd22..a44a72b 100644 --- a/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLoggerTest.java +++ b/components/org.wso2.carbon.identity.notification.push.device.handler/src/test/java/org/wso2/carbon/identity/notification/push/device/handler/utils/DeviceHandlerAuditLoggerTest.java @@ -130,9 +130,11 @@ public void testGetUserWithSystemUser() throws Exception { public void testCreateAuditLogEntryWithValidData() throws Exception { Method createAuditLogEntryMethod = DeviceHandlerAuditLogger.class - .getDeclaredMethod("createAuditLogEntry", String.class); + .getDeclaredMethod("createAuditLogEntry", + DeviceHandlerAuditLogger.Operation.class, String.class); createAuditLogEntryMethod.setAccessible(true); - JSONObject result = (JSONObject) createAuditLogEntryMethod.invoke(auditLogger, "testUserId"); + JSONObject result = (JSONObject) createAuditLogEntryMethod.invoke( + auditLogger, DeviceHandlerAuditLogger.Operation.UNREGISTER_DEVICE, "testUserId"); Assert.assertNotNull(result); Assert.assertTrue(result.has("UserId")); @@ -141,6 +143,46 @@ public void testCreateAuditLogEntryWithValidData() throws Exception { Assert.assertTrue(result.getLong("UnregisteredAt") > 0); } + /** + * Test the private method 'createAuditLogEntry' with register device operation. + */ + @Test + public void testCreateAuditLogEntryWithRegisterDevice() throws Exception { + + Method createAuditLogEntryMethod = DeviceHandlerAuditLogger.class + .getDeclaredMethod("createAuditLogEntry", + DeviceHandlerAuditLogger.Operation.class, String.class); + createAuditLogEntryMethod.setAccessible(true); + JSONObject result = (JSONObject) createAuditLogEntryMethod.invoke( + auditLogger, DeviceHandlerAuditLogger.Operation.REGISTER_DEVICE, "testUserId"); + + Assert.assertNotNull(result); + Assert.assertTrue(result.has("UserId")); + Assert.assertEquals(result.getString("UserId"), "testUserId"); + Assert.assertTrue(result.has("RegisteredAt")); + Assert.assertTrue(result.getLong("RegisteredAt") > 0); + } + + /** + * Test the private method 'createAuditLogEntry' with update device operation. + */ + @Test + public void testCreateAuditLogEntryWithUpdateDevice() throws Exception { + + Method createAuditLogEntryMethod = DeviceHandlerAuditLogger.class + .getDeclaredMethod("createAuditLogEntry", + DeviceHandlerAuditLogger.Operation.class, String.class); + createAuditLogEntryMethod.setAccessible(true); + JSONObject result = (JSONObject) createAuditLogEntryMethod.invoke( + auditLogger, DeviceHandlerAuditLogger.Operation.UPDATE_DEVICE, "testUserId"); + + Assert.assertNotNull(result); + Assert.assertTrue(result.has("UserId")); + Assert.assertEquals(result.getString("UserId"), "testUserId"); + Assert.assertTrue(result.has("UpdatedAt")); + Assert.assertTrue(result.getLong("UpdatedAt") > 0); + } + /** * Test the private method 'getInitiatorId'. */