Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update recovery service for recovery V2 API #751

Merged
merged 34 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dabdf82
Update recovery service for recovery V2 API
Rashmini Aug 29, 2023
6a54c6e
Rename new table
Rashmini Aug 29, 2023
4eb2973
Update unit tests
Rashmini Aug 31, 2023
2cc465d
Fix comments
Rashmini Aug 31, 2023
9aa50d3
Address review comments
Rashmini Sep 1, 2023
f953882
Clear tables with recovery flow id
Rashmini Sep 4, 2023
ddf827b
Update DB column name
Rashmini Sep 7, 2023
1f7ac66
Update method name to match with the new column name
Rashmini Sep 7, 2023
ab489ff
Fix comments
Rashmini Sep 8, 2023
f47cf74
Fix minor issues
Rashmini Sep 8, 2023
bc26f3b
Add a constant for confirmation code separator
Rashmini Sep 8, 2023
c81f391
Fix minor issues
Rashmini Sep 8, 2023
4800862
Fix minor issues
Rashmini Sep 8, 2023
f4785bb
Add changes related to the foreign key added for recovery flow id
Rashmini Sep 8, 2023
5cea278
Add DB constants
Rashmini Sep 8, 2023
cbe3ddb
Remove unwanted line
Rashmini Sep 8, 2023
3c3418f
Fix formatting
Rashmini Sep 8, 2023
4db8173
Update recovery flow id and secret concat logic
Rashmini Sep 11, 2023
666deed
Merge branch 'wso2-extensions:master' into recovery-api-v2
Rashmini Sep 12, 2023
9aa3aa4
Fix compilation failure
Rashmini Sep 12, 2023
4c8c9f6
Fix comments
Rashmini Sep 12, 2023
8d05544
Merge branch 'wso2-extensions:master' into recovery-api-v2
Rashmini Sep 14, 2023
d2bf396
Add fallback logic for already initiated email link based recovery flows
Rashmini Sep 15, 2023
37d4be5
Merge remote-tracking branch 'origin/recovery-api-v2' into recovery-a…
Rashmini Sep 15, 2023
6162e8a
Fix formatting
Rashmini Sep 15, 2023
8cc2732
Fix formatting
Rashmini Sep 15, 2023
e2fb879
Merge branch 'wso2-extensions:master' into recovery-api-v2
Rashmini Sep 15, 2023
32a535a
Fix formatting
Rashmini Sep 15, 2023
d2feb17
Merge remote-tracking branch 'origin/recovery-api-v2' into recovery-a…
Rashmini Sep 15, 2023
76dd419
Refactor fallback logic
Rashmini Sep 19, 2023
d0c47fc
Merge branch 'wso2-extensions:master' into recovery-api-v2
Rashmini Sep 19, 2023
178ac2d
Update method comment
Rashmini Sep 19, 2023
93a0259
Merge remote-tracking branch 'origin/recovery-api-v2' into recovery-a…
Rashmini Sep 19, 2023
a516155
Fix comments
Rashmini Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public class IdentityRecoveryConstants {
public static final String NOTIFICATION_CHANNEL_PROPERTY_KEY = "notificationChannel";
public static final String VERIFIED_USER_PROPERTY_KEY = "verifiedUser";
public static final String MANAGE_NOTIFICATIONS_INTERNALLY_PROPERTY_KEY = "manageNotificationsInternally";
public static final String CONFIRMATION_CODE_SEPARATOR = ".";

// Recovery Scenarios.
public static final String USER_NAME_RECOVERY = "UNR";
Expand All @@ -166,6 +167,7 @@ public class IdentityRecoveryConstants {

public static final int SMS_OTP_CODE_LENGTH = 6;
public static final String ENABLE_DETAILED_ERROR_RESPONSE = "Recovery.ErrorMessage.EnableDetailedErrorMessages";
public static final int RECOVERY_FLOW_ID_DEFAULT_EXPIRY_TIME = 15;
// Recovery code given at the username and password recovery initiation.
public static final int RECOVERY_CODE_DEFAULT_EXPIRY_TIME = 1;
public static final int RESEND_CODE_DEFAULT_EXPIRY_TIME = 1;
Expand Down Expand Up @@ -217,6 +219,8 @@ public enum ErrorMessages {
ERROR_CODE_INVALID_TENANT("18016", "Invalid tenant '%s'."),
ERROR_CODE_CHALLENGE_QUESTION_NOT_FOUND("18017", "No challenge question found. %s"),
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_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'."),
Expand Down Expand Up @@ -298,6 +302,8 @@ public enum ErrorMessages {
ERROR_CODE_DISABLE_LITE_SIGN_UP("20060", "Lite sign up feature is disabled"),
ERROR_CODE_ERROR_DELETING_RECOVERY_DATA("20061", "Error deleting user recovery data of the tenant: %s"),
ERROR_CODE_ERROR_GETTING_CONNECTOR_CONFIG("20062", "Error while getting connector configurations"),
ERROR_CODE_STORING_RECOVERY_FLOW_DATA("20063", "Error while storing recovery data."),
ERROR_CODE_UPDATING_RECOVERY_FLOW_DATA("20064", "Error while updating recovery data."),

ERROR_CODE_ERROR_RETRIVING_CLAIM("18004", "Error when retrieving the locale claim of user '%s' of '%s' domain."),
ERROR_CODE_RECOVERY_DATA_NOT_FOUND_FOR_USER("18005", "Recovery data not found."),
Expand Down Expand Up @@ -366,6 +372,10 @@ public enum ErrorMessages {
ERROR_CODE_EXPIRED_RECOVERY_CODE("UAR-10013", "Invalid recovery code: '%s'"),
ERROR_CODE_USER_ACCOUNT_RECOVERY_VALIDATION_FAILED("UAR-10014",
"User account recovery validation failed for user account: '%s'"),
ERROR_CODE_INVALID_RECOVERY_FLOW_ID("UAR-10015", "Invalid confirmation code : '%s'."),
ERROR_CODE_EXPIRED_RECOVERY_FLOW_ID("UAR-10016", "Expired confirmation code : '%s'."),
ERROR_CODE_NO_RECOVERY_FLOW_DATA("UAR-10018", "No recovery flow data found for "
+ "recovery flow id : '%s'."),
ERROR_CODE_ERROR_STORING_RECOVERY_DATA("UAR-15001", "Error storing user recovery data"),
ERROR_CODE_ERROR_GETTING_USERSTORE_MANAGER("UAR-15002", "Error getting userstore manager"),
ERROR_CODE_ERROR_RETRIEVING_USER_CLAIM("UAR-15003", "Error getting the claims: '%s' "
Expand Down Expand Up @@ -620,13 +630,36 @@ public static class ConnectorConfig {
public static final String ENABLE_AUTO_LGOIN_AFTER_PASSWORD_RESET = "Recovery.AutoLogin.Enable";
public static final String SELF_REGISTRATION_AUTO_LOGIN = "SelfRegistration.AutoLogin.Enable";
public static final String SELF_REGISTRATION_AUTO_LOGIN_ALIAS_NAME = "SelfRegistration.AutoLogin.AliasName";
public static final String RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS = "Recovery.OTP" +
".Password.MaxFailedAttempts";
public static final String RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS = "Recovery.OTP" +
".Password.MaxResendAttempts";
}

public static class DBConstants {

public static final String USER_NAME = "USER_NAME";
public static final String TENANT_ID = "TENANT_ID";
public static final String USER_DOMAIN = "USER_DOMAIN";
public static final String CODE = "CODE";
public static final String SCENARIO = "SCENARIO";
public static final String REMAINING_SETS = "REMAINING_SETS";
public static final String RECOVERY_FLOW_ID = "RECOVERY_FLOW_ID";
public static final String FAILED_ATTEMPTS = "FAILED_ATTEMPTS";
public static final String RESEND_COUNT = "RESEND_COUNT";
public static final String TIME_CREATED = "TIME_CREATED";
}

public static class SQLQueries {

public static final String STORE_RECOVERY_DATA = "INSERT INTO IDN_RECOVERY_DATA "
+ "(USER_NAME, USER_DOMAIN, TENANT_ID, CODE, SCENARIO,STEP, TIME_CREATED, REMAINING_SETS)"
+ "VALUES (?,?,?,?,?,?,?,?)";

public static final String STORE_RECOVERY_DATA_WITH_FLOW_ID = "INSERT INTO IDN_RECOVERY_DATA "
+ "(USER_NAME, USER_DOMAIN, TENANT_ID, CODE, SCENARIO,STEP, TIME_CREATED, REMAINING_SETS, " +
Rashmini marked this conversation as resolved.
Show resolved Hide resolved
"RECOVERY_FLOW_ID) VALUES (?,?,?,?,?,?,?,?,?)";

public static final String LOAD_RECOVERY_DATA = "SELECT "
+ "* FROM IDN_RECOVERY_DATA WHERE USER_NAME = ? AND USER_DOMAIN = ? AND TENANT_ID = ? AND CODE = ? AND " +
"SCENARIO = ? AND STEP = ?";
Expand All @@ -637,6 +670,9 @@ public static class SQLQueries {

public static final String LOAD_RECOVERY_DATA_FROM_CODE = "SELECT * FROM IDN_RECOVERY_DATA WHERE CODE = ?";

public static final String LOAD_RECOVERY_DATA_FROM_RECOVERY_FLOW_ID = "SELECT * FROM IDN_RECOVERY_DATA WHERE" +
" RECOVERY_FLOW_ID = ? AND STEP = ?";

public static final String INVALIDATE_CODE = "DELETE FROM IDN_RECOVERY_DATA WHERE CODE = ?";

public static final String INVALIDATE_USER_CODES =
Expand Down Expand Up @@ -682,6 +718,24 @@ public static class SQLQueries {
public static final String LOAD_RECOVERY_DATA_OF_USER_BY_STEP_CASE_INSENSITIVE = "SELECT "
+ "* FROM IDN_RECOVERY_DATA WHERE LOWER(USER_NAME)=LOWER(?) AND SCENARIO = ? AND USER_DOMAIN = ? " +
"AND TENANT_ID = ? AND STEP = ?";

public static final String STORE_RECOVERY_FLOW_DATA = "INSERT INTO IDN_RECOVERY_FLOW_DATA "
+ "(RECOVERY_FLOW_ID, CODE, FAILED_ATTEMPTS, RESEND_COUNT, TIME_CREATED) VALUES (?,?,?,?,?)";

public static final String UPDATE_RECOVERY_FLOW_DATA = "UPDATE IDN_RECOVERY_FLOW_DATA SET CODE = ? "
+ "WHERE RECOVERY_FLOW_ID = ?";

public static final String UPDATE_FAILED_ATTEMPTS = "UPDATE IDN_RECOVERY_FLOW_DATA SET FAILED_ATTEMPTS = ? "
+ "WHERE RECOVERY_FLOW_ID = ?";

public static final String UPDATE_CODE_RESEND_COUNT = "UPDATE IDN_RECOVERY_FLOW_DATA SET RESEND_COUNT = ? "
+ "WHERE RECOVERY_FLOW_ID = ?";

public static final String LOAD_RECOVERY_FLOW_DATA_FROM_RECOVERY_FLOW_ID = "SELECT * " +
"FROM IDN_RECOVERY_FLOW_DATA WHERE RECOVERY_FLOW_ID = ?";

public static final String INVALIDATE_BY_RECOVERY_FLOW_ID = "DELETE FROM IDN_RECOVERY_FLOW_DATA WHERE " +
"RECOVERY_FLOW_ID = ?";
}

public static class Questions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.wso2.carbon.identity.recovery.internal.service.impl.UserAccountRecoveryManager;
import org.wso2.carbon.identity.recovery.model.Property;
import org.wso2.carbon.identity.recovery.model.UserRecoveryData;
import org.wso2.carbon.identity.recovery.model.UserRecoveryFlowData;
import org.wso2.carbon.identity.recovery.store.JDBCRecoveryDataStore;
import org.wso2.carbon.identity.recovery.store.UserRecoveryDataStore;
import org.wso2.carbon.identity.recovery.util.Utils;
Expand Down Expand Up @@ -147,6 +148,19 @@ public ResendConfirmationDTO resendConfirmation(String tenantDomain, String rese
UserRecoveryData userRecoveryData = userAccountRecoveryManager
.getUserRecoveryData(resendCode, RecoverySteps.RESEND_CONFIRMATION_CODE);
User user = userRecoveryData.getUser();
String recoveryFlowId = userRecoveryData.getRecoveryFlowId();
UserRecoveryFlowData userRecoveryFlowData = userAccountRecoveryManager.loadUserRecoveryFlowData(
userRecoveryData);
int resendCount = userRecoveryFlowData.getResendCount();
if (resendCount >= Integer.parseInt(Utils.getRecoveryConfigs(IdentityRecoveryConstants.ConnectorConfig.
Rashmini marked this conversation as resolved.
Show resolved Hide resolved
RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS, tenantDomain))) {
userAccountRecoveryManager.invalidateRecoveryData(recoveryFlowId);
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID.getCode(),
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID.getMessage(),
recoveryFlowId);
}
userAccountRecoveryManager.updateRecoveryDataResendCount(recoveryFlowId, resendCount + 1);

// Validate the tenant domain and the recovery scenario in the request.
validateRequestAttributes(user, scenario, userRecoveryData.getRecoveryScenario(), tenantDomain, resendCode);
Expand All @@ -164,8 +178,10 @@ public ResendConfirmationDTO resendConfirmation(String tenantDomain, String rese
} else {
userRecoveryDataStore.invalidate(user);
confirmationCode = Utils.generateSecretKey(notificationChannel, user.getTenantDomain(), recoveryScenario);
confirmationCode = Utils.concatRecoveryFlowIdWithSecretKey(recoveryFlowId, notificationChannel,
confirmationCode);
// Store new confirmation code.
addRecoveryDataObject(confirmationCode, notificationChannel, scenario, step, user);
addRecoveryDataObject(confirmationCode, recoveryFlowId, notificationChannel, scenario, step, user);
}
ResendConfirmationDTO resendConfirmationDTO = new ResendConfirmationDTO();

Expand All @@ -185,6 +201,7 @@ public ResendConfirmationDTO resendConfirmation(String tenantDomain, String rese
IdentityRecoveryConstants.SuccessEvents.SUCCESS_STATUS_CODE_RESEND_CONFIRMATION_CODE.getCode());
resendConfirmationDTO.setSuccessMessage(
IdentityRecoveryConstants.SuccessEvents.SUCCESS_STATUS_CODE_RESEND_CONFIRMATION_CODE.getMessage());
resendConfirmationDTO.setRecoveryFlowId(recoveryFlowId);
return resendConfirmationDTO;
}

Expand Down Expand Up @@ -274,15 +291,16 @@ private String generateResendCode(String notificationChannel, RecoveryScenarios
UserRecoveryData userRecoveryData) throws IdentityRecoveryServerException {

String resendCode = UUID.randomUUID().toString();
String recoveryFlowId = userRecoveryData.getRecoveryFlowId();
/* Checking whether the existing confirmation code issued time is in the tolerance period. If so this code
updates the existing RESEND_CONFIRMATION_CODE with the new one by not changing the TIME_CREATED. */
if (Utils.reIssueExistingConfirmationCode(getResendConfirmationCodeData(userRecoveryData.getUser()),
notificationChannel)){
invalidateResendConfirmationCode(resendCode, notificationChannel, userRecoveryData);
return resendCode;
}
addRecoveryDataObject(resendCode, notificationChannel, scenario, RecoverySteps.RESEND_CONFIRMATION_CODE,
userRecoveryData.getUser());
addRecoveryDataObject(resendCode, recoveryFlowId, notificationChannel, scenario,
RecoverySteps.RESEND_CONFIRMATION_CODE, userRecoveryData.getUser());
return resendCode;
}

Expand Down Expand Up @@ -336,23 +354,28 @@ private void invalidateResendConfirmationCode(String resendCode, String notifica
* Add the notification channel recovery data to the store.
*
* @param secretKey RecoveryId
* @param recoveryFlowId Recovery flow ID.
* @param recoveryData Data to be stored as mata which are needed to evaluate the recovery data object
* @param recoveryScenario Recovery scenario
* @param recoveryStep Recovery step
* @param user User object
* @throws IdentityRecoveryServerException Error storing recovery data
*/
private void addRecoveryDataObject(String secretKey, String recoveryData, RecoveryScenarios recoveryScenario,
RecoverySteps recoveryStep, User user)
private void addRecoveryDataObject(String secretKey, String recoveryFlowId, String recoveryData,
Rashmini marked this conversation as resolved.
Show resolved Hide resolved
RecoveryScenarios recoveryScenario, RecoverySteps recoveryStep, User user)
throws IdentityRecoveryServerException {

UserRecoveryData recoveryDataDO = new UserRecoveryData(user, secretKey, recoveryScenario, recoveryStep);
UserRecoveryData recoveryDataDO = new UserRecoveryData(user, recoveryFlowId, secretKey, recoveryScenario, recoveryStep);

// Store available channels in remaining setIDs.
recoveryDataDO.setRemainingSetIds(recoveryData);
try {
UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance();
userRecoveryDataStore.store(recoveryDataDO);
if (StringUtils.equals(RecoverySteps.UPDATE_PASSWORD.name(), String.valueOf(recoveryStep))) {
userRecoveryDataStore.storeConfirmationCode(recoveryDataDO);
} else {
userRecoveryDataStore.store(recoveryDataDO);
}
} catch (IdentityRecoveryException e) {
throw Utils.handleServerException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_ERROR_STORING_RECOVERY_DATA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ public Map<String, String> getPropertyNameMapping() {
"Recovery callback URL regex");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_AUTO_LGOIN_AFTER_PASSWORD_RESET,
"Enable Auto Login After Password Reset");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS,
"Max failed attempts for OTP based recovery");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS,
"Max resend attempts for OTP based recovery");
return nameMapping;
}

Expand Down Expand Up @@ -168,6 +172,8 @@ public String[] getPropertyNames() {
properties.add(IdentityRecoveryConstants.ConnectorConfig.FORCE_MIN_NO_QUESTION_ANSWERED);
properties.add(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_CALLBACK_REGEX);
properties.add(IdentityRecoveryConstants.ConnectorConfig.ENABLE_AUTO_LGOIN_AFTER_PASSWORD_RESET);
properties.add(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS);
properties.add(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS);
return properties.toArray(new String[0]);
}

Expand All @@ -194,6 +200,8 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG
String minimumForcedChallengeQuestionsAnswered = "1";
String recoveryCallbackRegex = IdentityRecoveryConstants.DEFAULT_CALLBACK_REGEX;
String enableAdminPasswordResetAutoLoginProperty = "false";
String recoveryOTPMaxFailedAttempts = "3";
String recoveryOTPMaxResendAttempts = "5";

String notificationBasedPasswordRecovery = IdentityUtil.getProperty(
IdentityRecoveryConstants.ConnectorConfig.NOTIFICATION_BASED_PW_RECOVERY);
Expand Down Expand Up @@ -234,6 +242,10 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG
IdentityRecoveryConstants.ConnectorConfig.RECOVERY_CALLBACK_REGEX);
String adminPasswordResetAutoLoginProperty = IdentityUtil.getProperty(
IdentityRecoveryConstants.ConnectorConfig.ENABLE_AUTO_LGOIN_AFTER_PASSWORD_RESET);
String otpMaxFailedAttempts = IdentityUtil.getProperty(IdentityRecoveryConstants.
ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS);
String otpMaxResendAttempts = IdentityUtil.getProperty(IdentityRecoveryConstants.
ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS);

if (StringUtils.isNotEmpty(expiryTimeSMSOTPProperty)) {
expiryTimeSMSOTP = expiryTimeSMSOTPProperty;
Expand Down Expand Up @@ -295,6 +307,12 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG
if (StringUtils.isNotEmpty(adminPasswordResetAutoLoginProperty)) {
enableAdminPasswordResetAutoLoginProperty = adminPasswordResetAutoLoginProperty;
}
if (StringUtils.isNotEmpty(otpMaxFailedAttempts)) {
recoveryOTPMaxFailedAttempts = otpMaxFailedAttempts;
}
if (StringUtils.isNotEmpty(otpMaxResendAttempts)) {
recoveryOTPMaxResendAttempts = otpMaxResendAttempts;
}

Map<String, String> defaultProperties = new HashMap<>();
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.NOTIFICATION_BASED_PW_RECOVERY,
Expand Down Expand Up @@ -336,6 +354,10 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_CALLBACK_REGEX, recoveryCallbackRegex);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_AUTO_LGOIN_AFTER_PASSWORD_RESET,
enableAdminPasswordResetAutoLoginProperty);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS,
recoveryOTPMaxFailedAttempts);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS,
recoveryOTPMaxResendAttempts);

Properties properties = new Properties();
properties.putAll(defaultProperties);
Expand Down Expand Up @@ -411,6 +433,12 @@ public Map<String, Property> getMetaData() {
meta.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_CALLBACK_REGEX,
getPropertyObject(IdentityMgtConstants.DataTypes.STRING.getValue()));

meta.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS,
getPropertyObject(IdentityMgtConstants.DataTypes.INTEGER.getValue()));

meta.put(IdentityRecoveryConstants.ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_RESEND_ATTEMPTS,
getPropertyObject(IdentityMgtConstants.DataTypes.INTEGER.getValue()));

return meta;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public class PasswordRecoverDTO {
*/
private String message;

/**
* Recovery flow id.
*/
private String recoveryFlowId;

/**
* User notified channel.
*/
Expand Down Expand Up @@ -108,6 +113,26 @@ public void setMessage(String message) {
this.message = message;
}

/**
* Get the recovery flow id.
*
* @return Recovery flow id.
*/
public String getRecoveryFlowId() {

return recoveryFlowId;
}

/**
* Set the recovery flow id.
*
* @param recoveryFlowId Recovery flow id.
*/
public void setRecoveryFlowId(String recoveryFlowId) {

this.recoveryFlowId = recoveryFlowId;
}

/**
* Get the channel which the notification was sent.
*
Expand Down
Loading