From 4797f7e357ddb48ab4bcc46e1c68db225dce065e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Thu, 20 May 2021 11:19:01 +0200 Subject: [PATCH] Merge pull request #4047 from overleaf/ta-clear-sso-data Clear SSO Data via Admin Panel GitOrigin-RevId: bd4e79ccc7f98337bc1f8b78947cc647352f6fbe --- .../app/src/Features/Email/EmailBuilder.js | 28 ++++++++++++++ .../Features/Institutions/InstitutionsAPI.js | 1 + .../web/app/src/Features/User/UserUpdater.js | 38 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/services/web/app/src/Features/Email/EmailBuilder.js b/services/web/app/src/Features/Email/EmailBuilder.js index c12ef1a2ad..d79318ee75 100644 --- a/services/web/app/src/Features/Email/EmailBuilder.js +++ b/services/web/app/src/Features/Email/EmailBuilder.js @@ -509,6 +509,34 @@ templates.securityAlert = NoCTAEmailTemplate({ }, }) +templates.SAMLDataCleared = ctaTemplate({ + subject(opts) { + return `Institutional Login No Longer Linked - ${settings.appName}` + }, + title(opts) { + return 'Institutional Login No Longer Linked' + }, + message(opts, isPlainText) { + return [ + `We're writing to let you know that due to a bug on our end, we've had to temporarily disable logging into your ${settings.appName} through your institution.`, + `To get it going again, you'll need to relink your institutional email address to your ${settings.appName} account via your settings.`, + ] + }, + secondaryMessage() { + return [ + `If you ordinarily log in to your ${settings.appName} account through your institution, you may need to set or reset your password to regain access to your account first.`, + 'This bug did not affect the security of any accounts, but it may have affected license entitlements for a small number of users. We are sorry for any inconvenience that this may cause for you.', + `If you have any questions, please get in touch with our support team at ${settings.adminEmail} or by replying to this email.`, + ] + }, + ctaText(opts) { + return 'Update my Emails and Affiliations' + }, + ctaURL(opts) { + return `${settings.siteUrl}/user/settings` + }, +}) + function _formatUserNameAndEmail(user, placeholder) { if (user.first_name && user.last_name) { const fullName = `${user.first_name} ${user.last_name}` diff --git a/services/web/app/src/Features/Institutions/InstitutionsAPI.js b/services/web/app/src/Features/Institutions/InstitutionsAPI.js index 9a9c36a46c..2f1cc4fd4a 100644 --- a/services/web/app/src/Features/Institutions/InstitutionsAPI.js +++ b/services/web/app/src/Features/Institutions/InstitutionsAPI.js @@ -197,6 +197,7 @@ const InstitutionsAPI = { path: `/api/v2/users/${userId}/affiliations/remove_entitlement`, body: { email }, defaultErrorMessage: "Couldn't remove entitlement", + extraSuccessStatusCodes: [404], }, callback ) diff --git a/services/web/app/src/Features/User/UserUpdater.js b/services/web/app/src/Features/User/UserUpdater.js index 33eda69b6f..12ecf266e6 100644 --- a/services/web/app/src/Features/User/UserUpdater.js +++ b/services/web/app/src/Features/User/UserUpdater.js @@ -81,6 +81,42 @@ async function addEmailAddress(userId, newEmail, affiliationOptions, auditLog) { } } +async function clearSAMLData(userId, auditLog, sendEmail) { + const user = await UserGetter.promises.getUser(userId, { + email: 1, + emails: 1, + }) + + await UserAuditLogHandler.promises.addEntry( + userId, + 'clear-institution-sso-data', + auditLog.initiatorId, + auditLog.ipAddress, + {} + ) + + const update = { + $unset: { + samlIdentifiers: 1, + 'emails.$[].samlProviderId': 1, + }, + } + await UserUpdater.promises.updateUser(userId, update) + + for (const emailData of user.emails) { + await InstitutionsAPIPromises.removeEntitlement(userId, emailData.email) + } + + await FeaturesUpdater.promises.refreshFeatures( + userId, + 'clear-institution-sso-data' + ) + + if (sendEmail) { + await EmailHandler.promises.sendEmail('SAMLDataCleared', { to: user.email }) + } +} + async function setDefaultEmailAddress( userId, email, @@ -313,6 +349,8 @@ const UserUpdater = { }) }, + clearSAMLData: callbackify(clearSAMLData), + // set the default email address by setting the `email` attribute. The email // must be one of the user's multiple emails (`emails` attribute) setDefaultEmailAddress: callbackify(setDefaultEmailAddress),