diff --git a/components/org.wso2.carbon.identity.recovery/pom.xml b/components/org.wso2.carbon.identity.recovery/pom.xml
index ad208cddc5..ba22577755 100644
--- a/components/org.wso2.carbon.identity.recovery/pom.xml
+++ b/components/org.wso2.carbon.identity.recovery/pom.xml
@@ -132,6 +132,17 @@
org.wso2.carbon.identity.governance
org.wso2.carbon.identity.multi.attribute.login.service
+
+ org.wso2.carbon.identity.auth.otp.commons
+ org.wso2.carbon.identity.auth.otp.core
+ provided
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.application.authentication.framework
+
+
+
@@ -188,6 +199,8 @@
version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.multi.attribute.login.service;
version="${identity.governance.imp.pkg.version.range}",
+ org.wso2.carbon.identity.auth.otp.core.model;
+ version="${identity.auth.otp.commons.version.range}",
*
diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java
index 595438fdc6..ec263f31c2 100644
--- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java
+++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java
@@ -73,6 +73,7 @@ public class IdentityRecoveryConstants {
public static final String RESEND_EMAIL_TEMPLATE_NAME = "resendTemplateName";
public static final String INITIATED_PLATFORM = "initiated-platform";
public static final String CONFIRMATION_CODE = "confirmation-code";
+ public static final String OTP_TOKEN = "otpToken";
public static final String VERIFICATION_PENDING_EMAIL = "verification-pending-email";
public static final String NEW_EMAIL_ADDRESS = "new-email-address";
public static final String NOTIFY = "notify";
@@ -134,6 +135,7 @@ public class IdentityRecoveryConstants {
public static final String NOTIFICATION_EVENTNAME_PREFIX = "TRIGGER_";
public static final String NOTIFICATION_EVENTNAME_SUFFIX = "_NOTIFICATION";
+ public static final String NOTIFICATION_EVENTNAME_SUFFIX_LOCAL = "_LOCAL";
public static final String SEND_TO = "send-to";
public static final String LOCALE_EN_US = "en_US";
public static final String LOCALE_LK_LK = "lk_lk";
@@ -229,6 +231,7 @@ public enum ErrorMessages {
ERROR_CODE_EMAIL_NOT_FOUND("18018", "Sending email address is not found for the user %s."),
ERROR_CODE_INVALID_FLOW_ID("18019", "Invalid flow confirmation code '%s'."),
ERROR_CODE_EXPIRED_FLOW_ID("18020", "Expired flow confirmation code '%s'."),
+ ERROR_CODE_MOBILE_NOT_FOUND("18021", "Mobile number is not found for the user %s."),
ERROR_CODE_INVALID_CREDENTIALS("17002", "Invalid Credentials"),
ERROR_CODE_LOCKED_ACCOUNT("17003", "User account is locked - '%s'."),
ERROR_CODE_DISABLED_ACCOUNT("17004", "user account is disabled '%s'."),
diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java
index 00df8c3705..ee68937bce 100644
--- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java
+++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java
@@ -25,6 +25,7 @@
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.common.model.User;
+import org.wso2.carbon.identity.auth.otp.core.model.OTP;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.event.IdentityEventConstants;
import org.wso2.carbon.identity.event.IdentityEventException;
@@ -267,8 +268,20 @@ private void triggerNotification(User user, String notificationChannel, String t
notificationChannel = NotificationChannels.EMAIL_CHANNEL.getChannelType();
}
properties.put(IdentityEventConstants.EventProperty.NOTIFICATION_CHANNEL, notificationChannel);
+ if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
+ String sendTo = Utils.getUserClaim(user, IdentityRecoveryConstants.MOBILE_NUMBER_CLAIM);
+ if (StringUtils.isEmpty(sendTo)) {
+ throw Utils.handleClientException(
+ IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_MOBILE_NOT_FOUND, user.getUserName());
+ }
+ properties.put(IdentityRecoveryConstants.SEND_TO, sendTo);
+ }
if (StringUtils.isNotBlank(code)) {
- properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
+ if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
+ properties.put(IdentityRecoveryConstants.OTP_TOKEN, new OTP(code, 0, 0));
+ } else {
+ properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
+ }
}
if (metaProperties != null) {
for (Property metaProperty : metaProperties) {
diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java
index 195414b47d..7ad7d572b8 100644
--- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java
+++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java
@@ -24,6 +24,7 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.identity.auth.otp.core.model.OTP;
import org.json.JSONObject;
import org.slf4j.MDC;
import org.wso2.carbon.base.MultitenantConstants;
@@ -58,16 +59,15 @@
import org.wso2.carbon.identity.recovery.store.UserRecoveryDataStore;
import org.wso2.carbon.identity.recovery.util.Utils;
import org.wso2.carbon.registry.core.Resource;
-import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
-import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -141,7 +141,8 @@ IdentityEventConstants.Event.PRE_SEND_RECOVERY_NOTIFICATION, new UserRecoveryDat
user.getUserName());
}
return new NotificationResponseBean(user);
- } else if (isExistingUser(user) && StringUtils.isEmpty(getEmail(user))) {
+ } else if (isExistingUser(user) && StringUtils.isEmpty(Utils.getUserClaim(user,
+ IdentityRecoveryConstants.EMAIL_ADDRESS_CLAIM))) {
/* If the email is not found for the user, Check for NOTIFY_RECOVERY_EMAIL_EXISTENCE property.
If the property is not enabled, notify with an empty NotificationResponseBean.*/
@@ -193,6 +194,16 @@ IdentityEventConstants.Event.PRE_SEND_RECOVERY_NOTIFICATION, new UserRecoveryDat
recoveryDataDO = generateNewConfirmationCode(user, notificationChannel);
}
secretKey = recoveryDataDO.getSecret();
+ if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
+ String sendTo = Utils.getUserClaim(user, IdentityRecoveryConstants.MOBILE_NUMBER_CLAIM);
+ if (StringUtils.isEmpty(sendTo)) {
+ /* If the mobile number is not found for the user, notify with an empty
+ NotificationResponseBean.*/
+ throw Utils.handleClientException(
+ IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_MOBILE_NOT_FOUND, user.getUserName());
+ }
+ properties = addMobileNumberToProperties(properties, sendTo);
+ }
NotificationResponseBean notificationResponseBean = new NotificationResponseBean(user);
if (isNotificationInternallyManage) {
// Manage notifications by the identity server.
@@ -1010,7 +1021,13 @@ private void triggerNotification(User user, String notificationChannel, String t
userRecoveryData.getRecoveryScenario().name());
}
if (StringUtils.isNotBlank(code)) {
- properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
+ if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
+ /* Generate time and validity period not added since only used to pass the otp code to notification
+ event handler. */
+ properties.put(IdentityRecoveryConstants.OTP_TOKEN, new OTP(code, 0,0));
+ } else {
+ properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
+ }
}
if (metaProperties != null) {
for (Property metaProperty : metaProperties) {
@@ -1099,6 +1116,9 @@ private void publishEvent(User user, String notify, String code, String password
}
if (StringUtils.isNotBlank(code)) {
properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
+ /* Generate time and validity period not added since only used to pass the otp code to notification
+ event handler. */
+ properties.put(IdentityRecoveryConstants.OTP_TOKEN, new OTP(code, 0,0));
}
if (StringUtils.isNotBlank(notify)) {
@@ -1289,35 +1309,13 @@ private boolean isAskPasswordEmailTemplateTypeExists(String tenantDomain) {
return templateType != null;
}
- /**
- * Retrieve email address of the user.
- *
- * @param user User the email need to be retrieved.
- * @return email address of the user.
- * @throws IdentityRecoveryServerException
- */
- private String getEmail(User user) throws IdentityRecoveryServerException {
+ private Property[] addMobileNumberToProperties(Property[] properties, String mobile) {
- String userStoreDomain = user.getUserStoreDomain();
- RealmService realmService = IdentityRecoveryServiceDataHolder.getInstance().getRealmService();
- try {
- UserRealm userRealm = realmService.getTenantUserRealm(IdentityTenantUtil.getTenantId(user.getTenantDomain()));
- UserStoreManager userStoreManager = userRealm.getUserStoreManager();
-
- if (userStoreManager == null) {
- throw new IdentityRecoveryServerException(String.format("userStoreManager is null for user: " +
- "%s in tenant domain : %s", user.getUserName(), user.getTenantDomain()));
- }
- if (StringUtils.isNotBlank(userStoreDomain) && !PRIMARY_DEFAULT_DOMAIN_NAME.equals(userStoreDomain)) {
- userStoreManager = ((AbstractUserStoreManager) userStoreManager).getSecondaryUserStoreManager(userStoreDomain);
- }
-
- return userStoreManager.getUserClaimValue(user.getUserName(), IdentityRecoveryConstants.EMAIL_ADDRESS_CLAIM, null);
-
- } catch (UserStoreException e) {
- String error = String.format("Error occurred while retrieving existing email address for user: " +
- "%s in tenant domain : %s", user.getUserName(), user.getTenantDomain());
- throw new IdentityRecoveryServerException(error, e);
+ if (ArrayUtils.isEmpty(properties)) {
+ properties = new Property[0];
}
+ Property[] newProperties = Arrays.copyOf(properties, properties.length + 1);
+ newProperties[properties.length] = new Property(IdentityRecoveryConstants.SEND_TO, mobile);
+ return newProperties;
}
}
diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java
index 50ca856066..4ee2f66146 100644
--- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java
+++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java
@@ -82,7 +82,6 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -94,6 +93,7 @@
import static org.wso2.carbon.identity.recovery.IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_REGISTRATION_OPTION;
import static org.wso2.carbon.identity.recovery.IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_USER_ATTRIBUTES_FOR_REGISTRATION;
import static org.wso2.carbon.identity.recovery.IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_UNEXPECTED_ERROR_VALIDATING_ATTRIBUTES;
+import static org.wso2.carbon.user.core.UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME;
import static org.wso2.carbon.utils.CarbonUtils.isLegacyAuditLogsDisabled;
/**
@@ -949,7 +949,8 @@ public static String resolveEventName(String notificationChannel) {
if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
return IdentityRecoveryConstants.NOTIFICATION_EVENTNAME_PREFIX + notificationChannel
- + IdentityRecoveryConstants.NOTIFICATION_EVENTNAME_SUFFIX;
+ + IdentityRecoveryConstants.NOTIFICATION_EVENTNAME_SUFFIX
+ + IdentityRecoveryConstants.NOTIFICATION_EVENTNAME_SUFFIX_LOCAL;
} else {
return IdentityEventConstants.Event.TRIGGER_NOTIFICATION;
}
@@ -1687,4 +1688,39 @@ private static String convertFailureReasonsToString(List mockedIdentityUtil;
private MockedStatic mockedJDBCRecoveryDataStore;
private MockedStatic mockedIdentityProviderManager;
+ private MockedStatic mockedIdentityTenantUtil;
@BeforeMethod
public void setUp() {
@@ -124,15 +135,18 @@ public void setUp() {
mockedIdentityUtil = Mockito.mockStatic(IdentityUtil.class);
mockedJDBCRecoveryDataStore = Mockito.mockStatic(JDBCRecoveryDataStore.class);
mockedIdentityProviderManager = Mockito.mockStatic(IdentityProviderManager.class);
+ mockedIdentityTenantUtil = Mockito.mockStatic(IdentityTenantUtil.class);
identityProviderManager = Mockito.mock(IdentityProviderManager.class);
authAttributeHandlerManager = Mockito.mock(AuthAttributeHandlerManager.class);
identityGovernanceService = Mockito.mock(IdentityGovernanceService.class);
otpGenerator = Mockito.mock(OTPGenerator.class);
+ realmService = Mockito.mock(RealmService.class);
IdentityRecoveryServiceDataHolder.getInstance().setIdentityEventService(identityEventService);
IdentityRecoveryServiceDataHolder.getInstance().setIdentityGovernanceService(identityGovernanceService);
IdentityRecoveryServiceDataHolder.getInstance().setOtpGenerator(otpGenerator);
IdentityRecoveryServiceDataHolder.getInstance().setAuthAttributeHandlerManager(authAttributeHandlerManager);
+ IdentityRecoveryServiceDataHolder.getInstance().setRealmService(realmService);
}
@@ -142,6 +156,7 @@ public void tearDown() {
mockedIdentityUtil.close();
mockedJDBCRecoveryDataStore.close();
mockedIdentityProviderManager.close();
+ mockedIdentityTenantUtil.close();
}
@BeforeTest
@@ -194,6 +209,13 @@ public void testResendConfirmationCode(String username, String userstore, String
mockConfigurations("true", enableInternalNotificationManagement);
mockJDBCRecoveryDataStore(userRecoveryData);
mockEmailTrigger();
+ when(realmService.getTenantUserRealm(anyInt())).thenReturn(userRealm);
+ when(userRealm.getUserStoreManager()).thenReturn(userStoreManager);
+ when(userStoreManager.getUserClaimValue(any(), eq(IdentityRecoveryConstants.MOBILE_NUMBER_CLAIM), any()))
+ .thenReturn(TEST_MOBILE_CLAIM_VALUE);
+ when(userStoreManager.getUserClaimValue(any(), eq(IdentityRecoveryConstants.EMAIL_ADDRESS_CLAIM), any()))
+ .thenReturn(TEST_CLAIM_VALUE);
+ mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(-1234);
NotificationResponseBean responseBean =
userSelfRegistrationManager.resendConfirmationCode(user, null);
diff --git a/pom.xml b/pom.xml
index 55fe0320e3..7dd8e68e14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -505,6 +505,11 @@
org.wso2.carbon.identity.multi.attribute.login.service
${project.version}
+
+ org.wso2.carbon.identity.auth.otp.commons
+ org.wso2.carbon.identity.auth.otp.core
+ ${identity.auth.otp.commons.version}
+
org.wso2.carbon.identity.governance
org.wso2.carbon.identity.multi.attribute.login.server.feature
@@ -714,6 +719,9 @@
4.7.50
[4.7.2, 5.0.0)
+ 1.0.3
+ [1.0.0, 2.0.0)
+
1.0.0