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 1 commit
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 @@ -931,58 +931,6 @@ public UserRecoveryData getUserRecoveryData(String code, RecoverySteps step) thr
return recoveryData;
}

/**
* This method is to retrieve user recovery data using the confirmation code when there's no recovery flow id.
* This is added as a fallback logic to handle the already initiated email link based recovery flows which do not
* have recovery flow ids, which were initiated before moving to the Recovery V2 API.
* This shouldn't be used for any other purpose and should be kept for sometime.
*
* @param code Code given for recovery.
* @param recoveryFlowId Recovery flow id of the user.
* @param step Recovery step.
* @return UserRecoveryData Data associated with the provided code.
* @throws IdentityRecoveryException If an error occurred while validating the recoveryId.
*/
@Deprecated
public UserRecoveryData getUserRecoveryDataFromConfirmationCode(String code, String recoveryFlowId,
RecoverySteps step)
throws IdentityRecoveryException {

UserRecoveryData recoveryData;
UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance();
try {
// Retrieve recovery data bound to the recoveryId.
recoveryData = userRecoveryDataStore.load(code);
} catch (IdentityRecoveryException e) {
// Map code expired error to new error codes for user account recovery.
if (IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE.getCode().equals(e.getErrorCode())) {
e.setErrorCode(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID.getCode());
} else if (IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_EXPIRED_CODE.getCode()
.equals(e.getErrorCode())) {
e.setErrorCode(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_EXPIRED_RECOVERY_FLOW_ID.getCode());
} else {
e.setErrorCode(Utils.prependOperationScenarioToErrorCode(e.getErrorCode(),
IdentityRecoveryConstants.USER_ACCOUNT_RECOVERY));
}
throw e;
}
if (recoveryData == null) {
throw Utils
.handleClientException(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_ACCOUNT_RECOVERY_DATA,
recoveryFlowId);
}
if (!StringUtils.equals(recoveryData.getRemainingSetIds(),
NotificationChannels.EMAIL_CHANNEL.getChannelType())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID, recoveryFlowId);
}
if (!step.equals(recoveryData.getRecoveryStep())) {
throw Utils.handleClientException(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE,
recoveryFlowId);
}
return recoveryData;
}

/**
* Get user recovery data using the recovery flow id.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ public PasswordResetCodeDTO confirm(String otp, String confirmationCode, String
recoveryFlowId = confirmationCode;
code = otp;
}
// Get Recovery data.
try {
UserRecoveryData userRecoveryData = userAccountRecoveryManager
.getUserRecoveryDataFromFlowId(recoveryFlowId, RecoverySteps.UPDATE_PASSWORD);
Expand Down Expand Up @@ -271,21 +270,9 @@ public PasswordResetCodeDTO confirm(String otp, String confirmationCode, String
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_CODE.getCode(),
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE.getMessage(), code);
} catch (IdentityRecoveryException e) {
// This is a fallback logic to support already initiated email link based recovery flows using the
// recovery V1 API, which do not have recovery flow ids.
UserRecoveryData userRecoveryData = userAccountRecoveryManager.getUserRecoveryDataFromConfirmationCode(
recoveryFlowId, recoveryFlowId, RecoverySteps.UPDATE_PASSWORD);
if (!tenantDomain.equals(userRecoveryData.getUser().getTenantDomain())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_USER_TENANT_DOMAIN_MISS_MATCH_WITH_CONTEXT,
tenantDomain);
}
String domainQualifiedName = IdentityUtil.addDomainToName(userRecoveryData.getUser().getUserName(),
userRecoveryData.getUser().getUserStoreDomain());
if (log.isDebugEnabled()) {
log.debug("Valid confirmation code for user: " + domainQualifiedName);
}
return buildPasswordResetCodeDTO(recoveryFlowId);
/* This is a fallback logic to support already initiated email link based recovery flows using the
recovery V1 API, which do not have recovery flow ids. */
return validateConfirmationCode(userAccountRecoveryManager, recoveryFlowId, tenantDomain);
}
}

Expand Down Expand Up @@ -874,4 +861,48 @@ private boolean isMinNoOfRecoveryQuestionsAnswered(String username, String tenan
return isMinNoOfRecoveryQuestionsAnswered;
}

/**
* This method is to validate the confirmation code when there's no recovery flow id. This is added as a fallback
* logic to handle the already initiated email link based recovery flows which do not have recovery flow ids,
* which were initiated before moving to the Recovery V2 API. This shouldn't be used for any other purpose and
* should be kept for sometime.
*
* @param userAccountRecoveryManager UserAccountRecoveryManager.
* @param confirmationCode Confirmation code.
* @param tenantDomain Tenant domain.
* @return PasswordResetCodeDTO {@link PasswordResetCodeDTO} object which contains password reset code.
* @throws IdentityRecoveryException Error while confirming password recovery.
*/
@Deprecated
private PasswordResetCodeDTO validateConfirmationCode(UserAccountRecoveryManager userAccountRecoveryManager,
String confirmationCode, String tenantDomain)
throws IdentityRecoveryException {
UserRecoveryData userRecoveryData;
Rashmini marked this conversation as resolved.
Show resolved Hide resolved
try {
userRecoveryData = userAccountRecoveryManager.getUserRecoveryData(confirmationCode,
RecoverySteps.UPDATE_PASSWORD);
} catch (IdentityRecoveryException e) {
if (IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_CODE.getCode().equals(
e.getErrorCode())) {
e.setErrorCode(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID.getCode());
}
throw e;
}
if (!StringUtils.equals(userRecoveryData.getRemainingSetIds(),
NotificationChannels.EMAIL_CHANNEL.getChannelType())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID, confirmationCode);
}
if (!tenantDomain.equals(userRecoveryData.getUser().getTenantDomain())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_USER_TENANT_DOMAIN_MISS_MATCH_WITH_CONTEXT,
tenantDomain);
}
String domainQualifiedName = IdentityUtil.addDomainToName(userRecoveryData.getUser().getUserName(),
userRecoveryData.getUser().getUserStoreDomain());
if (log.isDebugEnabled()) {
log.debug("Valid confirmation code for user: " + domainQualifiedName);
}
return buildPasswordResetCodeDTO(confirmationCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,6 @@ public User updateUserPassword(String code, String confirmationCode, String pass
throws IdentityRecoveryException, IdentityEventException {

UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance();
UserAccountRecoveryManager userAccountRecoveryManager = UserAccountRecoveryManager.getInstance();
UserRecoveryData userRecoveryData;
try {
userRecoveryData = userRecoveryDataStore.loadFromRecoveryFlowId(confirmationCode,
Expand All @@ -687,20 +686,9 @@ public User updateUserPassword(String code, String confirmationCode, String pass
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE.getMessage(), code);
}
} catch (IdentityRecoveryException e) {
// This is a fallback logic to support already initiated email link based recovery flows using the
// recovery V1 API, which do not have recovery flow ids.
userRecoveryData = userAccountRecoveryManager.getUserRecoveryDataFromConfirmationCode(code,
confirmationCode, RecoverySteps.UPDATE_PASSWORD);
validateCallback(properties, userRecoveryData.getUser().getTenantDomain());
publishEvent(userRecoveryData.getUser(), null, code, password, properties,
IdentityEventConstants.Event.PRE_ADD_NEW_PASSWORD, userRecoveryData);
validateTenantDomain(userRecoveryData.getUser());

// Validate recovery step.
if (!RecoverySteps.UPDATE_PASSWORD.equals(userRecoveryData.getRecoveryStep())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE, code);
}
/* This is a fallback logic to support already initiated email link based recovery flows using the
recovery V1 API, which do not have recovery flow ids. */
userRecoveryData = validateUserRecoveryDataFromCode(code, confirmationCode, password, properties);
}

// Get the notification channel.
Expand Down Expand Up @@ -766,6 +754,52 @@ public User updateUserPassword(String code, String confirmationCode, String pass
return userRecoveryData.getUser();
}

/**
* This method is to validate user recovery data using the reset code when there's no recovery flow id.
* This is added as a fallback logic to handle the already initiated email link based recovery flows which do not
* have recovery flow ids, which were initiated before moving to the Recovery V2 API.
* This shouldn't be used for any other purpose and should be kept for sometime.
*
* @param userAccountRecoveryManager UserAccountRecoveryManager.
* @param code Password Reset code.
* @param confirmationCode Confirmation code.
* @param password New password.
* @param properties Properties.
* @return UserRecoveryData.
* @throws IdentityRecoveryException Error while updating the password.
*/
@Deprecated
private UserRecoveryData validateUserRecoveryDataFromCode(String code, String confirmationCode, String password,
Property[] properties) throws IdentityRecoveryException {
UserRecoveryData userRecoveryData;
Rashmini marked this conversation as resolved.
Show resolved Hide resolved
UserAccountRecoveryManager userAccountRecoveryManager = UserAccountRecoveryManager.getInstance();
try {
userRecoveryData = userAccountRecoveryManager.getUserRecoveryData(code, RecoverySteps.UPDATE_PASSWORD);
} catch (IdentityRecoveryException e) {
if (IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_CODE.getCode().equals(
e.getErrorCode())) {
e.setErrorCode(IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID.getCode());
}
throw e;
}
if (!StringUtils.equals(userRecoveryData.getRemainingSetIds(),
NotificationChannels.EMAIL_CHANNEL.getChannelType())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_RECOVERY_FLOW_ID, confirmationCode);
}
validateCallback(properties, userRecoveryData.getUser().getTenantDomain());
publishEvent(userRecoveryData.getUser(), null, code, password, properties,
IdentityEventConstants.Event.PRE_ADD_NEW_PASSWORD, userRecoveryData);
validateTenantDomain(userRecoveryData.getUser());

// Validate recovery step.
if (!RecoverySteps.UPDATE_PASSWORD.equals(userRecoveryData.getRecoveryStep())) {
throw Utils.handleClientException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_INVALID_CODE, code);
}
return userRecoveryData;
}

/**
* Update the new password of the user.
*
Expand Down