فهرست منبع

Merge pull request #8076 from overleaf/msm-settings-sso-journey-fixes

[ReactSettings] Added missing affiliation scenarios

GitOrigin-RevId: 15c9e8d7557d9d75034ad1c485f17b35ae952a13
Miguel Serrano 4 سال پیش
والد
کامیت
f81c52e6c7

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

@@ -2,6 +2,7 @@
   "access_your_projects_with_git": "",
   "account_not_linked_to_dropbox": "",
   "account_settings": "",
+  "acct_linked_to_institution_acct_2": "",
   "add_another_email": "",
   "add_files": "",
   "add_new_email": "",
@@ -22,6 +23,7 @@
   "beta_program_benefits": "",
   "blocked_filename": "",
   "can_edit": "",
+  "can_link_your_institution_acct_2": "",
   "cancel": "",
   "cannot_invite_non_user": "",
   "cannot_invite_self": "",
@@ -87,6 +89,7 @@
   "dismiss": "",
   "dismiss_error_popup": "",
   "doesnt_match": "",
+  "doing_this_allow_log_in_through_institution_2": "",
   "doing_this_will_verify_affiliation_and_allow_log_in_2": "",
   "done": "",
   "download": "",
@@ -219,6 +222,7 @@
   "let_us_know": "",
   "limited_offer": "",
   "link": "",
+  "link_accounts": "",
   "link_accounts_and_add_email": "",
   "link_sharing_is_off": "",
   "link_sharing_is_on": "",

+ 2 - 2
services/web/frontend/js/features/settings/components/emails/add-email.tsx

@@ -10,7 +10,7 @@ import SsoLinkingInfo from './add-email/sso-linking-info'
 import AddNewEmailBtn from './add-email/add-new-email-btn'
 import useAsync from '../../../../shared/hooks/use-async'
 import { useUserEmailsContext } from '../../context/user-email-context'
-import { isSsoAvailable } from '../../utils/sso'
+import { ssoAvailableForDomain } from '../../utils/sso'
 import { postJSON } from '../../../../infrastructure/fetch-json'
 import { University } from '../../../../../../types/university'
 import { CountryCode } from '../../data/countries-list'
@@ -120,7 +120,7 @@ function AddEmail() {
             />
           </Cell>
         </Col>
-        {isSsoAvailable(newEmailMatchedInstitution) ? (
+        {ssoAvailableForDomain(newEmailMatchedInstitution) ? (
           <Col md={8}>
             <Cell>
               <SsoLinkingInfo

+ 123 - 15
services/web/frontend/js/features/settings/components/emails/row.tsx

@@ -1,33 +1,141 @@
+import { Trans, useTranslation } from 'react-i18next'
 import { UserEmailData } from '../../../../../../types/user-email'
 import { Row, Col } from 'react-bootstrap'
 import Email from './email'
 import InstitutionAndRole from './institution-and-role'
 import EmailCell from './cell'
 import Actions from './actions'
+import { institutionAlreadyLinked } from '../../utils/selectors'
+import { useUserEmailsContext } from '../../context/user-email-context'
+import getMeta from '../../../../utils/meta'
+import { ExposedSettings } from '../../../../../../types/exposed-settings'
+import { ssoAvailableForInstitution } from '../../utils/sso'
 
 type EmailsRowProps = {
   userEmailData: UserEmailData
 }
 
 function EmailsRow({ userEmailData }: EmailsRowProps) {
+  const hasSSOAffiliation = Boolean(
+    userEmailData.affiliation &&
+      ssoAvailableForInstitution(userEmailData.affiliation.institution)
+  )
+
   return (
-    <Row>
-      <Col md={4}>
-        <EmailCell>
-          <Email userEmailData={userEmailData} />
-        </EmailCell>
-      </Col>
-      <Col md={5}>
-        {userEmailData.affiliation?.institution && (
+    <>
+      <Row>
+        <Col md={4}>
           <EmailCell>
-            <InstitutionAndRole userEmailData={userEmailData} />
+            <Email userEmailData={userEmailData} />
           </EmailCell>
-        )}
-      </Col>
-      <Col md={3}>
-        <EmailCell className="text-md-right">
-          <Actions userEmailData={userEmailData} />
-        </EmailCell>
+        </Col>
+        <Col md={5}>
+          {userEmailData.affiliation?.institution && (
+            <EmailCell>
+              <InstitutionAndRole userEmailData={userEmailData} />
+            </EmailCell>
+          )}
+        </Col>
+        <Col md={3}>
+          <EmailCell className="text-md-right">
+            <Actions userEmailData={userEmailData} />
+          </EmailCell>
+        </Col>
+      </Row>
+
+      {hasSSOAffiliation && (
+        <SSOAffiliationInfo userEmailData={userEmailData} />
+      )}
+    </>
+  )
+}
+
+type SSOAffiliationInfoProps = {
+  userEmailData: UserEmailData
+}
+
+function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
+  const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
+  const { t } = useTranslation()
+  const { state } = useUserEmailsContext()
+
+  if (
+    !userEmailData.samlProviderId &&
+    institutionAlreadyLinked(state, userEmailData)
+  ) {
+    // if the email is not linked to the institution, but there's another email already linked to that institution
+    // no SSO affiliation is displayed, since cannot have multiple emails linked to the same institution
+    return null
+  }
+
+  if (userEmailData.samlProviderId) {
+    return (
+      <Row>
+        <Col md={8} mdOffset={4}>
+          <EmailCell>
+            <p>
+              <Trans
+                i18nKey="acct_linked_to_institution_acct_2"
+                components={
+                  /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
+                  [<strong />]
+                }
+                values={{
+                  institutionName: userEmailData.affiliation?.institution.name,
+                }}
+              />
+            </p>
+          </EmailCell>
+        </Col>
+      </Row>
+    )
+  }
+
+  return (
+    <Row>
+      <Col md={8} mdOffset={4}>
+        <div className="horizontal-divider" />
+        <Row>
+          <Col md={9}>
+            <EmailCell>
+              <p className="small">
+                <Trans
+                  i18nKey="can_link_your_institution_acct_2"
+                  values={{
+                    institutionName:
+                      userEmailData.affiliation?.institution.name,
+                  }}
+                  components={
+                    /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
+                    [<strong />]
+                  }
+                />
+              </p>
+              <p className="small">
+                <Trans
+                  i18nKey="doing_this_allow_log_in_through_institution_2"
+                  components={
+                    /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
+                    [<strong />]
+                  }
+                />{' '}
+                <a href="/learn/how-to/Institutional_Login">
+                  {t('find_out_more_about_institution_login')}
+                </a>
+              </p>
+            </EmailCell>
+          </Col>
+          <Col md={3} className="text-md-right">
+            <EmailCell>
+              <a
+                className="btn-sm btn btn-info btn-link-accounts"
+                href={`${samlInitPath}?university_id=${userEmailData.affiliation?.institution?.id}&auto=/user/settings&email=${userEmailData.email}`}
+              >
+                {t('link_accounts')}
+              </a>
+            </EmailCell>
+          </Col>
+        </Row>
       </Col>
     </Row>
   )

+ 18 - 6
services/web/frontend/js/features/settings/utils/sso.ts

@@ -1,9 +1,9 @@
 import getMeta from '../../../utils/meta'
 import { InstitutionInfo } from '../components/emails/add-email/input'
 import { ExposedSettings } from '../../../../../types/exposed-settings'
-import { Nullable } from '../../../../../types/utils'
+import { Institution } from '../../../../../types/institution'
 
-const ssoAvailableForDomain = (domain: InstitutionInfo | null) => {
+export const ssoAvailableForDomain = (domain: InstitutionInfo | null) => {
   const { hasSamlBeta, hasSamlFeature } = getMeta(
     'ol-ExposedSettings'
   ) as ExposedSettings
@@ -16,8 +16,20 @@ const ssoAvailableForDomain = (domain: InstitutionInfo | null) => {
   return hasSamlBeta && domain.university.ssoBeta
 }
 
-export const isSsoAvailable = (
-  institutionInfo: Nullable<InstitutionInfo>
-): institutionInfo is InstitutionInfo => {
-  return Boolean(institutionInfo && ssoAvailableForDomain(institutionInfo))
+export const ssoAvailableForInstitution = (institution: Institution | null) => {
+  const { hasSamlBeta, hasSamlFeature } = getMeta(
+    'ol-ExposedSettings'
+  ) as ExposedSettings
+  if (
+    !hasSamlFeature ||
+    !institution ||
+    !institution.confirmed ||
+    !institution.isUniversity
+  ) {
+    return false
+  }
+  if (institution.ssoEnabled) {
+    return true
+  }
+  return hasSamlBeta && institution.ssoBeta
 }

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

@@ -209,6 +209,7 @@
   "institution_email_new_to_app": "Your <b>__institutionName__</b> email (<b>__email__</b>) is new to __appName__.",
   "institutional": "Institutional",
   "doing_this_allow_log_in_through_institution": "Doing this will allow you to log in to <b>__appName__</b> through your institution portal and will reconfirm your institutional email address.",
+  "doing_this_allow_log_in_through_institution_2": "Doing this will allow you to log in to <0>__appName__</0> through your institution portal and will reconfirm your institutional email address.",
   "doing_this_will_verify_affiliation_and_allow_log_in": "Doing this will verify your affiliation with <b>__institutionName__</b> and will allow you to log in to <b>__appName__</b> through your institution.",
   "doing_this_will_verify_affiliation_and_allow_log_in_2": "Doing this will verify your affiliation with <0>__institutionName__</0> and will allow you to log in to <0>__appName__</0> through your institution.",
   "email_already_associated_with": "The <b>__email1__</b> email is already associated with the <b>__email2__</b> <b>__appName__</b> account.",
@@ -221,6 +222,7 @@
   "can_link_institution_email_by_clicking": "You can link your <b>__email__</b> <b>__appName__</b> account to your  <b>__institutionName__</b> account by clicking <b>__clickText__</b>.",
   "can_link_institution_email_to_login": "You can link your <b>__email__</b> <b>__appName__</b> account to your <b>__institutionName__</b> account, which will allow you to log in to <b>__appName__</b> through your institution portal and will reconfirm your institutional email address.",
   "can_link_your_institution_acct": "You can now <b>link</b> your <b>__appName__</b> account to your <b>__institutionName__</b> institutional account.",
+  "can_link_your_institution_acct_2": "You can now <0>link</0> your <0>__appName__</0> account to your <0>__institutionName__</0> institutional account.",
   "can_now_link_to_institution_acct": "You can link your <b>__email__<b> <b>__appName__</b> account to your <b>__institutionName__</b> institutional account.</b></b>",
   "click_link_to_proceed": "Click <b>__clickText__</b> below to proceed.",
   "continue_with_email": "<a href=\"__link__\">Continue to __appName__</a> with your <b>__email__</b> account",
@@ -231,6 +233,7 @@
   "account_linking": "Account Linking",
   "account_with_email_exists": "It looks like an <b>__appName__</b> account with the email <b>__email__</b> already exists.",
   "acct_linked_to_institution_acct": "You can <b>log in</b> to Overleaf through your <b>__institutionName__</b> institutional login.",
+  "acct_linked_to_institution_acct_2": "You can <0>log in</0> to Overleaf through your <0>__institutionName__</0> institutional login.",
   "alternatively_create_new_institution_account": "Alternatively, you can create a <b>new account</b> with your institution email (<b>__email__</b>) by clicking <b>__clickText__</b>.",
   "as_a_member_of_sso": "As a member of <b>__institutionName__</b>, you can log in to <b>__appName__</b> through your institution portal.",
   "as_a_member_of_sso_required": "As a member of <b>__institutionName__</b>, you must log in to <b>__appName__</b> through your institution portal.",