Procházet zdrojové kódy

Merge pull request #32401 from overleaf/jel-sso-unable-to-reconfirm

[web] Return error when user is reconfirming but no user found

GitOrigin-RevId: 4c7b626b0c6ea6a7e883f90d16c5fb1330ffbbc6
Jessica Lawshe před 6 měsíci
rodič
revize
2758952781

+ 8 - 0
services/web/app/src/Features/Errors/Errors.js

@@ -120,6 +120,13 @@ class SAMLAuthenticationError extends OError {
     return 'saml_auth_error'
   }
 }
+
+class SAMLCommonsReconfirmationUnableToFindUserError extends SAMLAuthenticationError {
+  get i18nKey() {
+    return 'saml_commons_reconfirmation_unable_to_find_user'
+  }
+}
+
 class SAMLAssertionAudienceMismatch extends SAMLAuthenticationError {}
 
 class SAMLAuthenticationRequiredError extends SAMLAuthenticationError {
@@ -394,6 +401,7 @@ module.exports = {
   OutputFileFetchFailedError,
   SAMLAssertionAudienceMismatch,
   SAMLAuthenticationRequiredError,
+  SAMLCommonsReconfirmationUnableToFindUserError,
   SAMLCommonsUnavailable,
   SAMLDomainCaptureEmailExistsError,
   SAMLDomainCaptureEmailDomainMismatchError,

+ 1 - 0
services/web/frontend/extracted-translations.json

@@ -1601,6 +1601,7 @@
   "run_current_script_to_see_output": "",
   "run_python_code": "",
   "saml_auth_error": "",
+  "saml_commons_reconfirmation_unable_to_find_user": "",
   "saml_email_not_in_account_error_managed_users_2": "",
   "saml_identity_exists_error": "",
   "saml_invalid_signature_error": "",

+ 31 - 17
services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx

@@ -183,23 +183,37 @@ function Institution() {
                 }
               />
             )}
-            {templateKey === 'notification_institution_sso_error' && (
-              <Notification
-                type="error"
-                onDismiss={() => id && handleDismiss(id)}
-                content={
-                  <>
-                    {t('generic_something_went_wrong')}.
-                    <div>
-                      {error?.translatedMessage
-                        ? error?.translatedMessage
-                        : error?.message}
-                    </div>
-                    {error?.tryAgain ? `${t('try_again')}.` : null}
-                  </>
-                }
-              />
-            )}
+            {templateKey === 'notification_institution_sso_error' &&
+              (error?.name ===
+              'SAMLCommonsReconfirmationUnableToFindUserError' ? (
+                <Notification
+                  type="error"
+                  onDismiss={() => id && handleDismiss(id)}
+                  content={
+                    <Trans
+                      i18nKey="saml_commons_reconfirmation_unable_to_find_user"
+                      // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
+                      components={[<a href="/contact" target="_blank" />]}
+                    />
+                  }
+                />
+              ) : (
+                <Notification
+                  type="error"
+                  onDismiss={() => id && handleDismiss(id)}
+                  content={
+                    <>
+                      {t('generic_something_went_wrong')}.
+                      <div>
+                        {error?.translatedMessage
+                          ? error?.translatedMessage
+                          : error?.message}
+                      </div>
+                      {error?.tryAgain ? `${t('try_again')}.` : null}
+                    </>
+                  }
+                />
+              ))}
           </Fragment>
         )
       )}

+ 17 - 8
services/web/frontend/js/features/settings/components/emails/sso-alert.tsx

@@ -21,17 +21,26 @@ export function SSOAlert() {
   const handleErrorClosed = () => setErrorClosed(true)
 
   if (samlError) {
+    const content =
+      samlError.name === 'SAMLCommonsReconfirmationUnableToFindUserError' ? (
+        <Trans
+          i18nKey="saml_commons_reconfirmation_unable_to_find_user"
+          // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
+          components={[<a href="/contact" target="_blank" />]}
+        />
+      ) : (
+        <>
+          {samlError.translatedMessage
+            ? samlError.translatedMessage
+            : samlError.message}
+          {samlError.tryAgain && <p>{t('try_again')}</p>}
+        </>
+      )
+
     return !errorClosed ? (
       <OLNotification
         type="error"
-        content={
-          <>
-            {samlError.translatedMessage
-              ? samlError.translatedMessage
-              : samlError.message}
-            {samlError.tryAgain && <p>{t('try_again')}</p>}
-          </>
-        }
+        content={content}
         isDismissible
         onDismiss={handleErrorClosed}
       />

+ 1 - 0
services/web/locales/en.json

@@ -2089,6 +2089,7 @@
   "saml": "SAML",
   "saml_auth_error": "Sorry, your identity provider responded with an error.  Please contact your administrator for more information.",
   "saml_authentication_required_error": "Other login methods have been disabled by your group administrator. Please use your group SSO login.",
+  "saml_commons_reconfirmation_unable_to_find_user": "Sorry, we were unable to confirm your affiliation. This may have been caused by a change in the SSO data that is being sent to us from your institution. Please <0>contact us</0> for help with relinking your Overleaf account to your institution’s SSO.",
   "saml_commons_unavailable": "Institution SSO is currently unavailable. For more details, please see <0>__linkText__</0>.",
   "saml_create_admin_instructions": "Choose an email address for the first __appName__ admin account. This should correspond to an account in the SAML system. You will then be asked to log in with this account.",
   "saml_email_not_in_account_error_managed_users_2": "Your email address <0>__userEmail__</0> was not recognized. Your organization’s identity provider returned <1>__samlEmail__</1>. You will need to log out of <0>__userEmail__</0> and use <1>__samlEmail__</1> to create an account via SSO. You can <2>transfer your existing projects</2> to the new account.",

+ 25 - 0
services/web/test/frontend/features/project-list/components/notifications.test.tsx

@@ -627,6 +627,31 @@ describe('<UserNotifications />', function () {
 
       expect(screen.queryByRole('alert')).to.be.null
     })
+
+    it('shows reconfirmation unable-to-find-user error content', function () {
+      const institution: DeepPartial<InstitutionType> = {
+        templateKey: 'notification_institution_sso_error',
+        error: {
+          name: 'SAMLCommonsReconfirmationUnableToFindUserError',
+        },
+      }
+      window.metaAttributesCache.set('ol-notificationsInstitution', [
+        { ...notificationsInstitution, ...institution },
+      ])
+      render(<Institution />)
+
+      screen.getByRole('alert')
+      screen.getByText(/unable to confirm your affiliation/i)
+
+      const contactLink = screen.getByRole('link', { name: /contact us/i })
+      expect(contactLink.getAttribute('href')).to.equal('/contact')
+      expect(contactLink.getAttribute('target')).to.equal('_blank')
+
+      const closeBtn = screen.getByRole('button', { name: /close/i })
+      fireEvent.click(closeBtn)
+
+      expect(screen.queryByRole('alert')).to.be.null
+    })
   })
 
   describe('getEmailDeletionDate', function () {

+ 13 - 0
services/web/test/frontend/features/settings/components/emails/sso-alert.test.tsx

@@ -84,6 +84,19 @@ describe('<SSOAlert/>', function () {
       screen.getByText('Please try again')
     })
 
+    it('should render reconfirmation unable-to-find-user message for matching SAML error name', function () {
+      window.metaAttributesCache.set('ol-samlError', {
+        name: 'SAMLCommonsReconfirmationUnableToFindUserError',
+      })
+
+      render(<SSOAlert />)
+
+      screen.getByText(/unable to confirm your affiliation/i)
+      const contactLink = screen.getByRole('link', { name: /contact us/i })
+      expect(contactLink.getAttribute('href')).to.equal('/contact')
+      expect(contactLink.getAttribute('target')).to.equal('_blank')
+    })
+
     it('the alert should be closeable', function () {
       render(<SSOAlert />)
       const closeButton = screen.getByRole('button', {

+ 1 - 0
services/web/types/project/dashboard/notification.ts

@@ -93,6 +93,7 @@ export type Institution = {
     translatedMessage?: string
     message?: string
     tryAgain?: boolean
+    name?: string
   }
 }
 

+ 1 - 0
services/web/types/settings-page.ts

@@ -10,6 +10,7 @@ export type SAMLError = {
   translatedMessage?: string
   message?: string
   tryAgain?: boolean
+  name?: string
 }
 
 export type InstitutionLink = {