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

Support storing multiple mobile numbers and email addresses per user #817

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a6f6325
support multiple mobile numbers per user
lashinijay Apr 19, 2024
d97eaab
support multiple mobile numbers per user
lashinijay Apr 19, 2024
3968a6a
add method descriptions
lashinijay Apr 24, 2024
f52b461
fix validation when mobile number update on verification is turned off
lashinijay Apr 29, 2024
5e09068
fix validation when mobile number update on verification is turned off
lashinijay May 20, 2024
2811945
add multiple email addresses and verified email addresses per user su…
lashinijay May 29, 2024
63e822d
fix the logic for when verification on update is disabled
lashinijay May 29, 2024
b5ab7c7
fix imports
lashinijay May 29, 2024
e79adba
Merge branch 'refs/heads/support-multiple-email-addresses' into mulit…
lashinijay May 30, 2024
d63a79b
improvements for support multiple mobile numbers and email address co…
lashinijay May 30, 2024
a32a375
drop email addresses and mobile numbers claim when multiple mobile an…
lashinijay May 30, 2024
5cc6909
logic improvements
lashinijay May 30, 2024
4c19b27
add supportMultiEmailsAndMobileNumbers config as a governance config
lashinijay Jun 3, 2024
88e24a9
code refactoring
lashinijay Jun 4, 2024
4b619b7
code refactoring
lashinijay Jun 4, 2024
b24c93c
code refactoring
lashinijay Jun 4, 2024
0afcb52
address comments
lashinijay Jun 6, 2024
caa6430
address comments
lashinijay Jun 4, 2024
ad92924
update unit tests
lashinijay Jun 6, 2024
a633496
Update components/org.wso2.carbon.identity.recovery/src/test/java/org…
lashinijay Jun 6, 2024
d8f0d29
fix deleting primary email address and mobile number flow
lashinijay Jul 8, 2024
9bb8ffc
Merge branch 'master' into mulitple-mobile-number-support
lashinijay Sep 18, 2024
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
@@ -1,17 +1,19 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* Copyright (c) 2016-2024, WSO2 LLC. (http://www.wso2.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.recovery;
Expand Down Expand Up @@ -121,6 +123,10 @@ public class IdentityRecoveryConstants {
public static final String USER_ROLES_CLAIM = "http://wso2.org/claims/roles";
public static final String EMAIL_ADDRESS_CLAIM = "http://wso2.org/claims/emailaddress";
public static final String MOBILE_NUMBER_CLAIM = "http://wso2.org/claims/mobile";
public static final String MOBILE_NUMBERS_CLAIM = "http://wso2.org/claims/mobileNumbers";
public static final String VERIFIED_MOBILE_NUMBERS_CLAIM = "http://wso2.org/claims/verifiedMobileNumbers";
public static final String EMAIL_ADDRESSES_CLAIM = "http://wso2.org/claims/emailAddresses";
public static final String VERIFIED_EMAIL_ADDRESSES_CLAIM = "http://wso2.org/claims/verifiedEmailAddresses";
public static final String DEFAULT_CHALLENGE_QUESTION_SEPARATOR = "!";
public static final String ACCOUNT_STATE_CLAIM_URI = "http://wso2.org/claims/identity/accountState";
public static final String PENDING_SELF_REGISTRATION = "PENDING_SR";
Expand Down Expand Up @@ -440,9 +446,34 @@ public enum ErrorMessages {

// UEV - User Email Verification.
ERROR_CODE_VERIFICATION_EMAIL_NOT_FOUND("UEV-10001", "Email address not found for email verification"),
ERROR_CODE_EMAIL_VERIFICATION_NOT_ENABLED("UEV-10002", "Email verification is not enabled"),
ERROR_CODE_VERIFY_MULTIPLE_EMAILS("UEV-10003", "Unable to verify multiple email addresses " +
"simultaneously"),
ERROR_CODE_SUPPORT_MULTIPLE_EMAILS_NOT_ENABLED("UEV-10004", "Support for multiple email addresses " +
"per user is not enabled"),
ERROR_CODE_PRIMARY_EMAIL_SHOULD_BE_INCLUDED_IN_EMAILS_LIST("UEV-10005", "As multiple " +
"email addresses support is enabled, primary email address should be included in the email " +
"addresses list."),
ERROR_CODE_PRIMARY_EMAIL_SHOULD_BE_INCLUDED_IN_VERIFIED_EMAILS_LIST("UEV-10006", "As multiple " +
"email addresses support and email verification is enabled, primary email address should be included " +
"in the verified email addresses list."),

// UMV - User Mobile Verification.
ERROR_CODE_MOBILE_VERIFICATION_NOT_ENABLED("UMV-10001", " Verified mobile numbers claim cannot be" +
" updated as mobile number verification on update is disabled."),
ERROR_CODE_VERIFY_MULTIPLE_MOBILE_NUMBERS("UMV-10002", "Unable to verify " +
"multiple mobile numbers simultaneously."),
ERROR_CODE_SUPPORT_MULTIPLE_MOBILE_NUMBERS_NOT_ENABLED("UEV-10003", "Support for multiple mobile " +
"numbers per user is not enabled"),
ERROR_CODE_PRIMARY_MOBILE_NUMBER_SHOULD_BE_INCLUDED_IN_MOBILE_NUMBERS_LIST("UMV-10004",
"As multiple mobile numbers support is enabled, primary mobile number should be included in " +
"the mobile numbers list."),
ERROR_CODE_PRIMARY_MOBILE_NUMBER_SHOULD_BE_INCLUDED_IN_VERIFIED_MOBILES_LIST("UMV-10005", "As " +
"multiple mobile numbers support and mobile verification is enabled, primary mobile number should be " +
"included in the verified mobile numbers list."),

INVALID_PASSWORD_RECOVERY_REQUEST("APR-10000", "Invalid Password Recovery Request"),

INVALID_PASSWORD_RECOVERY_REQUEST("APR-10000", "Invalid Password Recovery Request")
,
// Idle User Account Identification related Error messages.
ERROR_RETRIEVING_ASSOCIATED_USER("UMM-65005",
"Error retrieving the associated user for the user: %s in the tenant %s.");
Expand Down Expand Up @@ -658,6 +689,9 @@ public static class ConnectorConfig {
public static final String ENABLE_MOBILE_VERIFICATION_BY_PRIVILEGED_USER = "UserClaimUpdate.MobileNumber." +
"EnableVerificationByPrivilegedUser";
public static final String USE_VERIFY_CLAIM_ON_UPDATE = "UserClaimUpdate.UseVerifyClaim";
// This config enables the support to store multiple mobile numbers and email addresses per user.
public static final String SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER =
"UserClaimUpdate.EnableMultipleEmailsAndMobileNumbers";
public static final String ASK_PASSWORD_EXPIRY_TIME = "EmailVerification.AskPassword.ExpiryTime";
public static final String ASK_PASSWORD_TEMP_PASSWORD_GENERATOR = "EmailVerification.AskPassword.PasswordGenerator";
public static final String ASK_PASSWORD_DISABLE_RANDOM_VALUE_FOR_CREDENTIALS = "EmailVerification.AskPassword" +
Expand Down Expand Up @@ -858,7 +892,11 @@ public enum SkipEmailVerificationOnUpdateStates {
/* State maintained to skip triggering an email verification, when the email address was updated by user during
the Email OTP flow at the first login where the email address is not previously set. At the moment email
address was already verified during the email OTP verification. So no need to verify it again. */
SKIP_ON_EMAIL_OTP_FLOW
SKIP_ON_EMAIL_OTP_FLOW,

/* State maintained to skip triggering an email verification, when the email address to be updated is included
in the verifiedEmailAddresses claim, which has been already verified. */
SKIP_ON_ALREADY_VERIFIED_EMAIL_ADDRESSES
}

/**
Expand Down Expand Up @@ -965,6 +1003,10 @@ public enum SkipMobileNumberVerificationOnUpdateStates {
/* State maintained to skip triggering an SMS OTP verification, when the mobile number was updated by user
during the SMS OTP flow at the first login where the mobile number is not previously set. At the moment mobile
number was already verified during the SMS OTP verification. So no need to verify it again. */
SKIP_ON_SMS_OTP_FLOW
SKIP_ON_SMS_OTP_FLOW,

/* State maintained to skip triggering an SMS OTP verification, when the mobile number to be updated is included
in the verifiedMobileNumbers claim, which has been already verified. */
SKIP_ON_ALREADY_VERIFIED_MOBILE_NUMBERS
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
/*
* Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* Copyright (c) 2020-2024, WSO2 LLC. (http://www.wso2.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations und
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.recovery.connector;

import org.apache.axiom.om.OMElement;
Expand Down Expand Up @@ -56,6 +59,7 @@ public class UserClaimUpdateConfigImpl implements IdentityConnectorConfig {
private static final String DEFAULT_MOBILE_NUM_VERIFICATION_ON_UPDATE_SMS_OTP_EXPIRY_TIME = "5";
private static final String DEFAULT_ENABLE_VALUE_FOR_MOBILE_NUMBER_VERIFICATION_ON_UPDATE = "false";
private static final String DEFAULT_MOBILE_NUM_VERIFICATION_BY_PRIVILEGED_USERS = "false";
private static final String DEFAULT_SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS = "true";
private static final String USER_CLAIM_UPDATE_ELEMENT = "UserClaimUpdate";
private static final String ENABLE_ELEMENT = "Enable";
private static final String SEND_OTP_IN_EMAIL_ELEMENT = "SendOTPInEmail";
Expand All @@ -71,6 +75,7 @@ public class UserClaimUpdateConfigImpl implements IdentityConnectorConfig {
private static final String VERIFICATION_ON_UPDATE_ELEMENT = "VerificationOnUpdate";
private static final String NOTIFICATION_ON_UPDATE_ELEMENT = "NotificationOnUpdate";
private static final String ENABLE_MOBILE_VERIFICATION_PRIVILEGED_USER = "EnableVerificationByPrivilegedUser";
private static final String ENABLE_MULTIPLE_EMAILS_AND_MOBILE_NUMBERS_ELEMENT = "EnableMultipleEmailsAndMobileNumbers";
private static String enableEmailVerificationOnUpdateProperty = null;
private static String enableSendOTPInEmailProperty = null;
private static String useUppercaseCharactersInOTPProperty = null;
Expand All @@ -82,6 +87,7 @@ public class UserClaimUpdateConfigImpl implements IdentityConnectorConfig {
private static String enableMobileNumVerificationOnUpdateProperty = null;
private static String mobileNumVerificationOnUpdateCodeExpiryProperty = null;
private static String mobileNumVerificationByPrivilegedUsersProperty = null;
private static String supportMultiEmailsAndMobileNumbersProperty = null;

@Override
public String getName() {
Expand Down Expand Up @@ -139,6 +145,8 @@ public Map<String, String> getPropertyNameMapping() {
"Enable mobile number verification by privileged users");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.MOBILE_NUM_VERIFICATION_ON_UPDATE_EXPIRY_TIME,
"Mobile number verification on update SMS OTP expiry time");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER,
"Support multiple emails and mobile numbers per user");
return nameMapping;
}

Expand Down Expand Up @@ -171,6 +179,8 @@ public Map<String, String> getPropertyDescriptionMapping() {
"Validity time of the mobile number confirmation OTP in minutes.");
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_MOBILE_VERIFICATION_BY_PRIVILEGED_USER,
"Allow privileged users to initiate mobile number verification on update.");
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER,
"Allow users to add multiple email addresses and mobile numbers to their account.");
return descriptionMapping;
}

Expand All @@ -189,6 +199,7 @@ public String[] getPropertyNames() {
properties.add(IdentityRecoveryConstants.ConnectorConfig.ENABLE_MOBILE_NUM_VERIFICATION_ON_UPDATE);
properties.add(IdentityRecoveryConstants.ConnectorConfig.MOBILE_NUM_VERIFICATION_ON_UPDATE_EXPIRY_TIME);
properties.add(IdentityRecoveryConstants.ConnectorConfig.ENABLE_MOBILE_VERIFICATION_BY_PRIVILEGED_USER);
properties.add(IdentityRecoveryConstants.ConnectorConfig.SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER);
return properties.toArray(new String[0]);
}

Expand All @@ -206,6 +217,7 @@ public Properties getDefaultPropertyValues(String tenantDomain) {
String enableMobileNumVerificationOnUpdate = DEFAULT_ENABLE_VALUE_FOR_MOBILE_NUMBER_VERIFICATION_ON_UPDATE;
String mobileNumVerificationOnUpdateCodeExpiry = DEFAULT_MOBILE_NUM_VERIFICATION_ON_UPDATE_SMS_OTP_EXPIRY_TIME;
String mobileNumVerificationByPrivilegedUsers = DEFAULT_MOBILE_NUM_VERIFICATION_BY_PRIVILEGED_USERS;
String supportMultiEmailsAndMobileNumbers = DEFAULT_SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS;

loadConfigurations();

Expand Down Expand Up @@ -242,6 +254,9 @@ public Properties getDefaultPropertyValues(String tenantDomain) {
if (StringUtils.isNotBlank(mobileNumVerificationByPrivilegedUsersProperty)) {
mobileNumVerificationByPrivilegedUsers = mobileNumVerificationByPrivilegedUsersProperty;
}
if (StringUtils.isNotBlank(supportMultiEmailsAndMobileNumbersProperty)) {
supportMultiEmailsAndMobileNumbers = supportMultiEmailsAndMobileNumbersProperty;
}

Properties properties = new Properties();
properties.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_VERIFICATION_ON_UPDATE,
Expand All @@ -266,6 +281,8 @@ public Properties getDefaultPropertyValues(String tenantDomain) {
mobileNumVerificationOnUpdateCodeExpiry);
properties.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_MOBILE_VERIFICATION_BY_PRIVILEGED_USER,
mobileNumVerificationByPrivilegedUsers);
properties.put(IdentityRecoveryConstants.ConnectorConfig.SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER,
supportMultiEmailsAndMobileNumbers);
return properties;
}

Expand Down Expand Up @@ -294,11 +311,15 @@ private void loadConfigurations() {
OMElement userClaimUpdate = IdentityConfigParser.getInstance().getConfigElement(USER_CLAIM_UPDATE_ELEMENT);
Iterator claims = null;
OMElement otpConfigs = null;
OMElement supportMultiEmailsAndMobileNumbers = null;
if (userClaimUpdate != null) {
claims = userClaimUpdate.getChildrenWithName(new QName(IdentityCoreConstants
.IDENTITY_DEFAULT_NAMESPACE, CLAIM_ELEMENT));
otpConfigs = userClaimUpdate.getFirstChildWithName(new QName
(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, OTP_ELEMENT));
supportMultiEmailsAndMobileNumbers = userClaimUpdate.getFirstChildWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE,
ENABLE_MULTIPLE_EMAILS_AND_MOBILE_NUMBERS_ELEMENT));
}

if (claims != null) {
Expand All @@ -317,7 +338,7 @@ private void loadConfigurations() {
(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, VERIFICATION_CODE_ELEMENT));
if (verificationCode != null) {
emailVerificationOnUpdateCodeExpiryProperty = verificationCode.getFirstChildWithName(new
QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, EXPIRY_TIME_ELEMENT))
QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, EXPIRY_TIME_ELEMENT))
.getText();
}
}
Expand All @@ -343,7 +364,7 @@ private void loadConfigurations() {
(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, VERIFICATION_CODE_ELEMENT));
if (verificationCode != null) {
mobileNumVerificationOnUpdateCodeExpiryProperty = verificationCode.getFirstChildWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, EXPIRY_TIME_ELEMENT))
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, EXPIRY_TIME_ELEMENT))
.getText();
}
}
Expand All @@ -362,6 +383,9 @@ private void loadConfigurations() {
otpLengthProperty = otpConfigs.getFirstChildWithName(new QName
(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, OTP_LENGTH_ELEMENT)).getText();
}
if (supportMultiEmailsAndMobileNumbers != null) {
supportMultiEmailsAndMobileNumbersProperty = supportMultiEmailsAndMobileNumbers.getText();
}
}

@Override
Expand Down Expand Up @@ -399,7 +423,9 @@ public Map<String, Property> getMetaData() {
meta.put(IdentityRecoveryConstants.ConnectorConfig.MOBILE_NUM_VERIFICATION_ON_UPDATE_EXPIRY_TIME,
getPropertyObject(IdentityMgtConstants.DataTypes.INTEGER.getValue()));

meta.put(IdentityRecoveryConstants.ConnectorConfig.SUPPORT_MULTI_EMAILS_AND_MOBILE_NUMBERS_PER_USER,
getPropertyObject(IdentityMgtConstants.DataTypes.BOOLEAN.getValue()));

return meta;
}

}
Loading
Loading