From d45ca7d4016e038fee122774db12cafd0d45ff0c Mon Sep 17 00:00:00 2001 From: Alexandre Bourdin Date: Mon, 6 Nov 2023 15:27:04 +0100 Subject: [PATCH] Merge pull request #15582 from overleaf/msm-sso-linking-reminders [web] SSO Linking redirects and interstitial info GitOrigin-RevId: fdad0290ebf2b8b8a77f12b75736c030a4c7b642 --- .../Subscription/SubscriptionLocator.js | 7 +++++++ .../Subscription/TeamInvitesController.js | 19 +++++++++++++++++-- .../Subscription/TeamInvitesHandler.js | 2 ++ .../subscriptions/team/invite-managed.pug | 2 ++ .../app/views/subscriptions/team/invite.pug | 4 +++- .../web/frontend/extracted-translations.json | 1 + .../subscription/team-invite-controller.js | 9 +++++++++ services/web/locales/en.json | 1 + .../Subscription/SubscriptionLocatorTests.js | 6 ++++++ 9 files changed, 48 insertions(+), 3 deletions(-) diff --git a/services/web/app/src/Features/Subscription/SubscriptionLocator.js b/services/web/app/src/Features/Subscription/SubscriptionLocator.js index 4df3d3b9b0..6f7f24043f 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionLocator.js +++ b/services/web/app/src/Features/Subscription/SubscriptionLocator.js @@ -2,6 +2,7 @@ const { promisify } = require('util') const { Subscription } = require('../../models/Subscription') const { DeletedSubscription } = require('../../models/DeletedSubscription') const logger = require('@overleaf/logger') +const { SSOConfig } = require('../../models/SSOConfig') require('./GroupPlansData') // make sure dynamic group plans are loaded const SubscriptionLocator = { @@ -124,6 +125,11 @@ const SubscriptionLocator = { }, } +async function hasSSOEnabled(subscription) { + const ssoConfig = await SSOConfig.findById(subscription.ssoConfig).exec() + return ssoConfig?.enabled +} + SubscriptionLocator.promises = { getUsersSubscription: promisify(SubscriptionLocator.getUsersSubscription), getUserIndividualSubscription: promisify( @@ -153,5 +159,6 @@ SubscriptionLocator.promises = { hasRecurlyGroupSubscription: promisify( SubscriptionLocator.hasRecurlyGroupSubscription ), + hasSSOEnabled, } module.exports = SubscriptionLocator diff --git a/services/web/app/src/Features/Subscription/TeamInvitesController.js b/services/web/app/src/Features/Subscription/TeamInvitesController.js index ef1b7739f2..8caa737ce5 100644 --- a/services/web/app/src/Features/Subscription/TeamInvitesController.js +++ b/services/web/app/src/Features/Subscription/TeamInvitesController.js @@ -84,6 +84,10 @@ async function viewInvite(req, res, next) { personalSubscription.recurlySubscription_id && personalSubscription.recurlySubscription_id !== '' + const groupSSOActive = await SubscriptionLocator.promises.hasSSOEnabled( + subscription + ) + if (subscription?.groupPolicy) { if (!subscription.populated('groupPolicy')) { await subscription.populate('groupPolicy') @@ -128,6 +132,8 @@ async function viewInvite(req, res, next) { expired: req.query.expired, validationStatus: Object.fromEntries(validationStatus), currentManagedUserAdminEmail, + groupSSOActive, + subscriptionId: subscription._id.toString(), }) } else { let currentManagedUserAdminEmail @@ -146,6 +152,8 @@ async function viewInvite(req, res, next) { expired: req.query.expired, userRestrictions: Array.from(req.userRestrictions || []), currentManagedUserAdminEmail, + groupSSOActive, + subscriptionId: subscription._id.toString(), }) } } else { @@ -167,8 +175,15 @@ async function acceptInvite(req, res, next) { const { token } = req.params const userId = SessionManager.getLoggedInUserId(req.session) - await TeamInvitesHandler.promises.acceptInvite(token, userId) - res.sendStatus(204) + const subscription = await TeamInvitesHandler.promises.acceptInvite( + token, + userId + ) + const groupSSOActive = await SubscriptionLocator.promises.hasSSOEnabled( + subscription + ) + + res.status(204).json({ groupSSOActive }) } function revokeInvite(req, res, next) { diff --git a/services/web/app/src/Features/Subscription/TeamInvitesHandler.js b/services/web/app/src/Features/Subscription/TeamInvitesHandler.js index fde4842fa6..c89f9257d7 100644 --- a/services/web/app/src/Features/Subscription/TeamInvitesHandler.js +++ b/services/web/app/src/Features/Subscription/TeamInvitesHandler.js @@ -95,6 +95,8 @@ async function acceptInvite(token, userId) { await NotificationsBuilder.promises .groupInvitation(userId, subscription._id, false) .read() + + return subscription } async function revokeInvite(teamManagerId, subscription, email) { diff --git a/services/web/app/views/subscriptions/team/invite-managed.pug b/services/web/app/views/subscriptions/team/invite-managed.pug index 79d8ded84c..935094d79a 100644 --- a/services/web/app/views/subscriptions/team/invite-managed.pug +++ b/services/web/app/views/subscriptions/team/invite-managed.pug @@ -10,6 +10,8 @@ block append meta meta(name="ol-alreadyEnrolled" data-type="boolean" content=alreadyEnrolled) meta(name="ol-validationStatus" data-type="json" content=validationStatus) meta(name="ol-currentManagedUserAdminEmail" data-type="string" content=currentManagedUserAdminEmail) + meta(name="ol-groupSSOActive" data-type="boolean" content=groupSSOActive) + meta(name="ol-subscriptionId" data-type="string" content=subscriptionId) block content main.content.content-alt.team-invite#invite-managed-root diff --git a/services/web/app/views/subscriptions/team/invite.pug b/services/web/app/views/subscriptions/team/invite.pug index b149da77d4..41d40fbe57 100644 --- a/services/web/app/views/subscriptions/team/invite.pug +++ b/services/web/app/views/subscriptions/team/invite.pug @@ -3,6 +3,8 @@ extends ../../layout block append meta meta(name="ol-hasIndividualRecurlySubscription" data-type="boolean" content=hasIndividualRecurlySubscription) meta(name="ol-inviteToken" content=inviteToken) + meta(name="ol-groupSSOActive" data-type="boolean" content=groupSSOActive) + meta(name="ol-subscriptionId" data-type="string" content=subscriptionId) block content main.content.content-alt.team-invite#main-content @@ -42,4 +44,4 @@ block content div(ng-show="view =='inviteAccepted'") p(ng-non-bindable) #{translate("joined_team", {inviterName: inviterName})} p - a.btn.btn.btn-primary(href="/project") #{translate("done")} + a.btn.btn.btn-primary(href=doneLink) #{translate("done")} diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 42ecfcfaac..1e3262d405 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1421,6 +1421,7 @@ "you_need_to_configure_your_sso_settings": "", "you_will_be_able_to_reassign_subscription": "", "youll_get_best_results_in_visual_but_can_be_used_in_source": "", + "youll_no_longer_need_to_remember_credentials": "", "your_affiliation_is_confirmed": "", "your_browser_does_not_support_this_feature": "", "your_compile_timed_out": "", diff --git a/services/web/frontend/js/main/subscription/team-invite-controller.js b/services/web/frontend/js/main/subscription/team-invite-controller.js index 6a45904e96..223e1058c5 100644 --- a/services/web/frontend/js/main/subscription/team-invite-controller.js +++ b/services/web/frontend/js/main/subscription/team-invite-controller.js @@ -24,6 +24,15 @@ export default App.controller('TeamInviteController', [ 'ol-hasIndividualRecurlySubscription' ) + const groupSSOActive = getMeta('ol-groupSSOActive', false) + + if (groupSSOActive) { + const subscriptionId = getMeta('ol-subscriptionId') + $scope.doneLink = `/subscription/${subscriptionId}/sso_enrollment` + } else { + $scope.doneLink = '/project' + } + if (hideJoinSubscription) { $scope.view = 'restrictedByManagedGroup' } else if (hasIndividualRecurlySubscription) { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 52b97a426e..c67379f785 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -2104,6 +2104,7 @@ "you_will_be_able_to_contact_us_any_time_to_share_your_feedback": "<0>You will be able to contact us any time to share your feedback", "you_will_be_able_to_reassign_subscription": "You will be able to reassign their subscription membership to another person in your organization", "youll_get_best_results_in_visual_but_can_be_used_in_source": "You’ll get the best results from using this tool in the <0>Visual Editor, although you can still use it to insert tables in the <1>Code Editor. Once you’ve selected the number of rows and columns you need, the table will appear in your document and you can double click in a cell to add contents to it.", + "youll_no_longer_need_to_remember_credentials": "You’ll no longer need to remember a separate email address and password. Instead, you will use single-sign on to login to Overleaf. <0>Read more about SSO.", "your_account_is_managed_by_admin_cant_join_additional_group": "Your __appName__ account is managed by your current group admin (__admin__). This means you can’t join additional group subscriptions. <0>Read more about Managed Users.", "your_affiliation_is_confirmed": "Your <0>__institutionName__ affiliation is confirmed.", "your_browser_does_not_support_this_feature": "Sorry, your browser doesn’t support this feature. Please update your browser to its latest version.", diff --git a/services/web/test/unit/src/Subscription/SubscriptionLocatorTests.js b/services/web/test/unit/src/Subscription/SubscriptionLocatorTests.js index a49583833f..d4e82888ed 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionLocatorTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionLocatorTests.js @@ -15,6 +15,9 @@ describe('Subscription Locator Tests', function () { findOne: sinon.stub().yields(), find: sinon.stub().yields(), } + this.SSOConfig = { + findById: sinon.stub().yields(), + } this.SubscriptionLocator = SandboxedModule.require(modulePath, { requires: { './GroupPlansData': {}, @@ -24,6 +27,9 @@ describe('Subscription Locator Tests', function () { '../../models/DeletedSubscription': { DeletedSubscription: this.DeletedSubscription, }, + '../../models/SSOConfig': { + SSOConfig: this.SSOConfig, + }, }, }) })