diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs index 7f12181dff..ad03dd24ed 100644 --- a/services/web/app/src/Features/Project/ProjectListController.mjs +++ b/services/web/app/src/Features/Project/ProjectListController.mjs @@ -321,14 +321,23 @@ async function projectListPage(req, res, next) { if (samlSession) { // Notification institution SSO: After SSO Linked if (samlSession.linked) { + let templateKey = 'notification_institution_sso_linked' + + if ( + samlSession.userCreatedViaDomainCapture && + samlSession.managedUsersEnabled + ) { + templateKey = + 'notification_account_created_via_group_domain_capture_and_managed_users_enabled' + } else if (samlSession.domainCaptureEnabled) { + templateKey = 'notification_group_sso_linked' + } notificationsInstitution.push({ email: samlSession.institutionEmail, institutionName: samlSession.linked.universityName || samlSession.linked.providerName, - templateKey: samlSession.domainCaptureEnabled - ? 'notification_group_sso_linked' - : 'notification_institution_sso_linked', + templateKey, }) } diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 62ed8591bc..58b24a95da 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -57,6 +57,7 @@ "account_has_past_due_invoice_change_plan_warning": "", "account_help": "", "account_managed_by_group_administrator": "", + "account_managed_by_group_teamname": "", "account_not_linked_to_dropbox": "", "account_settings": "", "acct_linked_to_institution_acct_2": "", @@ -1392,6 +1393,7 @@ "read_lines_from_path": "", "read_more": "", "read_more_about_free_compile_timeouts_servers": "", + "read_more_about_managed_users": "", "read_only_dropbox_sync_message": "", "read_only_token": "", "read_write_token": "", diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx index 5fc53c7d4b..5dfab49252 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx @@ -103,6 +103,34 @@ function Institution() { } /> )} + {templateKey === + 'notification_account_created_via_group_domain_capture_and_managed_users_enabled' && ( + id && handleDismiss(id)} + content={ + <> + ] + } + values={{ appName, email, teamName: institutionName }} + shouldUnescape + tOptions={{ interpolation: { escapeValue: true } }} + /> +   + + {t('read_more_about_managed_users')} + + + } + /> + )} {templateKey === 'notification_institution_sso_non_canonical' && ( __email__ is managed by <0>__teamName__.", "account_not_linked_to_dropbox": "Your account is not linked to Dropbox", "account_settings": "Account settings", "account_with_email_exists": "It looks like an __appName__ account with the email __email__ already exists.", @@ -1805,6 +1806,7 @@ "read_lines_from_path": "Read lines from __path__", "read_more": "Read more", "read_more_about_free_compile_timeouts_servers": "Read more about changes to free compile timeouts and servers", + "read_more_about_managed_users": "Read more about managed users", "read_only_dropbox_sync_message": "As a read-only viewer you can sync the current project version to Dropbox, but changes made in Dropbox will <0>not sync back to Overleaf.", "read_only_token": "Read-Only Token", "read_write_token": "Read-Write Token", diff --git a/services/web/test/unit/src/Project/ProjectListController.test.mjs b/services/web/test/unit/src/Project/ProjectListController.test.mjs index 6d58da5836..e1d55b820c 100644 --- a/services/web/test/unit/src/Project/ProjectListController.test.mjs +++ b/services/web/test/unit/src/Project/ProjectListController.test.mjs @@ -541,6 +541,101 @@ describe('ProjectListController', function () { }) }) + describe('when user linked to SSO', function () { + const linkedEmail = 'picard@starfleet.com' + const universityName = 'Starfleet' + const notificationData = { + email: linkedEmail, + institutionName: universityName, + } + beforeEach(function (ctx) { + ctx.Features.hasFeature.withArgs('saml').returns(true) + ctx.req.session.saml = { + institutionEmail: linkedEmail, + linked: { + universityName, + }, + } + }) + + it('should render with Commons template when Commons was linked', async function (ctx) { + await new Promise(resolve => { + ctx.res.render = (pageName, opts) => { + expect(opts.notificationsInstitution).to.deep.equal([ + Object.assign( + { templateKey: 'notification_institution_sso_linked' }, + notificationData + ), + ]) + resolve() + } + ctx.ProjectListController.projectListPage(ctx.req, ctx.res) + }) + }) + + describe('when via domain capture', function () { + beforeEach(function (ctx) { + ctx.req.session.saml.domainCaptureEnabled = true + }) + + it('should render with group template', async function (ctx) { + await new Promise(resolve => { + ctx.res.render = (pageName, opts) => { + expect(opts.notificationsInstitution).to.deep.equal([ + Object.assign( + { templateKey: 'notification_group_sso_linked' }, + notificationData + ), + ]) + resolve() + } + ctx.ProjectListController.projectListPage(ctx.req, ctx.res) + }) + }) + + describe('user created via domain capture and group is managed', function () { + beforeEach(function (ctx) { + ctx.req.session.saml.userCreatedViaDomainCapture = true + }) + it('should render with notification_group_sso_linked', async function (ctx) { + await new Promise(resolve => { + ctx.res.render = (pageName, opts) => { + expect(opts.notificationsInstitution).to.deep.equal([ + Object.assign( + { + templateKey: 'notification_group_sso_linked', + }, + notificationData + ), + ]) + resolve() + } + ctx.ProjectListController.projectListPage(ctx.req, ctx.res) + }) + }) + + it('should render with notification_account_created_via_group_domain_capture_and_managed_users_enabled when managed user is enabled', async function (ctx) { + ctx.req.session.saml.managedUsersEnabled = true + await new Promise(resolve => { + ctx.res.render = (pageName, opts) => { + expect(opts.notificationsInstitution).to.deep.equal([ + Object.assign( + { + templateKey: + 'notification_account_created_via_group_domain_capture_and_managed_users_enabled', + }, + notificationData + ), + ]) + resolve() + } + ctx.ProjectListController.projectListPage(ctx.req, ctx.res) + }) + }) + }) + }) + }) + describe('With Institution SSO feature', function () { beforeEach(async function (ctx) { await new Promise(resolve => {