Skip to content

Commit

Permalink
Merge pull request #5037 from Expensify/cmartins-isSecondaryLoginFrom…
Browse files Browse the repository at this point in the history
…PublicDomain

Ensure accounts with a private domain secondary login can create a workspace
  • Loading branch information
yuwenmemon authored Sep 14, 2021
2 parents eed95b9 + 6b15611 commit 478e9cf
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 46 deletions.
4 changes: 2 additions & 2 deletions src/libs/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,13 +741,13 @@ function User_GetBetas() {

/**
* @param {Object} parameters
* @param {String} parameters.email
* @param {String} parameters.emailList
* @param {Boolean} [parameters.requireCertainty]
* @returns {Promise}
*/
function User_IsFromPublicDomain(parameters) {
const commandName = 'User_IsFromPublicDomain';
requireParameters(['email'], parameters, commandName);
requireParameters(['emailList'], parameters, commandName);
return Network.post(commandName, {
...{requireCertainty: true},
...parameters,
Expand Down
1 change: 0 additions & 1 deletion src/libs/Navigation/AppNavigator/AuthScreens.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ class AuthScreens extends React.Component {
PersonalDetails.fetchPersonalDetails();
User.getUserDetails();
User.getBetas();
User.getDomainInfo();
PersonalDetails.fetchLocalCurrency();
fetchAllReports(true, true);
fetchCountryCodeByRequestIP();
Expand Down
1 change: 1 addition & 0 deletions src/libs/Pusher/EventType.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export default {
REPORT_COMMENT_EDIT: 'reportCommentEdit',
REPORT_TOGGLE_PINNED: 'reportTogglePinned',
PREFERRED_LOCALE: 'preferredLocale',
ACCOUNT_VALIDATED: 'accountValidated',
};
29 changes: 29 additions & 0 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,15 @@ function updateReportPinnedState(reportID, isPinned) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {isPinned});
}

/**
* Updates isFromPublicDomain in Onyx.
*
* @param {Boolean} isFromPublicDomain
*/
function setIsFromPublicDomain(isFromPublicDomain) {
Onyx.merge(ONYXKEYS.USER, {isFromPublicDomain});
}

/**
* Get the private pusher channel name for a Report.
*
Expand Down Expand Up @@ -741,6 +750,26 @@ function subscribeToUserEvents() {
{error, pusherChannelName, eventName: Pusher.TYPE.REPORT_TOGGLE_PINNED},
);
});

// Live-update if a user has private domains listed as primary or secondary logins.
Pusher.subscribe(pusherChannelName, Pusher.TYPE.ACCOUNT_VALIDATED, (pushJSON) => {
Log.info(
`[Report] Handled ${Pusher.TYPE.ACCOUNT_VALIDATED} event sent by Pusher`,
false,
{isFromPublicDomain: pushJSON.isFromPublicDomain},
);
setIsFromPublicDomain(pushJSON.isFromPublicDomain);
}, false,
() => {
NetworkConnection.triggerReconnectionCallbacks('pusher re-subscribed to private user channel');
})
.catch((error) => {
Log.info(
'[Report] Failed to subscribe to Pusher channel',
false,
{error, pusherChannelName, eventName: Pusher.TYPE.ACCOUNT_VALIDATED},
);
});
}

/**
Expand Down
97 changes: 54 additions & 43 deletions src/libs/actions/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,56 @@ function getBetas() {
});
}

/**
* Fetch the public domain info for the current user (includes secondary logins).
*
* This API is a bit weird in that it sometimes depends on information being cached in bedrock.
* If the info for the domain is not in bedrock, then it creates an asynchronous bedrock job to gather domain info.
* If that happens, this function will automatically retry itself in 10 minutes.
*
* @param {String} loginList
*/
function getDomainInfo(loginList) {
// If this command fails, we'll retry again in 10 minutes,
// arbitrarily chosen giving Bedrock time to resolve the ClearbitCheckPublicEmail job for this email.
const RETRY_TIMEOUT = 600000;

// First we filter out any domains that are in the list of common public domains
const emailList = _.filter(loginList, email => (
!_.contains(COMMON_PUBLIC_DOMAINS, Str.extractEmailDomain(email))
));

// If there are no emails left, we have a public domain
if (!emailList.length) {
Onyx.merge(ONYXKEYS.USER, {isFromPublicDomain: true});
return;
}

// Check the API for the remaining uncommon domains
API.User_IsFromPublicDomain({emailList: emailList.join(',')})
.then((response) => {
if (response.jsonCode === 200) {
const {isFromPublicDomain} = response;
Onyx.merge(ONYXKEYS.USER, {isFromPublicDomain});

// If the user is not on a public domain we'll want to know whether they are on a domain that has
// already provisioned the Expensify card
if (isFromPublicDomain) {
return;
}

API.User_IsUsingExpensifyCard()
.then(({isUsingExpensifyCard}) => {
Onyx.merge(ONYXKEYS.USER, {isUsingExpensifyCard});
});
} else {
// eslint-disable-next-line max-len
console.debug(`Command User_IsFromPublicDomain returned error code: ${response.jsonCode}. Most likely, this means that the domain ${Str.extractEmail(sessionEmail)} is not in the bedrock cache. Retrying in ${RETRY_TIMEOUT / 1000 / 60} minutes`);
setTimeout(() => getDomainInfo(loginList), RETRY_TIMEOUT);
}
});
}

/**
* Fetches the data needed for user settings
*/
Expand All @@ -90,6 +140,10 @@ function getUserDetails() {
const blockedFromConcierge = lodashGet(response, `nameValuePairs.${CONST.NVP.BLOCKED_FROM_CONCIERGE}`, {});
Onyx.merge(ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE, blockedFromConcierge);

// Get domainInfo for user
const emailList = _.map(loginList, userLogin => userLogin.partnerUserID);
getDomainInfo(emailList);

const preferredSkinTone = lodashGet(response, `nameValuePairs.${CONST.NVP.PREFERRED_EMOJI_SKIN_TONE}`, {});
Onyx.merge(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE,
getSkinToneEmojiFromIndex(preferredSkinTone).skinTone);
Expand Down Expand Up @@ -207,49 +261,6 @@ function isBlockedFromConcierge(expiresAt) {
return moment().isBefore(moment(expiresAt), 'day');
}

/**
* Fetch the public domain info for the current user.
*
* This API is a bit weird in that it sometimes depends on information being cached in bedrock.
* If the info for the domain is not in bedrock, then it creates an asynchronous bedrock job to gather domain info.
* If that happens, this function will automatically retry itself in 10 minutes.
*/
function getDomainInfo() {
// If this command fails, we'll retry again in 10 minutes,
// arbitrarily chosen giving Bedrock time to resolve the ClearbitCheckPublicEmail job for this email.
const RETRY_TIMEOUT = 600000;

// First check list of common public domains
if (_.contains(COMMON_PUBLIC_DOMAINS, sessionEmail)) {
Onyx.merge(ONYXKEYS.USER, {isFromPublicDomain: true});
return;
}

// If it is not a common public domain, check the API
API.User_IsFromPublicDomain({email: sessionEmail})
.then((response) => {
if (response.jsonCode === 200) {
const {isFromPublicDomain} = response;
Onyx.merge(ONYXKEYS.USER, {isFromPublicDomain});

// If the user is not on a public domain we'll want to know whether they are on a domain that has
// already provisioned the Expensify card
if (isFromPublicDomain) {
return;
}

API.User_IsUsingExpensifyCard()
.then(({isUsingExpensifyCard}) => {
Onyx.merge(ONYXKEYS.USER, {isUsingExpensifyCard});
});
} else {
// eslint-disable-next-line max-len
console.debug(`Command User_IsFromPublicDomain returned error code: ${response.jsonCode}. Most likely, this means that the domain ${Str.extractEmail(sessionEmail)} is not in the bedrock cache. Retrying in ${RETRY_TIMEOUT / 1000 / 60} minutes`);
setTimeout(getDomainInfo, RETRY_TIMEOUT);
}
});
}

/**
* Initialize our pusher subscription to listen for user changes
*/
Expand Down

0 comments on commit 478e9cf

Please sign in to comment.