diff --git a/.changeset/enterprise-connection-page.md b/.changeset/enterprise-connection-page.md new file mode 100644 index 00000000000..3b643e2ea7a --- /dev/null +++ b/.changeset/enterprise-connection-page.md @@ -0,0 +1,11 @@ +--- +'@clerk/localizations': patch +'@clerk/shared': patch +'@clerk/ui': patch +--- + +Each enterprise connection listed on the organization Security page now opens its own page. It lists the connection name and domains, the service provider values to copy into the identity provider, the identity provider configuration behind an Edit form, and the connection settings as a form you save. The header carries one action, either Activate or Continue setup, and deactivating or removing the connection lives in a Danger zone section at the bottom of the page. The row menu is gone; click the row instead. + +The setup wizard's domains step now shows a checkbox per verified domain, so an admin picks which domains a connection covers. A domain another connection of the organization already authenticates is disabled and labelled with that connection's name, and an error from creating the connection is shown on the provider step instead of being dropped. + +New customization handles ship with it: the `organizationProfileSecuritySsoConnectionRow` and `organizationProfileSecuritySsoConnectionPage` appearance elements, the `configureSSOVerifyDomainCardCheckbox` element, the `claimed` badge id, the new `FieldId` values for the connection settings, and the `ssoConnectionName`, `ssoConnectionDomains`, `ssoConnectionServiceProvider`, `ssoConnectionIdentityProvider`, `ssoConnectionSettings` and `ssoConnectionDangerZone` `ProfileSectionId` values. diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index 4cd5f624e81..fd4597949ca 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -807,9 +807,11 @@ export const arSA: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'لم يتم التحقق', badge__verified: 'تم التحقق', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1285,6 +1287,68 @@ export const arSA: LocalizationResource = { title: 'حذف النطاق', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index e3a7b3e42d3..fe3ed7f6329 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -807,9 +807,11 @@ export const beBY: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Не пацверджана', badge__verified: 'Пацверджана', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1288,6 +1290,68 @@ export const beBY: LocalizationResource = { title: 'Выдаліць дамен', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index df57d777dce..c6be2e48f7e 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -808,9 +808,11 @@ export const bgBG: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Непотвърден', badge__verified: 'Потвърден', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1288,6 +1290,68 @@ export const bgBG: LocalizationResource = { title: 'Премахване на домейн', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts index e616bde8ef4..adc228ee6af 100644 --- a/packages/localizations/src/bn-IN.ts +++ b/packages/localizations/src/bn-IN.ts @@ -813,9 +813,11 @@ export const bnIN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'যাচাই করা হয়নি', badge__verified: 'যাচাই করা হয়েছে', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const bnIN: LocalizationResource = { title: 'ডোমেন সরান', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index 7e9bd721b39..f0cd8a5027d 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -814,9 +814,11 @@ export const caES: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Sense verificar', badge__verified: 'Verificat', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const caES: LocalizationResource = { title: 'Elimina domini', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index 7465db68394..2c5a68fd287 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -811,9 +811,11 @@ export const csCZ: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Neověřeno', badge__verified: 'Ověřeno', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1293,6 +1295,68 @@ export const csCZ: LocalizationResource = { title: 'Odebrat doménu', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 38d6262c49b..996558bf6fd 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -807,9 +807,11 @@ export const daDK: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Ikke bekræftet', badge__verified: 'Bekræftet', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1286,6 +1288,68 @@ export const daDK: LocalizationResource = { title: 'Fjern domæne', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index e979b57f942..1290fc0357e 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -813,9 +813,11 @@ export const deDE: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Nicht verifiziert', badge__verified: 'Verifiziert', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1301,6 +1303,68 @@ export const deDE: LocalizationResource = { title: 'Domain entfernen', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 586c31308d3..6f4d9c5315e 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -807,9 +807,11 @@ export const elGR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Μη επαληθευμένο', badge__verified: 'Επαληθευμένο', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1291,6 +1293,68 @@ export const elGR: LocalizationResource = { title: 'Αφαίρεση τομέα', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index e2077393f49..8a130982cc7 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -807,9 +807,11 @@ export const enGB: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Unverified', badge__verified: 'Verified', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const enGB: LocalizationResource = { title: 'Remove domain', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index f8a3fe28875..7f3e0b7b9b5 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -828,9 +828,11 @@ export const enUS: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: 'Used by another connection', badge__expired: 'Expired', badge__unverified: 'Unverified', badge__verified: 'Verified', + checkboxLabel: 'Use {{domain}} for this connection', expiredAtLabel: "Domain verification expired on {{ date | shortDate('en-US') }}. Verify again to generate a new DNS record.", expiredLabel: 'Domain verification expired. Verify again to generate a new DNS record.', @@ -860,7 +862,8 @@ export const enUS: LocalizationResource = { subtitle__inactive: "You're about to remove {{domain}} from this enterprise connection.", title: 'Removing domain', }, - subtitle: 'Add and verify ownership of the domains your organization uses to sign in.', + subtitle: + 'Add and verify ownership of the domains your organization uses to sign in, then pick the ones this connection covers.', title: 'Add SSO domains', }, resetConnectionDialog: { @@ -1320,6 +1323,68 @@ export const enUS: LocalizationResource = { title: 'Remove domain', }, securityPage: { + connectionPage: { + actions: { + activate: 'Activate', + continueSetup: 'Continue setup', + }, + dangerZone: { + deactivateButton: 'Deactivate connection', + removeButton: 'Remove connection', + title: 'Danger zone', + }, + domains: { + title: 'Domains', + }, + identityProvider: { + certificateExpires: 'Certificate expires', + clientSecret: { + placeholder: 'Leave empty to keep the current secret', + }, + editButton: 'Edit', + form: { + title: 'Edit identity provider', + }, + title: 'Identity provider', + }, + name: { + editButton: 'Edit', + form: { + title: 'Rename connection', + }, + title: 'Name', + }, + serviceProvider: { + acsUrl: 'Assertion consumer service (ACS) URL', + entityId: 'Entity ID', + metadataUrl: 'Metadata URL', + redirectUri: 'Redirect URI', + title: 'Service provider', + }, + settings: { + allowAdditionalIdentifiers: { + description: 'Members may keep other sign-in methods.', + label: 'Allow additional identifiers', + }, + allowIdpInitiated: { + description: 'Sign-in may start from the identity provider.', + label: 'Allow IdP-initiated flow', + }, + allowSubdomains: { + description: "Also match subdomains of the connection's domains.", + label: 'Allow subdomains', + }, + forceAuthn: { + description: 'Re-authenticate at the identity provider on every sign-in.', + label: 'Force re-authentication', + }, + syncUserAttributes: { + description: 'Refresh name and email from the identity provider on each sign-in.', + label: 'Sync user attributes', + }, + title: 'Settings', + }, + }, directorySyncSection: { badge__active: 'Active', badge__inactive: 'Inactive', diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts index a1cf07baf9c..9dba726d490 100644 --- a/packages/localizations/src/es-CR.ts +++ b/packages/localizations/src/es-CR.ts @@ -807,9 +807,11 @@ export const esCR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Sin verificar', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1291,6 +1293,68 @@ export const esCR: LocalizationResource = { title: 'Eliminar dominio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index b03330c65e6..79c0cd3682d 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -813,9 +813,11 @@ export const esES: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Sin verificar', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1296,6 +1298,68 @@ export const esES: LocalizationResource = { title: 'Eliminar dominio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 9e7107d9511..f479dcec92b 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -808,9 +808,11 @@ export const esMX: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Sin verificar', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1292,6 +1294,68 @@ export const esMX: LocalizationResource = { title: 'Eliminar dominio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts index 1f7dafc1eac..fc1ccfb673d 100644 --- a/packages/localizations/src/es-UY.ts +++ b/packages/localizations/src/es-UY.ts @@ -807,9 +807,11 @@ export const esUY: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Sin verificar', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1290,6 +1292,68 @@ export const esUY: LocalizationResource = { title: 'Eliminar dominio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts index 2275beb9fc3..d1a86efd480 100644 --- a/packages/localizations/src/fa-IR.ts +++ b/packages/localizations/src/fa-IR.ts @@ -812,9 +812,11 @@ export const faIR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'تأییدنشده', badge__verified: 'تأییدشده', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const faIR: LocalizationResource = { title: 'حذف دامنه', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 745601b8108..e42c0aae86c 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -813,9 +813,11 @@ export const fiFI: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Vahvistamaton', badge__verified: 'Vahvistettu', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1296,6 +1298,68 @@ export const fiFI: LocalizationResource = { title: 'Poista verkkotunnus', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index 48e19a902f8..edc24d821b1 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -815,9 +815,11 @@ export const frFR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Non vérifié', badge__verified: 'Vérifié', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1301,6 +1303,68 @@ export const frFR: LocalizationResource = { title: 'Supprimer un domaine', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index c72d1b44658..32e3238bac1 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -807,9 +807,11 @@ export const heIL: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'לא מאומת', badge__verified: 'מאומת', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1282,6 +1284,68 @@ export const heIL: LocalizationResource = { title: 'הסר דומיין', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts index a8fc2cecb88..029c978649b 100644 --- a/packages/localizations/src/hi-IN.ts +++ b/packages/localizations/src/hi-IN.ts @@ -813,9 +813,11 @@ export const hiIN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'असत्यापित', badge__verified: 'सत्यापित', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const hiIN: LocalizationResource = { title: 'डोमेन हटाएं', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 46a33c63079..b2d915f4ddf 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -814,9 +814,11 @@ export const hrHR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Nepotvrđeno', badge__verified: 'Potvrđeno', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1296,6 +1298,68 @@ export const hrHR: LocalizationResource = { title: 'Ukloni domenu', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 7b041d5ed91..4348cb10ab1 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -814,9 +814,11 @@ export const huHU: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Nincs igazolva', badge__verified: 'Igazolva', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const huHU: LocalizationResource = { title: 'Domain törlése', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index ec2492089e6..e6a7242915a 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -807,9 +807,11 @@ export const idID: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Belum terverifikasi', badge__verified: 'Terverifikasi', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1289,6 +1291,68 @@ export const idID: LocalizationResource = { title: 'Hapus domain', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index a92921b045f..947cadc2355 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -813,9 +813,11 @@ export const isIS: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Óstaðfest', badge__verified: 'Staðfest', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1296,6 +1298,68 @@ export const isIS: LocalizationResource = { title: 'Fjarlægja lén', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index bfd6c55b6dc..42db5e09eb7 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -813,9 +813,11 @@ export const itIT: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Non verificato', badge__verified: 'Verificato', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1296,6 +1298,68 @@ export const itIT: LocalizationResource = { title: 'Rimuovi dominio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 874193c5536..b38e0a973aa 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -814,9 +814,11 @@ export const jaJP: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: '未確認', badge__verified: '確認済み', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const jaJP: LocalizationResource = { title: 'ドメインの削除', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts index 3c56fb5faa5..7c7cf5e3f87 100644 --- a/packages/localizations/src/kk-KZ.ts +++ b/packages/localizations/src/kk-KZ.ts @@ -807,9 +807,11 @@ export const kkKZ: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Расталмаған', badge__verified: 'Расталған', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1282,6 +1284,68 @@ export const kkKZ: LocalizationResource = { title: 'Доменді жою', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 772cd18ce00..769e698b417 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -811,9 +811,11 @@ export const koKR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: '미확인', badge__verified: '확인됨', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const koKR: LocalizationResource = { title: '도메인 삭제', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 988439b65e7..82464828e99 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -807,9 +807,11 @@ export const mnMN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Баталгаажаагүй', badge__verified: 'Баталгаажсан', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1289,6 +1291,68 @@ export const mnMN: LocalizationResource = { title: 'Домэйн устгах', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts index 5fd32c89915..d13904c2137 100644 --- a/packages/localizations/src/ms-MY.ts +++ b/packages/localizations/src/ms-MY.ts @@ -815,9 +815,11 @@ export const msMY: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Belum disahkan', badge__verified: 'Disahkan', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1298,6 +1300,68 @@ export const msMY: LocalizationResource = { title: 'Keluarkan domain', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 789e9ec5aa9..b1e9796364a 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -814,9 +814,11 @@ export const nbNO: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Ikke verifisert', badge__verified: 'Verifisert', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const nbNO: LocalizationResource = { title: 'Fjern domene', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index 8916645b14f..ab77f55a2be 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -807,9 +807,11 @@ export const nlBE: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Niet geverifieerd', badge__verified: 'Geverifieerd', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1289,6 +1291,68 @@ export const nlBE: LocalizationResource = { title: 'Domein verwijderen', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index df892a21f1e..45b190124eb 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -807,9 +807,11 @@ export const nlNL: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Niet geverifieerd', badge__verified: 'Geverifieerd', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1289,6 +1291,68 @@ export const nlNL: LocalizationResource = { title: 'Domein verwijderen', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index 9f20bebcaee..61de28f9faa 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -807,9 +807,11 @@ export const plPL: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Niezweryfikowano', badge__verified: 'Zweryfikowano', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const plPL: LocalizationResource = { title: 'Usuń domenę', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index 554de9d7300..1d96b3278ae 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -813,9 +813,11 @@ export const ptBR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Não verificado', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const ptBR: LocalizationResource = { title: 'Excluir domínio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 511e4a4e428..cd3440dc725 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -815,9 +815,11 @@ export const ptPT: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Não verificado', badge__verified: 'Verificado', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const ptPT: LocalizationResource = { title: 'Eliminar domínio', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index e05cd23312c..17f01b1782e 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -813,9 +813,11 @@ export const roRO: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Neverificat', badge__verified: 'Verificat', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const roRO: LocalizationResource = { title: 'Elimină domeniul', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 73970a35392..2797e7e91d2 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -807,9 +807,11 @@ export const ruRU: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Не подтверждён', badge__verified: 'Подтверждён', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1291,6 +1293,68 @@ export const ruRU: LocalizationResource = { title: 'Удалить домен', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index b2179b9cbb9..53c9182feed 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -807,9 +807,11 @@ export const skSK: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Neoverené', badge__verified: 'Overené', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const skSK: LocalizationResource = { title: 'Odstrániť doménu', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index e75f1e7e2ed..6fccddae128 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -807,9 +807,11 @@ export const srRS: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Nepotvrđeno', badge__verified: 'Potvrđeno', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const srRS: LocalizationResource = { title: 'Ukloni domen', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 4f9913d0ff5..50223b67001 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -807,9 +807,11 @@ export const svSE: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Overifierad', badge__verified: 'Verifierad', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const svSE: LocalizationResource = { title: 'Ta bort domän', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts index e8dca720c74..1b3f83f090f 100644 --- a/packages/localizations/src/ta-IN.ts +++ b/packages/localizations/src/ta-IN.ts @@ -815,9 +815,11 @@ export const taIN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'சரிபார்க்கப்படவில்லை', badge__verified: 'சரிபார்க்கப்பட்டது', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1300,6 +1302,68 @@ export const taIN: LocalizationResource = { title: 'டொமைன் நீக்கு', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts index 10eb76b48c2..e087dcc0f8b 100644 --- a/packages/localizations/src/te-IN.ts +++ b/packages/localizations/src/te-IN.ts @@ -814,9 +814,11 @@ export const teIN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'ధృవీకరించబడలేదు', badge__verified: 'ధృవీకరించబడింది', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1297,6 +1299,68 @@ export const teIN: LocalizationResource = { title: 'డొమైన్‌ను తొలగించండి', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 9e40d971bdd..a979fc15df9 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -811,9 +811,11 @@ export const thTH: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'ยังไม่ได้ยืนยัน', badge__verified: 'ยืนยันแล้ว', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1288,6 +1290,68 @@ export const thTH: LocalizationResource = { title: 'ลบโดเมน', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 85399202f47..0274cd116fe 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -807,9 +807,11 @@ export const trTR: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Doğrulanmadı', badge__verified: 'Doğrulandı', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const trTR: LocalizationResource = { title: 'Alan adını kaldır', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 8a2b22331cf..8efdb2af84b 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -807,9 +807,11 @@ export const ukUA: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Не підтверджено', badge__verified: 'Підтверджено', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1287,6 +1289,68 @@ export const ukUA: LocalizationResource = { title: 'Remove domain', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index 054a022fea6..a7e24dadae0 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -813,9 +813,11 @@ export const viVN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: 'Chưa xác minh', badge__verified: 'Đã xác minh', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1295,6 +1297,68 @@ export const viVN: LocalizationResource = { title: 'Xóa tên miền', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index 2a59b0658be..5303d2181b9 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -807,9 +807,11 @@ export const zhCN: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: '未验证', badge__verified: '已验证', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1280,6 +1282,68 @@ export const zhCN: LocalizationResource = { title: '删除域名', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index e953d0c69e7..ae00b4dbfb8 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -810,9 +810,11 @@ export const zhTW: LocalizationResource = { }, organizationDomainsStep: { domainCard: { + badge__claimed: undefined, badge__expired: undefined, badge__unverified: '未驗證', badge__verified: '已驗證', + checkboxLabel: undefined, expiredAtLabel: undefined, expiredLabel: undefined, removeButtonTooltip__lastVerifiedDomain: undefined, @@ -1283,6 +1285,68 @@ export const zhTW: LocalizationResource = { title: '移除網域', }, securityPage: { + connectionPage: { + actions: { + activate: undefined, + continueSetup: undefined, + }, + dangerZone: { + deactivateButton: undefined, + removeButton: undefined, + title: undefined, + }, + domains: { + title: undefined, + }, + identityProvider: { + certificateExpires: undefined, + clientSecret: { + placeholder: undefined, + }, + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + name: { + editButton: undefined, + form: { + title: undefined, + }, + title: undefined, + }, + serviceProvider: { + acsUrl: undefined, + entityId: undefined, + metadataUrl: undefined, + redirectUri: undefined, + title: undefined, + }, + settings: { + allowAdditionalIdentifiers: { + description: undefined, + label: undefined, + }, + allowIdpInitiated: { + description: undefined, + label: undefined, + }, + allowSubdomains: { + description: undefined, + label: undefined, + }, + forceAuthn: { + description: undefined, + label: undefined, + }, + syncUserAttributes: { + description: undefined, + label: undefined, + }, + title: undefined, + }, + }, directorySyncSection: { badge__active: undefined, badge__inactive: undefined, diff --git a/packages/shared/src/types/elementIds.ts b/packages/shared/src/types/elementIds.ts index d23b9c9f9d3..a401bb0a20b 100644 --- a/packages/shared/src/types/elementIds.ts +++ b/packages/shared/src/types/elementIds.ts @@ -41,6 +41,11 @@ export type FieldId = | 'redirectUri' | 'acsUrl' | 'spEntityId' + | 'syncUserAttributes' + | 'allowAdditionalIdentifiers' + | 'allowSubdomains' + | 'allowIdpInitiated' + | 'forceAuthn' | 'web3WalletName' | 'domain'; export type ProfileSectionId = @@ -72,6 +77,12 @@ export type ProfileSectionId = | 'resetSso' | 'testSsoUrl' | 'testResults' + | 'ssoConnectionName' + | 'ssoConnectionDomains' + | 'ssoConnectionServiceProvider' + | 'ssoConnectionIdentityProvider' + | 'ssoConnectionSettings' + | 'ssoConnectionDangerZone' | 'accountCredits'; export type ProfilePageId = | 'account' diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index a7758f5425a..d5ed7482917 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1220,6 +1220,68 @@ export type __internal_LocalizationResource = { tooltip__noRole: LocalizationValue; tooltipLabel: LocalizationValue; }; + connectionPage: { + actions: { + activate: LocalizationValue; + continueSetup: LocalizationValue; + }; + dangerZone: { + title: LocalizationValue; + deactivateButton: LocalizationValue; + removeButton: LocalizationValue; + }; + name: { + title: LocalizationValue; + editButton: LocalizationValue; + form: { + title: LocalizationValue; + }; + }; + domains: { + title: LocalizationValue; + }; + serviceProvider: { + title: LocalizationValue; + acsUrl: LocalizationValue; + entityId: LocalizationValue; + metadataUrl: LocalizationValue; + redirectUri: LocalizationValue; + }; + identityProvider: { + title: LocalizationValue; + certificateExpires: LocalizationValue; + editButton: LocalizationValue; + form: { + title: LocalizationValue; + }; + clientSecret: { + placeholder: LocalizationValue; + }; + }; + settings: { + title: LocalizationValue; + syncUserAttributes: { + label: LocalizationValue; + description: LocalizationValue; + }; + allowAdditionalIdentifiers: { + label: LocalizationValue; + description: LocalizationValue; + }; + allowSubdomains: { + label: LocalizationValue; + description: LocalizationValue; + }; + allowIdpInitiated: { + label: LocalizationValue; + description: LocalizationValue; + }; + forceAuthn: { + label: LocalizationValue; + description: LocalizationValue; + }; + }; + }; directorySyncSection: { title: LocalizationValue; badge__unconfigured: LocalizationValue; @@ -1549,6 +1611,12 @@ export type __internal_LocalizationResource = { }; }; configureSSO: { + connectionScopeBanner: { + title__editing: LocalizationValue<'name'>; + subtitle__editing: LocalizationValue<'count'>; + title__adding: LocalizationValue; + subtitle__adding: LocalizationValue<'count'>; + }; missingManageEnterpriseConnectionsPermission: { title: LocalizationValue; subtitle: LocalizationValue; @@ -1556,12 +1624,6 @@ export type __internal_LocalizationResource = { navbar: { title: LocalizationValue; }; - connectionScopeBanner: { - title__editing: LocalizationValue<'name'>; - subtitle__editing: LocalizationValue<'count'>; - title__adding: LocalizationValue; - subtitle__adding: LocalizationValue<'count'>; - }; resetConnectionDialog: { cancelButton: LocalizationValue; confirmationFieldLabel: LocalizationValue<'name'>; @@ -1606,6 +1668,8 @@ export type __internal_LocalizationResource = { badge__verified: LocalizationValue; badge__unverified: LocalizationValue; badge__expired: LocalizationValue; + badge__claimed: LocalizationValue; + checkboxLabel: LocalizationValue<'domain'>; verifiedAtLabel: LocalizationValue<'date'>; expiredAtLabel: LocalizationValue<'date'>; expiredLabel: LocalizationValue; diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx index 1fbcaea084e..63f6aa09c3d 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx @@ -47,8 +47,9 @@ export const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObjec const { isLoading, enterpriseConnection, - enterpriseConnections, - connectionScope, + connectionDomains, + setConnectionDomains, + claimedDomains, organizationEnterpriseConnection, testRuns, enterpriseConnectionMutations, @@ -66,8 +67,9 @@ export const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObjec organizationEnterpriseConnection={organizationEnterpriseConnection} testRuns={testRuns} enterpriseConnection={enterpriseConnection} - enterpriseConnections={enterpriseConnections} - connectionScope={connectionScope} + connectionDomains={connectionDomains} + setConnectionDomains={setConnectionDomains} + claimedDomains={claimedDomains} contentRef={contentRef} enterpriseConnectionMutations={enterpriseConnectionMutations} organizationDomainMutations={organizationDomainMutations} diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx index 90e6d68fdca..272f9dd6657 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx @@ -1,7 +1,6 @@ import type { EnterpriseConnectionResource, OrganizationDomainResource } from '@clerk/shared/types'; import React, { type PropsWithChildren } from 'react'; -import type { ConnectionScope } from './domain/connectionScope'; import type { OrganizationEnterpriseConnection } from './domain/organizationEnterpriseConnection'; import type { EnterpriseConnectionMutations, @@ -19,8 +18,11 @@ export type { OrganizationDomainMutations }; */ export interface ConfigureSSOData { enterpriseConnection: EnterpriseConnectionResource | undefined; - enterpriseConnections: EnterpriseConnectionResource[]; - connectionScope: ConnectionScope; + /** The scoped connection's domains, a draft while the scope is `new`. */ + connectionDomains: string[]; + setConnectionDomains: (domains: string[]) => Promise; + /** Domains other connections of the organization authenticate, keyed to that connection's name. */ + claimedDomains: Map; /** Ref to the wizard's scrollable content container. */ contentRef: React.RefObject; enterpriseConnectionMutations: EnterpriseConnectionMutations; @@ -33,8 +35,9 @@ export interface ConfigureSSOData { interface ConfigureSSOProviderProps { enterpriseConnection: EnterpriseConnectionResource | undefined; - enterpriseConnections: EnterpriseConnectionResource[]; - connectionScope: ConnectionScope; + connectionDomains: string[]; + setConnectionDomains: (domains: string[]) => Promise; + claimedDomains: Map; organizationEnterpriseConnection: OrganizationEnterpriseConnection; testRuns: TestRunsView; organizationDomains: OrganizationDomainResource[] | undefined; @@ -49,8 +52,9 @@ ConfigureSSOContext.displayName = 'ConfigureSSOContext'; export const ConfigureSSOProvider = ({ enterpriseConnection, - enterpriseConnections, - connectionScope, + connectionDomains, + setConnectionDomains, + claimedDomains, organizationEnterpriseConnection, testRuns, organizationDomains, @@ -64,8 +68,9 @@ export const ConfigureSSOProvider = ({ () => ({ contentRef, enterpriseConnection, - enterpriseConnections, - connectionScope, + connectionDomains, + setConnectionDomains, + claimedDomains, organizationEnterpriseConnection, testRuns, organizationDomains, @@ -81,8 +86,9 @@ export const ConfigureSSOProvider = ({ testRuns, organizationDomains, enterpriseConnection, - enterpriseConnections, - connectionScope, + connectionDomains, + setConnectionDomains, + claimedDomains, onExit, ], ); diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx index 67c5501ec25..316c763bf1a 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx @@ -4,8 +4,7 @@ import { CardStateProvider } from '@/elements/contexts'; import { ConfigureSSOProvider } from './ConfigureSSOContext'; import { ConfigureSSOHeader } from './ConfigureSSOHeader'; -import { ConnectionScopeBanner } from './ConnectionScopeBanner'; -import { areAllOrganizationDomainsVerified } from './domain/organizationEnterpriseConnection'; +import { areConnectionDomainsReady } from './domain/organizationEnterpriseConnection'; import { Wizard, type WizardStepConfig } from './elements/Wizard'; import { ActivateStep, ConfigureStep, OrganizationDomainsStep, TestConfigurationStep } from './steps'; @@ -15,17 +14,17 @@ export type ConfigureSSOWizardProps = Omit { - const { organizationEnterpriseConnection: c, organizationDomains } = props; + const { organizationEnterpriseConnection: c, connectionDomains, claimedDomains, organizationDomains } = props; - const allDomainsVerified = areAllOrganizationDomainsVerified(organizationDomains); + const domainsReady = areConnectionDomainsReady(connectionDomains, organizationDomains, claimedDomains); const steps = React.useMemo( () => [ - { id: 'verify-domain', label: 'Domains', isComplete: () => allDomainsVerified }, + { id: 'verify-domain', label: 'Domains', isComplete: () => domainsReady }, { id: 'configure', label: 'Connection', - isReachable: () => allDomainsVerified || c.hasConnection, + isReachable: () => domainsReady || c.hasConnection, isComplete: () => c.hasMinimumConfiguration || c.isActive, }, { @@ -41,7 +40,7 @@ export const ConfigureSSOWizard = ({ title, forceInitialStep, ...props }: Config isComplete: () => c.isActive, }, ], - [c, allDomainsVerified], + [c, domainsReady], ); const initialStepId = forceInitialStep ? steps[0].id : undefined; @@ -54,8 +53,6 @@ export const ConfigureSSOWizard = ({ title, forceInitialStep, ...props }: Config > - - diff --git a/packages/ui/src/components/ConfigureSSO/ConnectionScopeBanner.tsx b/packages/ui/src/components/ConfigureSSO/ConnectionScopeBanner.tsx deleted file mode 100644 index 597b7a18a96..00000000000 --- a/packages/ui/src/components/ConfigureSSO/ConnectionScopeBanner.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { descriptors, localizationKeys } from '@/customizables'; -import { Alert } from '@/ui/elements/Alert'; - -import { useConfigureSSO } from './ConfigureSSOContext'; - -export const ConnectionScopeBanner = (): JSX.Element | null => { - const { enterpriseConnections, enterpriseConnection } = useConfigureSSO(); - - if (enterpriseConnections.length <= 1) { - return null; - } - - const count = enterpriseConnections.length; - - return ( - ({ marginInline: t.space.$5, marginBlockStart: t.space.$5 })} - /> - ); -}; diff --git a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx index fc3d88f7fbe..b5309a864c5 100644 --- a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx @@ -116,75 +116,6 @@ describe('ConfigureSSO', () => { }); }); - describe('connection scope banner', () => { - const samlConnection = { - idpSsoUrl: 'https://idp.example.com/sso', - idpEntityId: 'https://idp.example.com/entity', - idpCertificate: 'CERT', - }; - - const connection = (overrides: Record) => - ({ - provider: 'saml_okta', - active: true, - organizationId: 'Org1', - domains: ['clerk.com'], - samlConnection, - ...overrides, - }) as any; - - const withOrganizationFixtures = (f: Parameters[0]>[0]) => { - f.withEnterpriseSso({ selfServeSSO: true }); - f.withEmailAddress(); - f.withOrganizations(); - f.withUser({ - email_addresses: ['test@clerk.com'], - organization_memberships: [{ name: 'Org1', permissions: ['org:sys_entconns:manage'] }], - }); - }; - - it('names the scoped connection when the organization has more than one', async () => { - const { wrapper, fixtures } = await createFixtures(withOrganizationFixtures); - - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([ - connection({ id: 'ent_2', name: 'second.com', createdAt: new Date('2024-06-01T00:00:00Z') }), - connection({ id: 'ent_1', name: 'first.com', createdAt: new Date('2024-01-01T00:00:00Z') }), - ]); - mockOrganizationDomains(fixtures, [verifiedDomain]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [{ id: 'run_1', status: 'success' }], - total_count: 1, - } as any); - - const { findByText } = render(, { wrapper }); - - // The standalone host has no list UI, so it falls back to the oldest connection. - expect(await findByText('Editing "first.com"')).toBeInTheDocument(); - expect( - await findByText('This organization has 2 SSO connections. Changes here apply only to this connection.'), - ).toBeInTheDocument(); - }); - - it('stays silent for a single connection', async () => { - const { wrapper, fixtures } = await createFixtures(withOrganizationFixtures); - - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([ - connection({ id: 'ent_1', name: 'first.com', createdAt: new Date('2024-01-01T00:00:00Z') }), - ]); - mockOrganizationDomains(fixtures, [verifiedDomain]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [{ id: 'run_1', status: 'success' }], - total_count: 1, - } as any); - - const { findByText, queryByText } = render(, { wrapper }); - - await findByText(/sso connection is active/i); - expect(queryByText(/^Editing /)).not.toBeInTheDocument(); - expect(queryByText('Adding a new SSO connection')).not.toBeInTheDocument(); - }); - }); - describe('state machine mounts on the right step', () => { it('mounts on select-provider when all organization domains are verified and there is no connection', async () => { const { wrapper, fixtures } = await createFixtures(f => { diff --git a/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts b/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts index 0974f811736..1cef06cdf00 100644 --- a/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts +++ b/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts @@ -9,8 +9,10 @@ import type { import { describe, expect, it } from 'vitest'; import { - areAllOrganizationDomainsVerified, + areConnectionDomainsReady, connectionBackingEmail, + defaultConnectionDomains, + domainsClaimedByOtherConnections, isEnterpriseConnectionConfigured, organizationEnterpriseConnection, sortEnterpriseConnections, @@ -364,34 +366,78 @@ describe('isEnterpriseConnectionConfigured', () => { }); }); -describe('areAllOrganizationDomainsVerified', () => { - const makeDomain = (status: 'verified' | 'unverified' | null): OrganizationDomainResource => - ({ - id: `dmn_${status}`, - name: 'acme.com', - ownershipVerification: status ? { status } : null, - }) as OrganizationDomainResource; +const makeDomain = (name: string, status: 'verified' | 'unverified' | null): OrganizationDomainResource => + ({ + id: `dmn_${name}`, + name, + ownershipVerification: status ? { status } : null, + }) as OrganizationDomainResource; + +describe('domainsClaimedByOtherConnections', () => { + const connection = (id: string, name: string, domains: string[]) => + ({ id, name, domains }) as unknown as EnterpriseConnectionResource; + + it('maps each domain of every other connection to that connection name', () => { + const claimed = domainsClaimedByOtherConnections( + [connection('ent_1', 'Okta', ['acme.com']), connection('ent_2', 'Google', ['example.com', 'other.com'])], + 'ent_1', + ); - it('undefined domains → false', () => { - expect(areAllOrganizationDomainsVerified(undefined)).toBe(false); + expect(claimed.get('acme.com')).toBeUndefined(); + expect(claimed.get('example.com')).toBe('Google'); + expect(claimed.get('other.com')).toBe('Google'); }); - it('null domains → false', () => { - expect(areAllOrganizationDomainsVerified(null)).toBe(false); + + it('treats every connection as another one while the scope is new', () => { + const claimed = domainsClaimedByOtherConnections([connection('ent_1', 'Okta', ['acme.com'])], undefined); + + expect(claimed.get('acme.com')).toBe('Okta'); }); - it('empty list → false', () => { - expect(areAllOrganizationDomainsVerified([])).toBe(false); +}); + +describe('defaultConnectionDomains', () => { + it('keeps verified domains no other connection claims', () => { + const domains = [ + makeDomain('acme.com', 'verified'), + makeDomain('pending.com', 'unverified'), + makeDomain('taken.com', 'verified'), + ]; + + expect(defaultConnectionDomains(domains, new Map([['taken.com', 'Okta']]))).toEqual(['acme.com']); }); - it('a single verified domain → true', () => { - expect(areAllOrganizationDomainsVerified([makeDomain('verified')])).toBe(true); + + it('undefined domains → empty', () => { + expect(defaultConnectionDomains(undefined, new Map())).toEqual([]); }); - it('every domain verified → true', () => { - expect(areAllOrganizationDomainsVerified([makeDomain('verified'), makeDomain('verified')])).toBe(true); +}); + +describe('areConnectionDomainsReady', () => { + it('no connection domains → false', () => { + expect(areConnectionDomainsReady([], [makeDomain('acme.com', 'verified')])).toBe(false); }); - it('any unverified domain → false', () => { - expect(areAllOrganizationDomainsVerified([makeDomain('verified'), makeDomain('unverified')])).toBe(false); + it('every connection domain verified → true', () => { + expect(areConnectionDomainsReady(['acme.com'], [makeDomain('acme.com', 'verified')])).toBe(true); + }); + it('a connection domain another connection claims → false', () => { + expect( + areConnectionDomainsReady(['acme.com'], [makeDomain('acme.com', 'verified')], new Map([['acme.com', 'Other']])), + ).toBe(false); + }); + it('a connection domain still unverified → false', () => { + expect( + areConnectionDomainsReady( + ['acme.com', 'pending.com'], + [makeDomain('acme.com', 'verified'), makeDomain('pending.com', 'unverified')], + ), + ).toBe(false); + }); + it('an unverified organization domain outside the connection does not block', () => { + expect( + areConnectionDomainsReady(['acme.com'], [makeDomain('acme.com', 'verified'), makeDomain('pending.com', null)]), + ).toBe(true); }); - it('domain without ownership verification → false', () => { - expect(areAllOrganizationDomainsVerified([makeDomain(null)])).toBe(false); + it('a connection domain missing from the organization list counts as ready', () => { + expect(areConnectionDomainsReady(['legacy.com'], [])).toBe(true); }); }); diff --git a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts index 260c3fcd0b6..a92cfc099d6 100644 --- a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts +++ b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts @@ -87,8 +87,61 @@ export const isEnterpriseConnectionConfigured = ( return Boolean(connection.samlConnection?.idpSsoUrl && connection.samlConnection?.idpEntityId); }; -export const areAllOrganizationDomainsVerified = (domains: OrganizationDomainResource[] | null | undefined): boolean => - !!domains?.length && domains.every(domain => domain.ownershipVerification?.status === 'verified'); +export const isOrganizationDomainVerified = (domain: OrganizationDomainResource): boolean => + domain.ownershipVerification?.status === 'verified'; + +/** + * Domains every connection other than `scopedConnectionId` already authenticates, + * keyed to that connection's name. FAPI rejects a domain shared by two + * connections of the same instance, so the wizard never offers these. + */ +export const domainsClaimedByOtherConnections = ( + connections: EnterpriseConnectionResource[], + scopedConnectionId: string | undefined, +): Map => { + const claimed = new Map(); + for (const connection of connections) { + if (connection.id === scopedConnectionId) { + continue; + } + for (const domain of connection.domains ?? []) { + claimed.set(domain, connection.name); + } + } + return claimed; +}; + +/** + * The domains a connection would receive before its admin touches the + * selection: every verified organization domain no other connection claims. + */ +export const defaultConnectionDomains = ( + organizationDomains: OrganizationDomainResource[] | null | undefined, + claimed: Map, +): string[] => + (organizationDomains ?? []) + .filter(domain => isOrganizationDomainVerified(domain) && !claimed.has(domain.name)) + .map(domain => domain.name); + +/** + * Whether the connection's domains let the wizard move past the domains step: + * at least one domain, none of them still pending verification. A connection + * domain missing from the organization list was accepted by FAPI already, so it + * does not block. + */ +export const areConnectionDomainsReady = ( + connectionDomains: readonly string[], + organizationDomains: OrganizationDomainResource[] | null | undefined, + claimedDomains?: ReadonlyMap, +): boolean => + connectionDomains.length > 0 && + connectionDomains.every(name => { + if (claimedDomains?.has(name)) { + return false; + } + const organizationDomain = organizationDomains?.find(domain => domain.name === name); + return !organizationDomain || isOrganizationDomainVerified(organizationDomain); + }); const connectionStatus = ({ hasConnection, diff --git a/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx b/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx index 49458352532..16a526b0f4b 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx @@ -11,6 +11,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; // `samlConnection.idpSsoUrl` + `idpEntityId`, and the active path reads `active`. type MockConnection = { id: string; + name?: string; + domains?: string[]; provider: string; active?: boolean; createdAt?: Date; @@ -50,10 +52,12 @@ const mutationSpies = vi.hoisted(() => ({ })); const domainsState = vi.hoisted(() => ({ - data: undefined as Array<{ name: string }> | undefined, + data: undefined as Array<{ name: string; ownershipVerification?: { status: string } }> | undefined, isLoading: false, })); +const verifiedDomain = (name: string) => ({ name, ownershipVerification: { status: 'verified' } }); + vi.mock('@clerk/shared/react', () => ({ __internal_useOrganizationEnterpriseConnections: () => ({ data: connectionsState.data, @@ -188,24 +192,39 @@ describe('useOrganizationEnterpriseConnection — test-runs gating', () => { }); describe('useOrganizationEnterpriseConnection — mutations', () => { - it('createConnection forwards the provider and the organization domains', async () => { - domainsState.data = [{ name: 'acme.com' }, { name: 'example.com' }]; + it('createConnection forwards the provider and the verified organization domains', async () => { + domainsState.data = [verifiedDomain('acme.com'), verifiedDomain('example.com'), { name: 'pending.com' }]; const { result } = renderHook(() => useOrganizationEnterpriseConnection()); await result.current.enterpriseConnectionMutations.createConnection('saml_okta'); expect(mutationSpies.create).toHaveBeenCalledTimes(1); - // `name` is derived by FAPI, so it is not sent from the client; `domains` - // are the verified organization domains passed straight through by the - // caller. + // `name` is derived by FAPI, so it is not sent from the client; an + // unverified domain never reaches the create. expect(mutationSpies.create).toHaveBeenCalledWith({ provider: 'saml_okta', domains: ['acme.com', 'example.com'], }); }); - it('createConnection forwards undefined domains when the organization has none', async () => { + it('createConnection leaves out a domain another connection already authenticates', async () => { + connectionsState.data = [{ ...configuredConnection('ent_other'), name: 'Other', domains: ['acme.com'] }]; + domainsState.data = [verifiedDomain('acme.com'), verifiedDomain('example.com')]; + + const { result } = renderHook(() => useOrganizationEnterpriseConnection()); + + act(() => result.current.selectConnection({ kind: 'new' })); + + expect(result.current.claimedDomains.get('acme.com')).toBe('Other'); + expect(result.current.connectionDomains).toEqual(['example.com']); + + await result.current.enterpriseConnectionMutations.createConnection('saml_okta'); + + expect(mutationSpies.create).toHaveBeenCalledWith({ provider: 'saml_okta', domains: ['example.com'] }); + }); + + it('createConnection forwards an empty domain list when the organization has none', async () => { domainsState.data = undefined; const { result } = renderHook(() => useOrganizationEnterpriseConnection()); @@ -215,10 +234,34 @@ describe('useOrganizationEnterpriseConnection — mutations', () => { expect(mutationSpies.create).toHaveBeenCalledTimes(1); expect(mutationSpies.create).toHaveBeenCalledWith({ provider: 'saml_okta', - domains: undefined, + domains: [], }); }); + it('setConnectionDomains edits the draft for a new scope and updates an existing connection', async () => { + domainsState.data = [verifiedDomain('acme.com'), verifiedDomain('example.com')]; + + const { result } = renderHook(() => useOrganizationEnterpriseConnection()); + + await act(() => result.current.setConnectionDomains(['example.com'])); + + expect(mutationSpies.update).not.toHaveBeenCalled(); + expect(result.current.connectionDomains).toEqual(['example.com']); + + await result.current.enterpriseConnectionMutations.createConnection('saml_okta'); + + expect(mutationSpies.create).toHaveBeenCalledWith({ provider: 'saml_okta', domains: ['example.com'] }); + + connectionsState.data = [{ ...configuredConnection('ent_1'), domains: ['acme.com'] }]; + const existing = renderHook(() => useOrganizationEnterpriseConnection()); + + expect(existing.result.current.connectionDomains).toEqual(['acme.com']); + + await act(() => existing.result.current.setConnectionDomains(['acme.com', 'example.com'])); + + expect(mutationSpies.update).toHaveBeenCalledWith('ent_1', { domains: ['acme.com', 'example.com'] }); + }); + it('setConnectionActive forwards only the active flag to update', async () => { const { result } = renderHook(() => useOrganizationEnterpriseConnection()); diff --git a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts index 1188f32c516..701d6ec3a4f 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts +++ b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts @@ -21,6 +21,8 @@ import { useCallback, useMemo, useRef, useState } from 'react'; import type { ConnectionScope } from '../domain/connectionScope'; import { + defaultConnectionDomains, + domainsClaimedByOtherConnections, isEnterpriseConnectionConfigured, type OrganizationEnterpriseConnection, organizationEnterpriseConnection as buildOrganizationEnterpriseConnection, @@ -43,9 +45,9 @@ import { type RefreshTestRunsOptions, useEnterpriseConnectionTestRuns } from './ */ export interface EnterpriseConnectionMutations { /** - * Creates a new enterprise connection for the active organization. The - * verified organization domains are sourced from the hook itself, so callers - * never thread them through. + * Creates a new enterprise connection for the active organization with the + * domains held in [UseOrganizationEnterpriseConnectionResult.connectionDomains], + * so callers never thread them through. */ createConnection: (provider: ProviderType) => Promise; /** Replaces the connection `id` with a fresh one for `provider`. */ @@ -89,6 +91,16 @@ export interface UseOrganizationEnterpriseConnectionResult { selectConnection: (scope: ConnectionScope) => void; /** The scoped connection, `undefined` while the scope is `new`. */ enterpriseConnection: EnterpriseConnectionResource | undefined; + /** + * The domains the scoped connection authenticates. An existing connection + * reads them off the resource; a `new` scope keeps a draft until the create, + * seeded with every verified organization domain no other connection claims. + */ + connectionDomains: string[]; + /** Replaces [connectionDomains]: an update for an existing connection, a draft edit for a `new` scope. */ + setConnectionDomains: (domains: string[]) => Promise; + /** Domains other connections of the organization already authenticate, keyed to that connection's name. */ + claimedDomains: Map; /** The domain entity the wizard makes every flow decision from. */ organizationEnterpriseConnection: OrganizationEnterpriseConnection; enterpriseConnectionMutations: EnterpriseConnectionMutations; @@ -142,11 +154,20 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise const enterpriseConnections = useMemo(() => sortEnterpriseConnections(sourceConnections ?? []), [sourceConnections]); // `null` resolves to the first connection so the standalone host, which has no list UI, still edits a deterministic one. - const [scope, setScope] = useState(null); + const [requestedScope, setRequestedScope] = useState(null); + // The `new` scope's domains until the create lands; `null` means the admin has not touched the default yet. + const [draftDomains, setDraftDomains] = useState(null); + + const setScope = useCallback((next: ConnectionScope | null) => { + setRequestedScope(next); + setDraftDomains(null); + }, []); const connectionScope = useMemo( - () => scope ?? (enterpriseConnections[0] ? { kind: 'existing', id: enterpriseConnections[0].id } : { kind: 'new' }), - [scope, enterpriseConnections], + () => + requestedScope ?? + (enterpriseConnections[0] ? { kind: 'existing', id: enterpriseConnections[0].id } : { kind: 'new' }), + [requestedScope, enterpriseConnections], ); const enterpriseConnection = @@ -154,7 +175,7 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise ? enterpriseConnections.find(connection => connection.id === connectionScope.id) : undefined; - const selectConnection = useCallback((next: ConnectionScope) => setScope(next), []); + const selectConnection = useCallback((next: ConnectionScope) => setScope(next), [setScope]); // Whether the scoped connection already existed the first time the source // query settled. Captured during render (not in an effect) the first time the @@ -203,22 +224,34 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise const { session } = useSession(); const { organization } = useOrganization(); + const claimedDomains = useMemo( + () => domainsClaimedByOtherConnections(enterpriseConnections, enterpriseConnection?.id), + [enterpriseConnections, enterpriseConnection], + ); + + // A domain verified from this wizard joins the scoped connection on its own; + // one another connection claims stays out, since FAPI would reject it. const handleDomainOwnershipVerified = useCallback( async (verifiedDomains: OrganizationDomainResource[]) => { - if (!enterpriseConnection) { + const current = enterpriseConnection ? (enterpriseConnection.domains ?? []) : draftDomains; + if (current === null) { return; } - const verifiedDomainNames = verifiedDomains.map(domain => domain.name); - const domains = Array.from(new Set([...(enterpriseConnection.domains ?? []), ...verifiedDomainNames])); - const hasNewDomains = domains.length !== (enterpriseConnection.domains?.length ?? 0); - if (!hasNewDomains) { + const domains = Array.from( + new Set([...current, ...verifiedDomains.map(domain => domain.name).filter(name => !claimedDomains.has(name))]), + ); + if (domains.length === current.length) { return; } - await updateEnterpriseConnection(enterpriseConnection.id, { domains }); + if (enterpriseConnection) { + await updateEnterpriseConnection(enterpriseConnection.id, { domains }); + } else { + setDraftDomains(domains); + } }, - [enterpriseConnection, updateEnterpriseConnection], + [enterpriseConnection, draftDomains, claimedDomains, updateEnterpriseConnection], ); const { @@ -233,6 +266,25 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise onOwnershipVerified: handleDomainOwnershipVerified, }); + const connectionDomains = useMemo( + () => + enterpriseConnection + ? (enterpriseConnection.domains ?? []) + : (draftDomains ?? defaultConnectionDomains(organizationDomains, claimedDomains)), + [enterpriseConnection, draftDomains, organizationDomains, claimedDomains], + ); + + const setConnectionDomains = useCallback( + async (domains: string[]) => { + if (enterpriseConnection) { + await updateEnterpriseConnection(enterpriseConnection.id, { domains }); + } else { + setDraftDomains(domains); + } + }, + [enterpriseConnection, updateEnterpriseConnection], + ); + const organizationDomainMutations = useMemo( () => ({ createDomain, @@ -245,10 +297,7 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise const enterpriseConnectionMutations = useMemo(() => { const createConnection: EnterpriseConnectionMutations['createConnection'] = async provider => { - const created = await createEnterpriseConnection({ - provider, - domains: organizationDomains?.map(domain => domain.name), - }); + const created = await createEnterpriseConnection({ provider, domains: connectionDomains }); if (created) { setScope({ kind: 'existing', id: created.id }); @@ -264,10 +313,7 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise const replaced = enterpriseConnections.find(connection => connection.id === id); await deleteEnterpriseConnection(id); - const created = await createEnterpriseConnection({ - provider, - domains: replaced?.domains ?? organizationDomains?.map(domain => domain.name), - }); + const created = await createEnterpriseConnection({ provider, domains: replaced?.domains ?? connectionDomains }); if (created) { setScope({ kind: 'existing', id: created.id }); @@ -315,9 +361,10 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise }; }, [ organization, - organizationDomains, + connectionDomains, enterpriseConnections, connectionScope, + setScope, createEnterpriseConnection, updateEnterpriseConnection, deleteEnterpriseConnection, @@ -373,6 +420,9 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise connectionScope, selectConnection, enterpriseConnection, + connectionDomains, + setConnectionDomains, + claimedDomains, organizationEnterpriseConnection, enterpriseConnectionMutations, testRuns, diff --git a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnectionStatus.ts b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnectionStatus.ts index e93dfc37647..7479cdb0f20 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnectionStatus.ts +++ b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnectionStatus.ts @@ -7,18 +7,26 @@ import { organizationEnterpriseConnection, } from '../domain/organizationEnterpriseConnection'; +type StatusOptions = { + /** Lists pass false to skip the per-connection test-runs request; a configured, inactive connection then reads `inactive`. */ + probe?: boolean; +}; + /** Same probe and query key as the umbrella hook, so react-query dedupes it for the scoped connection. */ export const useOrganizationEnterpriseConnectionStatus = ( connection: EnterpriseConnectionResource, + { probe = true }: StatusOptions = {}, ): OrganizationEnterpriseConnection => { + const isConfigured = isEnterpriseConnectionConfigured(connection); + const { data: successfulTestRuns } = __internal_useOrganizationEnterpriseConnectionTestRuns({ enterpriseConnectionId: connection.id, params: { initialPage: 1, pageSize: 1, status: ['success'] }, - enabled: isEnterpriseConnectionConfigured(connection) && !connection.active, + enabled: probe && isConfigured && !connection.active, }); return organizationEnterpriseConnection({ connection, - hasSuccessfulTestRun: (successfulTestRuns?.length ?? 0) > 0, + hasSuccessfulTestRun: probe ? (successfulTestRuns?.length ?? 0) > 0 : isConfigured, }); }; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx index ded9cbeafa4..b42c6be20e3 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx @@ -24,6 +24,7 @@ import { useConfigureSSO } from '../../../ConfigureSSOContext'; import { Step } from '../../../elements/Step'; import { useWizard, Wizard, type WizardStepConfig } from '../../../elements/Wizard'; import { InnerStepCounter } from '../../../elements/Wizard/InnerStepCounter'; +import { ActiveConnectionAlert } from '../shared/ActiveConnectionAlert'; import { IdentityProviderConfigurationModes, type SamlIdpConfigurationMode, @@ -493,6 +494,7 @@ const SamlCustomIdentityProviderMetadataStep = (): JSX.Element => { }} /> + diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx index dff36e9feaf..901884d9238 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx @@ -12,6 +12,7 @@ import { useConfigureSSO } from '../../../ConfigureSSOContext'; import { Step } from '../../../elements/Step'; import { useWizard, Wizard, type WizardStepConfig } from '../../../elements/Wizard'; import { InnerStepCounter } from '../../../elements/Wizard/InnerStepCounter'; +import { ActiveConnectionAlert } from '../shared/ActiveConnectionAlert'; import { IdentityProviderConfigurationModes, type SamlIdpConfigurationMode, @@ -339,6 +340,7 @@ const SamlGoogleIdentityProviderMetadataStep = (): JSX.Element => { }} /> + diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx index c7c937028dd..a4e1bc94983 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx @@ -33,6 +33,7 @@ import { useConfigureSSO } from '../../../ConfigureSSOContext'; import { Step } from '../../../elements/Step'; import { useWizard, Wizard, type WizardStepConfig } from '../../../elements/Wizard'; import { InnerStepCounter } from '../../../elements/Wizard/InnerStepCounter'; +import { ActiveConnectionAlert } from '../shared/ActiveConnectionAlert'; import { IdentityProviderConfigurationModes, type SamlIdpConfigurationMode, @@ -817,6 +818,7 @@ const SamlMicrosoftIdentityProviderMetadataStep = (): JSX.Element => { }} /> + diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx index ac4e0739ee4..39ca8214704 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx @@ -25,6 +25,7 @@ import { useConfigureSSO } from '../../../ConfigureSSOContext'; import { Step } from '../../../elements/Step'; import { useWizard, Wizard, type WizardStepConfig } from '../../../elements/Wizard'; import { InnerStepCounter } from '../../../elements/Wizard/InnerStepCounter'; +import { ActiveConnectionAlert } from '../shared/ActiveConnectionAlert'; import { IdentityProviderConfigurationModes, type SamlIdpConfigurationMode, @@ -674,6 +675,7 @@ const SamlOktaIdentityProviderMetadataStep = (): JSX.Element => { }} /> + diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx index 14fad7e2586..0237bc7df53 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/shared/IdentityProviderConfigurationForm.tsx @@ -21,7 +21,6 @@ import { ArrowUpTray, Close } from '@/icons'; import type { FormControlState } from '@/ui/utils/useFormControl'; import { handleError } from '@/utils/errorHandler'; -import { ActiveConnectionAlert } from '../../shared/ActiveConnectionAlert'; import type { SamlIdpConfigurationMode } from '../../shared/IdentityProviderConfigurationModes'; type CardState = ReturnType; @@ -71,7 +70,7 @@ export type IdentityProviderConfigurationFormProps = | { mode: 'metadataFile'; form: MetadataFileForm; labels: MetadataFileLabels } | { mode: 'manual'; form: ManualConfigurationForm; labels: ManualConfigurationLabels }; -const ConfigurationPanel = (config: IdentityProviderConfigurationFormProps): JSX.Element => { +export const IdentityProviderConfigurationForm = (config: IdentityProviderConfigurationFormProps): JSX.Element => { switch (config.mode) { case 'metadataUrl': return ( @@ -97,15 +96,6 @@ const ConfigurationPanel = (config: IdentityProviderConfigurationFormProps): JSX } }; -export const IdentityProviderConfigurationForm = (config: IdentityProviderConfigurationFormProps): JSX.Element => { - return ( - <> - - - - ); -}; - type MetadataUrlPanelProps = { form: MetadataUrlForm; labels: MetadataUrlLabels; diff --git a/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx index 8c449089080..f1a78e479cb 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx @@ -8,6 +8,7 @@ import { Badge, Box, Button, + CheckboxInput, Col, descriptors, Flex, @@ -31,7 +32,7 @@ import { useFormControl } from '@/ui/utils/useFormControl'; import { getFieldError, getGlobalError } from '@/utils/errorHandler'; import { useConfigureSSO } from '../ConfigureSSOContext'; -import { areAllOrganizationDomainsVerified } from '../domain/organizationEnterpriseConnection'; +import { areConnectionDomainsReady, isOrganizationDomainVerified } from '../domain/organizationEnterpriseConnection'; import { Step } from '../elements/Step'; import { useWizard } from '../elements/Wizard/WizardContext'; import { RemoveDomainDialog } from '../RemoveDomainDialog'; @@ -40,17 +41,20 @@ export const OrganizationDomainsStep = (): JSX.Element => { const { t } = useLocalizations(); const { enterpriseConnection, + connectionDomains, + setConnectionDomains, + claimedDomains, organizationDomains, organizationEnterpriseConnection, contentRef, organizationDomainMutations: { createDomain, revalidate, prepareOwnershipVerification }, - enterpriseConnectionMutations: { updateConnection }, } = useConfigureSSO(); const { goPrev, goNext, isFirstStep, isLastStep } = useWizard(); const card = useCardState(); const clerk = useClerk(); const { organization } = useOrganization(); const [domainToRemove, setDomainToRemove] = useState(null); + const [isUpdatingDomains, setIsUpdatingDomains] = useState(false); const hasRecordedTelemetryEvent = useRef(false); useEffect(() => { @@ -92,24 +96,43 @@ export const OrganizationDomainsStep = (): JSX.Element => { } }; + const handleToggleDomain = async (domain: OrganizationDomainResource, checked: boolean) => { + if (isUpdatingDomains) { + return; + } + + card.setError(undefined); + setIsUpdatingDomains(true); + + const domains = checked + ? [...connectionDomains, domain.name] + : connectionDomains.filter(name => name !== domain.name); + + try { + await setConnectionDomains(domains); + } catch (err: any) { + const apiError = getFieldError(err) ?? getGlobalError(err); + card.setError(apiError); + } finally { + setIsUpdatingDomains(false); + } + }; + const handleRemoveDomain = async (domain: OrganizationDomainResource) => { - if (enterpriseConnection) { - const domains = enterpriseConnection.domains.filter(name => name !== domain.name); - await updateConnection(enterpriseConnection.id, { domains }); + if (connectionDomains.includes(domain.name)) { + await setConnectionDomains(connectionDomains.filter(name => name !== domain.name)); } await domain.delete(); await revalidate(); }; - const hasAllDomainsVerified = areAllOrganizationDomainsVerified(organizationDomains); + const domainsReady = areConnectionDomainsReady(connectionDomains, organizationDomains, claimedDomains); - // A connection needs at least one verified domain to point at, so the last - // remaining verified domain cannot be removed while a connection exists - const verifiedDomainCount = - organizationDomains?.filter(domain => domain.ownershipVerification?.status === 'verified').length ?? 0; - const lockLastVerifiedDomain = Boolean(enterpriseConnection); - const lastVerifiedDomainTooltip = enterpriseConnection?.active + // An existing connection must keep at least one domain, so its last one can + // be neither deselected nor removed. + const lockLastConnectionDomain = Boolean(enterpriseConnection) && connectionDomains.length === 1; + const lastConnectionDomainTooltip = enterpriseConnection?.active ? localizationKeys('configureSSO.organizationDomainsStep.domainCard.removeButtonTooltip__lastVerifiedDomainActive') : localizationKeys('configureSSO.organizationDomainsStep.domainCard.removeButtonTooltip__lastVerifiedDomain'); @@ -161,17 +184,20 @@ export const OrganizationDomainsStep = (): JSX.Element => { })} > {organizationDomains.map(domain => { - const isVerified = domain.ownershipVerification?.status === 'verified'; - const isLastVerifiedDomain = isVerified && verifiedDomainCount === 1; - const isRemoveDisabled = lockLastVerifiedDomain && isLastVerifiedDomain; + const isSelected = connectionDomains.includes(domain.name); + const isLocked = isSelected && lockLastConnectionDomain; return ( void handleToggleDomain(domain, checked)} + isToggleDisabled={isLocked || isUpdatingDomains} onRemove={() => setDomainToRemove(domain)} onPrepareOwnershipVerification={() => handlePrepareOwnershipVerification(domain)} - isRemoveDisabled={isRemoveDisabled} - removeDisabledTooltip={lastVerifiedDomainTooltip} + isRemoveDisabled={isLocked} + removeDisabledTooltip={lastConnectionDomainTooltip} /> ); })} @@ -187,7 +213,7 @@ export const OrganizationDomainsStep = (): JSX.Element => { /> goNext()} - isDisabled={isLastStep || !hasAllDomainsVerified} + isDisabled={isLastStep || !domainsReady} /> @@ -364,25 +390,39 @@ const DomainSuggestion = ({ onSubmit }: { onSubmit: (domain: string) => Promise< const DomainCard = ({ domain, + isSelected, + claimedBy, + onToggle, + isToggleDisabled = false, onRemove, onPrepareOwnershipVerification, isRemoveDisabled = false, removeDisabledTooltip, }: { domain: OrganizationDomainResource; + /** Whether the scoped connection authenticates this domain. */ + isSelected: boolean; + /** The name of the other connection that already authenticates this domain. */ + claimedBy: string | undefined; + onToggle: (checked: boolean) => void; + isToggleDisabled?: boolean; onRemove: () => void; onPrepareOwnershipVerification: () => Promise; isRemoveDisabled?: boolean; removeDisabledTooltip?: ReturnType; }): JSX.Element | null => { + const { t } = useLocalizations(); + if (!domain.name) { return null; } const ownershipVerification = domain.ownershipVerification; - const isVerified = ownershipVerification?.status === 'verified'; + const isVerified = isOrganizationDomainVerified(domain); const isExpired = ownershipVerification?.status === 'expired'; const cardId = ownershipVerification?.status ?? 'unverified'; + // Only a verified domain no other connection claims can join this one. + const isSelectable = isVerified && (!claimedBy || isSelected); const removeButton = ( ); }; -const SsoDescription = (): JSX.Element => { +const SsoDescription = (): JSX.Element => ( + +); + +const SsoInfoTooltip = (): JSX.Element => { const roleName = useEnrollmentRoleName(); const { t } = useLocalizations(); return ( - - + + + + - - - - - - - - + ); }; diff --git a/packages/ui/src/components/OrganizationProfile/__tests__/EnterpriseConnectionPage.test.tsx b/packages/ui/src/components/OrganizationProfile/__tests__/EnterpriseConnectionPage.test.tsx new file mode 100644 index 00000000000..1bf0eecdb8a --- /dev/null +++ b/packages/ui/src/components/OrganizationProfile/__tests__/EnterpriseConnectionPage.test.tsx @@ -0,0 +1,530 @@ +import { ClerkAPIResponseError } from '@clerk/shared/error'; +import { fireEvent, within } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { bindCreateFixtures } from '@/test/create-fixtures'; +import { render, screen, waitFor } from '@/test/utils'; + +import type { EnterpriseConnectionMutations } from '../../ConfigureSSO/hooks/useOrganizationEnterpriseConnection'; +import { EnterpriseConnectionPage } from '../EnterpriseConnectionPage'; + +const { createFixtures } = bindCreateFixtures('OrganizationProfile'); + +const withPageFixtures = (f: Parameters[0]>[0]) => { + f.withEnterpriseSso({ selfServeSSO: true }); + f.withEmailAddress(); + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [{ name: 'Org1', permissions: ['org:sys_entconns:manage'] }], + }); +}; + +const samlConnection = (overrides: Record = {}) => + ({ + id: 'ent_1', + name: 'clerk.com', + provider: 'saml_okta', + active: false, + organizationId: 'Org1', + domains: ['clerk.com'], + logoPublicUrl: null, + syncUserAttributes: false, + disableAdditionalIdentifications: false, + createdAt: new Date('2024-01-01T00:00:00Z'), + samlConnection: { + idpSsoUrl: 'https://idp.example.com/sso', + idpEntityId: 'https://idp.example.com/entity', + idpCertificate: 'CERT', + idpCertificateExpiresAt: 0, + idpMetadataUrl: '', + acsUrl: 'https://accounts.clerk.com/v1/acs', + spEntityId: 'https://accounts.clerk.com/saml/ent_1', + spMetadataUrl: 'https://accounts.clerk.com/saml/ent_1/metadata', + allowSubdomains: false, + allowIdpInitiated: false, + forceAuthn: false, + }, + ...overrides, + }) as any; + +const samlConnectionWith = (samlOverrides: Record) => + samlConnection({ samlConnection: { ...samlConnection().samlConnection, ...samlOverrides } }); + +const oidcConnection = (overrides: Record = {}) => + ({ + id: 'ent_2', + name: 'oidc.com', + provider: 'oidc_custom', + active: false, + organizationId: 'Org1', + domains: ['oidc.com'], + logoPublicUrl: null, + syncUserAttributes: false, + disableAdditionalIdentifications: false, + createdAt: new Date('2024-01-01T00:00:00Z'), + samlConnection: null, + oauthConfig: { + clientId: 'client-abc', + redirectUri: 'https://accounts.clerk.com/v1/oauth_callback', + discoveryUrl: 'https://idp.example.com/.well-known/openid-configuration', + }, + ...overrides, + }) as any; + +/** Mirrors how the umbrella hook maps the mutation surface onto the organization resource. */ +const mutationsFor = (fixtures: any): EnterpriseConnectionMutations => ({ + createConnection: vi.fn(), + changeProvider: vi.fn(), + updateConnection: (id, params) => fixtures.clerk.organization.updateEnterpriseConnection(id, params), + setConnectionActive: (id, active) => fixtures.clerk.organization.updateEnterpriseConnection(id, { active }), + deleteConnection: id => fixtures.clerk.organization.deleteEnterpriseConnection(id), + createTestRun: vi.fn(), +}); + +const renderPage = ( + wrapper: React.ComponentType<{ children?: React.ReactNode }>, + fixtures: any, + connection: any, + onBack = vi.fn(), + onOpenWizard = vi.fn(), +) => { + const page = (next: any) => ( + + ); + + const result = render(page(connection), { wrapper }); + + return { + onBack, + onOpenWizard, + ...result, + rerenderWith: (next: any) => result.rerender(page(next)), + }; +}; + +const withNoTestRuns = (fixtures: any) => { + fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ + data: [], + total_count: 0, + } as any); +}; + +describe('EnterpriseConnectionPage', () => { + describe('sections', () => { + it('renders every section for a SAML connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection()); + + expect(await screen.findByRole('heading', { name: 'clerk.com' })).toBeInTheDocument(); + + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByText('Domains')).toBeInTheDocument(); + expect(screen.getByText('Service provider')).toBeInTheDocument(); + expect(screen.getByText('Identity provider')).toBeInTheDocument(); + expect(screen.getByText('Settings')).toBeInTheDocument(); + expect(screen.getByText('Danger zone')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Remove connection' })).toBeInTheDocument(); + + expect(screen.getByDisplayValue('https://accounts.clerk.com/v1/acs')).toBeInTheDocument(); + expect(screen.getByDisplayValue('https://accounts.clerk.com/saml/ent_1')).toBeInTheDocument(); + + expect(screen.getAllByRole('checkbox')).toHaveLength(5); + }); + + it('labels the name and the domains with the section titles alone', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection()); + + expect(await screen.findByText('Name')).toBeInTheDocument(); + expect(screen.getAllByText('Name')).toHaveLength(1); + expect(screen.getAllByText('Domains')).toHaveLength(1); + expect(screen.queryByText('General')).not.toBeInTheDocument(); + expect(screen.queryByText('Provider')).not.toBeInTheDocument(); + expect(screen.queryByText('Created')).not.toBeInTheDocument(); + + expect(screen.getAllByText('Okta Workforce')).toHaveLength(1); + }); + + it('omits the Domains section when the connection has no domains', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection({ domains: [] })); + + expect(await screen.findByText('Name')).toBeInTheDocument(); + expect(screen.queryByText('Domains')).not.toBeInTheDocument(); + }); + + it('renders the manual SAML endpoints as read-only captioned values', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection()); + + const signOnUrl = await screen.findByText('Sign on URL'); + expect(signOnUrl.parentElement).toHaveTextContent('https://idp.example.com/sso'); + + const issuer = screen.getByText('Issuer'); + expect(issuer.parentElement).toHaveTextContent('https://idp.example.com/entity'); + + expect(screen.queryByDisplayValue('https://idp.example.com/sso')).not.toBeInTheDocument(); + expect(screen.queryByText('Certificate expires')).not.toBeInTheDocument(); + }); + + it('renders a single metadata URL row when the connection was configured from metadata', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage( + wrapper, + fixtures, + samlConnectionWith({ idpMetadataUrl: 'https://idp.example.com/metadata', idpSsoUrl: '', idpEntityId: '' }), + ); + + expect(await screen.findByText('https://idp.example.com/metadata')).toBeInTheDocument(); + expect(screen.queryByText('Sign on URL')).not.toBeInTheDocument(); + expect(screen.queryByText('Issuer')).not.toBeInTheDocument(); + }); + + it('renders the certificate expiry as a plain row when the certificate has one', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage( + wrapper, + fixtures, + samlConnectionWith({ idpCertificateExpiresAt: Date.parse('2030-05-01T00:00:00Z') }), + ); + + expect(await screen.findByText('Certificate expires')).toBeInTheDocument(); + }); + + it('renders the OIDC variant of the service provider section and only the shared settings', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, oidcConnection()); + + expect(await screen.findByRole('heading', { name: 'oidc.com' })).toBeInTheDocument(); + + expect(screen.getByDisplayValue('https://accounts.clerk.com/v1/oauth_callback')).toBeInTheDocument(); + expect(screen.queryByDisplayValue('https://accounts.clerk.com/v1/acs')).not.toBeInTheDocument(); + + expect(screen.getByText('Client ID')).toBeInTheDocument(); + expect(screen.getByText('client-abc')).toBeInTheDocument(); + expect(screen.getByText('Discovery endpoint')).toBeInTheDocument(); + expect(screen.queryByText('Authorization URL')).not.toBeInTheDocument(); + + expect(screen.getAllByRole('checkbox')).toHaveLength(2); + }); + }); + + describe('header actions', () => { + it('offers no header action for an active connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection({ active: true })); + + expect(await screen.findByRole('heading', { name: 'clerk.com' })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Activate' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Deactivate' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Continue setup' })).not.toBeInTheDocument(); + }); + + it('activates an inactive connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ + data: [{ id: 'run_1', status: 'success' }], + total_count: 1, + } as any); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue({ active: true } as any); + + const { userEvent } = renderPage(wrapper, fixtures, samlConnection({ active: false })); + + await userEvent.click(await screen.findByRole('button', { name: 'Activate' })); + + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { active: true }); + }); + + it('offers Continue setup for a connection that is still mid-setup', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + const { userEvent, onOpenWizard } = renderPage( + wrapper, + fixtures, + samlConnection({ samlConnection: null, oauthConfig: null }), + ); + + await userEvent.click(await screen.findByRole('button', { name: 'Continue setup' })); + + expect(onOpenWizard).toHaveBeenCalled(); + expect(screen.queryByRole('button', { name: 'Open setup wizard' })).not.toBeInTheDocument(); + }); + }); + + describe('danger zone', () => { + it('deactivates an active connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue({ active: false } as any); + + const { userEvent } = renderPage(wrapper, fixtures, samlConnection({ active: true })); + + await userEvent.click(await screen.findByRole('button', { name: 'Deactivate connection' })); + + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { active: false }); + }); + + it('offers no Deactivate connection button for an inactive connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + renderPage(wrapper, fixtures, samlConnection({ active: false })); + + expect(await screen.findByRole('button', { name: 'Remove connection' })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Deactivate connection' })).not.toBeInTheDocument(); + }); + + it('removes the connection from the danger zone and returns to the overview', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.deleteEnterpriseConnection.mockResolvedValue({} as any); + + const { userEvent, onBack } = renderPage(wrapper, fixtures, samlConnection({ active: true })); + + expect(await screen.findAllByRole('button', { name: 'Remove connection' })).toHaveLength(1); + await userEvent.click(screen.getByRole('button', { name: 'Remove connection' })); + + const dialog = within(await screen.findByRole('dialog')); + expect(dialog.getByRole('heading', { name: 'Remove SSO connection' })).toBeInTheDocument(); + expect(dialog.getByText(/Are you sure you want to remove the connection "clerk.com"\?/i)).toBeInTheDocument(); + + await userEvent.type(dialog.getByLabelText(/below to continue/i), 'Org1'); + await waitFor(() => expect(dialog.getByRole('button', { name: 'Remove connection' })).toBeEnabled()); + await userEvent.click(dialog.getByRole('button', { name: 'Remove connection' })); + + await waitFor(() => { + expect(fixtures.clerk.organization?.deleteEnterpriseConnection).toHaveBeenCalledWith('ent_1'); + }); + expect(onBack).toHaveBeenCalled(); + }); + }); + + describe('editing', () => { + it('renames the connection from the Name section', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue(samlConnection({ name: 'Renamed' })); + + const { userEvent, container } = renderPage(wrapper, fixtures, samlConnection()); + + await waitFor(() => expect(screen.getAllByRole('button', { name: 'Edit' })).toHaveLength(2)); + await userEvent.click(screen.getAllByRole('button', { name: 'Edit' })[0]); + + const form = container.querySelector('.cl-actionCard') as HTMLElement; + expect(within(form).getByRole('heading', { name: 'Rename connection' })).toBeInTheDocument(); + + const nameInput = within(form).getByLabelText('Name'); + await userEvent.clear(nameInput); + await userEvent.type(nameInput, 'Renamed'); + await userEvent.click(within(form).getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { + name: 'Renamed', + }); + }); + }); + + it('saves the SAML identity provider configuration from the Edit form', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue(samlConnection()); + + const { userEvent, container } = renderPage(wrapper, fixtures, samlConnection()); + + await waitFor(() => expect(screen.getAllByRole('button', { name: 'Edit' })).toHaveLength(2)); + await userEvent.click(screen.getAllByRole('button', { name: 'Edit' })[1]); + + const form = container.querySelector('.cl-actionCard') as HTMLElement; + expect(within(form).getByRole('heading', { name: 'Edit identity provider' })).toBeInTheDocument(); + + await userEvent.click(within(form).getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { + saml: { + idpSsoUrl: 'https://idp.example.com/sso', + idpEntityId: 'https://idp.example.com/entity', + }, + }); + }); + + // auto-animate keeps a clone of the closing card in the DOM until its exit animation ends, which + // outlasts the default waitFor budget on CI, so the restored Edit trigger is the close signal. + await waitFor(() => expect(screen.getAllByRole('button', { name: 'Edit' })).toHaveLength(2), { + timeout: 10_000, + }); + }, 15_000); + + it('opens the SAML Edit form on the metadata URL mode for a metadata-configured connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + const { userEvent, container } = renderPage( + wrapper, + fixtures, + samlConnectionWith({ idpMetadataUrl: 'https://idp.example.com/metadata', idpSsoUrl: '', idpEntityId: '' }), + ); + + await waitFor(() => expect(screen.getAllByRole('button', { name: 'Edit' })).toHaveLength(2)); + await userEvent.click(screen.getAllByRole('button', { name: 'Edit' })[1]); + + const form = container.querySelector('.cl-actionCard') as HTMLElement; + expect(within(form).getByDisplayValue('https://idp.example.com/metadata')).toBeInTheDocument(); + expect(within(form).queryByLabelText('Sign on URL')).not.toBeInTheDocument(); + }); + + it('opens the OIDC Edit form on the discovery mode for a discovery-configured connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + const { userEvent, container } = renderPage( + wrapper, + fixtures, + oidcConnection({ + oauthConfig: { + ...oidcConnection().oauthConfig, + authUrl: 'https://idp.example.com/authorize', + tokenUrl: 'https://idp.example.com/token', + }, + }), + ); + + await waitFor(() => expect(screen.getAllByRole('button', { name: 'Edit' })).toHaveLength(2)); + await userEvent.click(screen.getAllByRole('button', { name: 'Edit' })[1]); + + const form = container.querySelector('.cl-actionCard') as HTMLElement; + expect( + within(form).getByDisplayValue('https://idp.example.com/.well-known/openid-configuration'), + ).toBeInTheDocument(); + expect(within(form).queryByLabelText('Authorization URL')).not.toBeInTheDocument(); + }); + }); + + describe('settings', () => { + it('reports a failed save inside the Settings section', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.updateEnterpriseConnection.mockRejectedValue( + new ClerkAPIResponseError('Error', { + data: [ + { code: 'form_param_format_invalid', message: 'Settings rejected', long_message: 'Settings rejected' }, + ], + status: 422, + }), + ); + + const { userEvent, container } = renderPage(wrapper, fixtures, samlConnection()); + + await userEvent.click(await screen.findByRole('checkbox', { name: /Sync user attributes/ })); + await userEvent.click(screen.getByRole('button', { name: 'Save' })); + + const alert = await screen.findByText('Settings rejected'); + expect(container.querySelector('.cl-profileSection__ssoConnectionSettings')).toContainElement(alert); + }); + + it('submits once when Save is clicked twice in a row', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + let resolveUpdate: (value: unknown) => void = () => {}; + fixtures.clerk.organization?.updateEnterpriseConnection.mockReturnValue( + new Promise(resolve => { + resolveUpdate = resolve; + }) as any, + ); + + const { userEvent } = renderPage(wrapper, fixtures, samlConnection()); + + await userEvent.click(await screen.findByRole('checkbox', { name: /Sync user attributes/ })); + + const save = screen.getByRole('button', { name: 'Save' }); + await userEvent.click(save); + // The in-flight Save is disabled, so the second click has to bypass the pointer-events guard + // to reach the submit handler at all. + fireEvent.click(save); + + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledTimes(1); + + resolveUpdate(samlConnection({ syncUserAttributes: true })); + }); + + it('reseats the checkboxes when the connection comes back changed', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + const { rerenderWith } = renderPage(wrapper, fixtures, samlConnection()); + + expect(await screen.findByRole('checkbox', { name: /Sync user attributes/ })).not.toBeChecked(); + + rerenderWith(samlConnection({ syncUserAttributes: true })); + + await waitFor(() => expect(screen.getByRole('checkbox', { name: /Sync user attributes/ })).toBeChecked()); + expect(screen.getByRole('button', { name: 'Save' })).toBeDisabled(); + }); + + it('keeps Save disabled until a setting differs from the connection', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + + const { userEvent } = renderPage(wrapper, fixtures, samlConnection()); + + expect(await screen.findByRole('button', { name: 'Save' })).toBeDisabled(); + + await userEvent.click(screen.getByRole('checkbox', { name: /Sync user attributes/ })); + expect(screen.getByRole('button', { name: 'Save' })).toBeEnabled(); + + await userEvent.click(screen.getByRole('button', { name: 'Cancel' })); + expect(screen.getByRole('checkbox', { name: /Sync user attributes/ })).not.toBeChecked(); + expect(screen.getByRole('button', { name: 'Save' })).toBeDisabled(); + }); + + it('saves only the changed settings in one call', async () => { + const { wrapper, fixtures } = await createFixtures(withPageFixtures); + withNoTestRuns(fixtures); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue( + samlConnection({ syncUserAttributes: true }), + ); + + const { userEvent } = renderPage(wrapper, fixtures, samlConnection()); + + await userEvent.click(await screen.findByRole('checkbox', { name: /Sync user attributes/ })); + await userEvent.click(screen.getByRole('checkbox', { name: /Allow subdomains/ })); + await userEvent.click(screen.getByRole('button', { name: 'Save' })); + + await waitFor(() => { + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledTimes(1); + }); + expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { + syncUserAttributes: true, + saml: { allowSubdomains: true }, + }); + }); + }); +}); diff --git a/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPage.test.tsx b/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPage.test.tsx index 6af6137395a..519bbbf11f2 100644 --- a/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPage.test.tsx +++ b/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPage.test.tsx @@ -65,12 +65,12 @@ describe('OrganizationSecurityPage', () => { expect(screen.getByText(DESCRIPTION_LINE_1)).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Start configuration' })).toBeInTheDocument(); - expect(screen.queryByRole('switch')).not.toBeInTheDocument(); + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); expect(screen.queryByText(/select your identity provider/i)).not.toBeInTheDocument(); }); - it('renders the in-progress state with a Continue configuration action', async () => { + it('renders the in-progress state as a clickable row', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); // A connection without SAML configuration is mid-setup. @@ -82,19 +82,18 @@ describe('OrganizationSecurityPage', () => { total_count: 0, } as any); - const { userEvent } = renderPage(wrapper); + renderPage(wrapper); expect(await screen.findByText('In Progress')).toBeInTheDocument(); expect(screen.getByText(DESCRIPTION_LINE_1)).toBeInTheDocument(); expect(screen.queryByText(/you have started a configuration/i)).not.toBeInTheDocument(); - expect(screen.queryByRole('switch')).not.toBeInTheDocument(); + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Continue configuration' })).not.toBeInTheDocument(); - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - expect(screen.getByRole('menuitem', { name: 'Continue configuration' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /clerk\.com/ })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); }); - it('renders the active state as a condensed overview with the domains and actions menu', async () => { + it('renders the active state as a condensed overview with the domains', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); @@ -108,12 +107,13 @@ describe('OrganizationSecurityPage', () => { expect(await screen.findByText('Active')).toBeInTheDocument(); expect(screen.getByText(DESCRIPTION_LINE_1)).toBeInTheDocument(); - expect(screen.queryByRole('switch')).not.toBeInTheDocument(); + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); expect(screen.queryByText(/^Domains:?$/)).not.toBeInTheDocument(); expect(screen.getAllByText('clerk.com').length).toBeGreaterThan(0); - expect(screen.getByRole('button', { name: /open menu/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /clerk\.com/ })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Start configuration' })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Continue configuration' })).not.toBeInTheDocument(); }); @@ -133,8 +133,8 @@ describe('OrganizationSecurityPage', () => { expect(await screen.findByText('Inactive')).toBeInTheDocument(); - expect(screen.queryByRole('switch')).not.toBeInTheDocument(); - expect(screen.getByRole('button', { name: /open menu/i })).toBeInTheDocument(); + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); for (const domain of ['github.com', 'gmail.com', 'maps.com', 'another.com']) { expect(screen.getByText(domain)).toBeInTheDocument(); @@ -221,7 +221,7 @@ describe('OrganizationSecurityPage', () => { expect(screen.queryByRole('button', { name: 'Start configuration' })).not.toBeInTheDocument(); }); - it('resumes the wizard at the reachable step when Continue configuration is clicked', async () => { + it("resumes the wizard at the reachable step from the connection page's Continue setup", async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); // A connection without SAML configuration is mid-setup (in_progress). @@ -236,10 +236,10 @@ describe('OrganizationSecurityPage', () => { const { userEvent } = renderPage(wrapper); - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Continue configuration' })); + await userEvent.click(await screen.findByRole('button', { name: /clerk\.com/ })); + await userEvent.click(await screen.findByRole('button', { name: 'Continue setup' })); - // Continue passes no forced step, so the wizard resumes at the furthest- + // The connection page forces no step, so the wizard resumes at the furthest- // reachable step for this connection (configure, since a provider connection // exists and the domain is verified) rather than the forced first step. // Resuming into `configure` (direction 0) falls through to its furthest- @@ -249,28 +249,6 @@ describe('OrganizationSecurityPage', () => { expect(screen.queryByRole('heading', { name: /select your identity provider/i })).not.toBeInTheDocument(); expect(screen.queryByRole('heading', { name: /add SSO domains/i })).not.toBeInTheDocument(); }); - - it('opens the wizard at the first step when Edit is selected from the actions menu', async () => { - const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [{ id: 'run_1', status: 'success' }], - total_count: 1, - } as any); - fixtures.clerk.organization?.getDomains.mockResolvedValue({ data: [verifiedDomain], total_count: 1 } as any); - - const { userEvent } = renderPage(wrapper); - - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Edit' })); - - // Edit forces the first step rather than the connection's furthest-reachable - // step (confirmation, for an active connection). - expect(await screen.findByRole('heading', { name: /add SSO domains/i })).toBeInTheDocument(); - expect(screen.queryByText(/configuration details/i)).not.toBeInTheDocument(); - expect(screen.queryByText(DESCRIPTION_LINE_1)).not.toBeInTheDocument(); - }); }); describe('wizard back control', () => { @@ -293,194 +271,92 @@ describe('OrganizationSecurityPage', () => { expect(await screen.findByRole('button', { name: 'Start configuration' })).toBeInTheDocument(); expect(screen.queryByRole('heading', { name: /add SSO domains/i })).not.toBeInTheDocument(); }); - }); - describe('actions menu', () => { - it('lists Edit, Deactivate, and Remove for an active connection', async () => { + it('returns to the connection page when the wizard was opened from it', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); + fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([ + configuredConnection({ samlConnection: null }), + ]); fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ data: [], total_count: 0, } as any); + fixtures.clerk.organization?.getDomains.mockResolvedValue({ data: [verifiedDomain], total_count: 1 } as any); const { userEvent } = renderPage(wrapper); - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); - - expect(screen.getByRole('menuitem', { name: 'Edit' })).toBeInTheDocument(); - expect(screen.getByRole('menuitem', { name: 'Deactivate' })).toBeInTheDocument(); - expect(screen.getByRole('menuitem', { name: 'Remove' })).toBeInTheDocument(); - expect(screen.queryByRole('menuitem', { name: 'Activate' })).not.toBeInTheDocument(); - expect(screen.queryByRole('menuitem', { name: 'Delete' })).not.toBeInTheDocument(); - }); - - it('lists Edit, Activate, and Remove for an inactive connection', async () => { - const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection()]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [{ id: 'run_1', status: 'success' }], - total_count: 1, - } as any); - - const { userEvent } = renderPage(wrapper); + await userEvent.click(await screen.findByRole('button', { name: /clerk\.com/ })); + await userEvent.click(await screen.findByRole('button', { name: 'Continue setup' })); + expect(await screen.findByRole('heading', { name: /configure okta workforce/i })).toBeInTheDocument(); - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); + await userEvent.click(await screen.findByRole('button', { name: 'Security' })); - expect(screen.getByRole('menuitem', { name: 'Edit' })).toBeInTheDocument(); - expect(screen.getByRole('menuitem', { name: 'Activate' })).toBeInTheDocument(); - expect(screen.getByRole('menuitem', { name: 'Remove' })).toBeInTheDocument(); - expect(screen.queryByRole('menuitem', { name: 'Deactivate' })).not.toBeInTheDocument(); + expect(await screen.findByText('Danger zone')).toBeInTheDocument(); + expect(screen.queryByText(DESCRIPTION_LINE_1)).not.toBeInTheDocument(); }); + }); - it('opens the type-to-confirm removal dialog with Remove-oriented copy from Remove', async () => { + describe('connection page', () => { + it('opens the connection page for the clicked row', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [], - total_count: 0, - } as any); - fixtures.clerk.organization?.deleteEnterpriseConnection.mockResolvedValue({} as any); - - const { userEvent } = renderPage(wrapper); - - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Remove' })); - - // The shared dialog renders the Remove copy here, not the wizard's Reset copy. - expect(await screen.findByRole('heading', { name: 'Remove SSO connection' })).toBeInTheDocument(); - expect(screen.queryByRole('heading', { name: 'Reset connection' })).not.toBeInTheDocument(); - expect(screen.getByText(/Are you sure you want to remove the connection "clerk.com"\?/i)).toBeInTheDocument(); - - // Type-to-confirm uses the organization name. - await userEvent.type(screen.getByLabelText(/below to continue/i), 'Org1'); - await waitFor(() => expect(screen.getByRole('button', { name: 'Remove connection' })).toBeEnabled()); - await userEvent.click(screen.getByRole('button', { name: 'Remove connection' })); - - await waitFor(() => { - expect(fixtures.clerk.organization?.deleteEnterpriseConnection).toHaveBeenCalledWith('ent_1'); - }); - }); - - it('deactivates directly from the menu, settling on the revalidated connection', async () => { - const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - - // The revalidation that follows the update returns the deactivated connection. - fixtures.clerk.organization?.getEnterpriseConnections - .mockResolvedValueOnce([configuredConnection({ active: true })]) - .mockResolvedValue([configuredConnection()]); fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ data: [{ id: 'run_1', status: 'success' }], total_count: 1, } as any); - fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue({ active: false } as any); const { userEvent } = renderPage(wrapper); - expect(await screen.findByText('Active')).toBeInTheDocument(); + await userEvent.click(await screen.findByRole('button', { name: /clerk\.com/ })); - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Deactivate' })); - - expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { - active: false, - }); - - // The badge follows the revalidated entity, not an optimistic flip. - await waitFor(() => expect(screen.getByText('Inactive')).toBeInTheDocument()); - expect(screen.queryByText('Active')).not.toBeInTheDocument(); + expect(await screen.findByRole('heading', { name: 'clerk.com' })).toBeInTheDocument(); + expect(screen.getAllByText('Okta Workforce').length).toBeGreaterThan(0); + expect(screen.getByText('Identity provider')).toBeInTheDocument(); + expect(screen.queryByText(DESCRIPTION_LINE_1)).not.toBeInTheDocument(); }); - it('activates directly from the menu, settling on the revalidated connection', async () => { + it('returns to the overview from the back control', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - fixtures.clerk.organization?.getEnterpriseConnections - .mockResolvedValueOnce([configuredConnection()]) - .mockResolvedValue([configuredConnection({ active: true })]); + fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ data: [{ id: 'run_1', status: 'success' }], total_count: 1, } as any); - fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue({ active: true } as any); const { userEvent } = renderPage(wrapper); - expect(await screen.findByText('Inactive')).toBeInTheDocument(); + await userEvent.click(await screen.findByRole('button', { name: /clerk\.com/ })); + await userEvent.click(await screen.findByRole('button', { name: 'Security' })); - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Activate' })); - - expect(fixtures.clerk.organization?.updateEnterpriseConnection).toHaveBeenCalledWith('ent_1', { - active: true, - }); - - await waitFor(() => expect(screen.getByText('Active')).toBeInTheDocument()); - expect(screen.queryByText('Inactive')).not.toBeInTheDocument(); + expect(await screen.findByText(DESCRIPTION_LINE_1)).toBeInTheDocument(); + expect(screen.queryByText('Identity provider')).not.toBeInTheDocument(); }); - it('disables the activation action while the update is in flight', async () => { + it('falls back to the overview when the open connection leaves the list', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); + // The revalidation that follows the settings write no longer returns the + // connection — the same state a removal in another tab leaves behind. + fixtures.clerk.organization?.getEnterpriseConnections + .mockResolvedValueOnce([configuredConnection({ active: true })]) + .mockResolvedValue([]); fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ data: [{ id: 'run_1', status: 'success' }], total_count: 1, } as any); - - let resolveUpdate!: (value: unknown) => void; - fixtures.clerk.organization?.updateEnterpriseConnection.mockImplementation( - () => new Promise(resolve => (resolveUpdate = resolve)) as any, - ); - - const { userEvent } = renderPage(wrapper); - - expect(await screen.findByText('Active')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Deactivate' })); - - // The update is still pending; the acting item is disabled when the menu is reopened. - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - await waitFor(() => expect(screen.getByRole('menuitem', { name: 'Deactivate' })).toBeDisabled()); - - resolveUpdate({ active: true }); - }); - - it('surfaces the error and keeps the entity state when the update fails', async () => { - const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - - fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([configuredConnection({ active: true })]); - fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ - data: [], - total_count: 0, - } as any); - fixtures.clerk.organization?.updateEnterpriseConnection.mockRejectedValue( - new ClerkAPIResponseError('Error', { - data: [ - { - code: 'connection_update_failed', - long_message: 'The connection could not be updated', - message: 'update failed', - }, - ], - status: 400, - }), - ); + fixtures.clerk.organization?.updateEnterpriseConnection.mockResolvedValue({} as any); const { userEvent } = renderPage(wrapper); - expect(await screen.findByText('Active')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button', { name: /open menu/i })); - await userEvent.click(screen.getByRole('menuitem', { name: 'Deactivate' })); + await userEvent.click(await screen.findByRole('button', { name: /clerk\.com/ })); + await userEvent.click(await screen.findByRole('checkbox', { name: /Sync user attributes/ })); + await userEvent.click(screen.getByRole('button', { name: 'Save' })); - expect(await screen.findByText('The connection could not be updated')).toBeInTheDocument(); - // The badge reads from the (unchanged) entity — no optimistic flip to roll back. - expect(screen.getByText('Active')).toBeInTheDocument(); - expect(screen.queryByText('Inactive')).not.toBeInTheDocument(); + expect(await screen.findByRole('button', { name: 'Start configuration' })).toBeInTheDocument(); + expect(screen.queryByText('Identity provider')).not.toBeInTheDocument(); }); }); @@ -528,42 +404,16 @@ describe('OrganizationSecurityPage', () => { expect(screen.queryByText('Unconfigured')).not.toBeInTheDocument(); }); - it('opens the wizard scoped to the row whose Edit was selected', async () => { + it('opens the connection page of the clicked row', async () => { const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); withTwoConnections(fixtures); const { userEvent } = renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); - await userEvent.click(screen.getByRole('menuitem', { name: 'Edit' })); + await userEvent.click(await screen.findByRole('button', { name: /second\.com/ })); - expect(await screen.findByText('Editing "second.com"')).toBeInTheDocument(); - expect(screen.queryByText('Editing "first.com"')).not.toBeInTheDocument(); - }); - - it('removes the connection of the row whose Remove was selected', async () => { - const { wrapper, fixtures } = await createFixtures(withSecurityPageFixtures); - withTwoConnections(fixtures); - fixtures.clerk.organization?.deleteEnterpriseConnection.mockResolvedValue({} as any); - - const { userEvent } = renderPage(wrapper); - - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); - await userEvent.click(screen.getByRole('menuitem', { name: 'Remove' })); - - expect( - await screen.findByText(/Are you sure you want to remove the connection "second.com"/i), - ).toBeInTheDocument(); - - await userEvent.type(screen.getByLabelText(/below to continue/i), 'Org1'); - await waitFor(() => expect(screen.getByRole('button', { name: 'Remove connection' })).toBeEnabled()); - await userEvent.click(screen.getByRole('button', { name: 'Remove connection' })); - - await waitFor(() => { - expect(fixtures.clerk.organization?.deleteEnterpriseConnection).toHaveBeenCalledWith('ent_2'); - }); + expect(await screen.findByRole('heading', { name: 'second.com' })).toBeInTheDocument(); + expect(screen.queryByRole('heading', { name: 'first.com' })).not.toBeInTheDocument(); }); it('opens the wizard on a new connection from Add connection', async () => { @@ -574,8 +424,8 @@ describe('OrganizationSecurityPage', () => { await userEvent.click(await screen.findByRole('button', { name: /Add connection/i })); - expect(await screen.findByText('Adding a new SSO connection')).toBeInTheDocument(); - expect(screen.queryByText(/^Editing /)).not.toBeInTheDocument(); + expect(await screen.findByText(/add and verify ownership of the domains/i)).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /Add connection/i })).not.toBeInTheDocument(); }); }); @@ -619,7 +469,8 @@ describe('OrganizationSecurityPage', () => { renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(1)); + expect(await screen.findByRole('button', { name: /clerk\.com/ })).toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); expect(screen.queryByText('Directory Sync')).not.toBeInTheDocument(); expect(fixtures.clerk.organization?.getDirectorySync).not.toHaveBeenCalled(); }); @@ -636,7 +487,7 @@ describe('OrganizationSecurityPage', () => { const startButton = await screen.findByRole('button', { name: 'Start configuration' }); expect(startButton).toBeEnabled(); expect(screen.queryByText('SSO Required')).not.toBeInTheDocument(); - expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(1); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); }); it('surfaces a load error when the directory request fails for any other reason', async () => { @@ -653,7 +504,7 @@ describe('OrganizationSecurityPage', () => { expect(await screen.findByText('Could not load Directory Sync')).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Start configuration' })).not.toBeInTheDocument(); - expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(1); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); }); it('disables setup and flags SSO as required when no connection exists', async () => { @@ -697,8 +548,7 @@ describe('OrganizationSecurityPage', () => { const { userEvent } = renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); + await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); expect(screen.getByRole('menuitem', { name: 'Edit' })).toBeInTheDocument(); expect(screen.getByRole('menuitem', { name: 'Deactivate' })).toBeInTheDocument(); @@ -718,13 +568,12 @@ describe('OrganizationSecurityPage', () => { const { userEvent } = renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); + await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); await userEvent.click(screen.getByRole('menuitem', { name: 'Deactivate' })); expect(activeDirectory.update).toHaveBeenCalledWith({ enabled: false }); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); + await userEvent.click(screen.getByRole('button', { name: /open menu/i })); await waitFor(() => expect(screen.getByRole('menuitem', { name: 'Activate' })).toBeInTheDocument()); }); @@ -737,8 +586,7 @@ describe('OrganizationSecurityPage', () => { const { userEvent } = renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); + await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); await userEvent.click(screen.getByRole('menuitem', { name: 'Remove' })); expect(await screen.findByRole('heading', { name: 'Remove Directory Sync' })).toBeInTheDocument(); @@ -750,7 +598,8 @@ describe('OrganizationSecurityPage', () => { expect(activeDirectory.delete).toHaveBeenCalledWith(); await waitFor(() => expect(screen.getByRole('button', { name: 'Start configuration' })).toBeInTheDocument()); - }); + // Typing the confirmation and swapping the section back has hit the 5s default on CI. + }, 15_000); it('opens the Directory Sync wizard from Edit', async () => { const { wrapper, fixtures } = await createFixtures(withDirectorySyncFixtures); @@ -759,8 +608,7 @@ describe('OrganizationSecurityPage', () => { const { userEvent } = renderPage(wrapper); - await waitFor(() => expect(screen.getAllByRole('button', { name: /open menu/i })).toHaveLength(2)); - await userEvent.click(screen.getAllByRole('button', { name: /open menu/i })[1]); + await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); await userEvent.click(screen.getByRole('menuitem', { name: 'Edit' })); await waitFor(() => expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument()); diff --git a/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPageWizardLoading.test.tsx b/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPageWizardLoading.test.tsx index b3aea010d68..a4b9c6722a1 100644 --- a/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPageWizardLoading.test.tsx +++ b/packages/ui/src/components/OrganizationProfile/__tests__/OrganizationSecurityPageWizardLoading.test.tsx @@ -68,6 +68,9 @@ vi.mock('../../ConfigureSSO/hooks/useOrganizationEnterpriseConnection', () => ({ enterpriseConnections: [activeConnection], connectionScope: { kind: 'existing', id: activeConnection.id }, selectConnection: () => {}, + connectionDomains: activeConnection.domains, + setConnectionDomains: noop, + claimedDomains: new Map(), organizationEnterpriseConnection: buildOrganizationEnterpriseConnection({ connection: activeConnection, hasSuccessfulTestRun: true, @@ -122,9 +125,10 @@ describe('OrganizationSecurityPage — wizard survives a mid-flow loading toggle const { userEvent } = render(, { wrapper }); - // Enter the wizard from the overview via Edit, which forces the first step. - await userEvent.click(await screen.findByRole('button', { name: /open menu/i })); - await userEvent.click(await screen.findByRole('menuitem', { name: 'Edit' })); + // Enter the wizard from the overview via Add connection, which forces the + // first step. The mocked hook pins the scope to `activeConnection`, so the + // wizard opens on it regardless of the requested scope. + await userEvent.click(await screen.findByRole('button', { name: /Add connection/i })); expect(await screen.findByRole('heading', { name: /add SSO domains/i })).toBeInTheDocument(); // Navigate forward to the Activate step via the breadcrumb (reachable because diff --git a/packages/ui/src/components/OrganizationProfile/enterpriseConnectionStatusBadges.ts b/packages/ui/src/components/OrganizationProfile/enterpriseConnectionStatusBadges.ts new file mode 100644 index 00000000000..73b43cad2fa --- /dev/null +++ b/packages/ui/src/components/OrganizationProfile/enterpriseConnectionStatusBadges.ts @@ -0,0 +1,29 @@ +import type { LocalizationKey } from '../../customizables'; +import { localizationKeys } from '../../customizables'; +import type { OrganizationEnterpriseConnectionStatus } from '../ConfigureSSO/domain/organizationEnterpriseConnection'; + +export const STATUS_BADGES: Record< + OrganizationEnterpriseConnectionStatus, + { id: string; colorScheme?: 'primary' | 'danger' | 'warning' | 'success'; label: LocalizationKey } +> = { + unconfigured: { + id: 'unconfigured', + colorScheme: 'primary', + label: localizationKeys('organizationProfile.securityPage.ssoSection.badge__unconfigured'), + }, + in_progress: { + id: 'inProgress', + colorScheme: 'warning', + label: localizationKeys('organizationProfile.securityPage.ssoSection.badge__inProgress'), + }, + active: { + id: 'active', + colorScheme: 'success', + label: localizationKeys('organizationProfile.securityPage.ssoSection.badge__active'), + }, + inactive: { + id: 'inactive', + colorScheme: 'danger', + label: localizationKeys('organizationProfile.securityPage.ssoSection.badge__inactive'), + }, +}; diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts index 4c7e9b04aaf..e14557f0f09 100644 --- a/packages/ui/src/customizables/elementDescriptors.ts +++ b/packages/ui/src/customizables/elementDescriptors.ts @@ -240,6 +240,8 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'organizationProfileSecuritySsoDetailRowChip', 'organizationProfileSecuritySsoDetailRowLink', 'organizationProfileSecuritySsoProviderIcon', + 'organizationProfileSecuritySsoConnectionRow', + 'organizationProfileSecuritySsoConnectionPage', 'organizationListPreviewItems', 'organizationListPreviewItem', @@ -593,6 +595,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'configureSSOVerifyDomainSuggestion', 'configureSSOVerifyDomainCard', 'configureSSOVerifyDomainCardBadge', + 'configureSSOVerifyDomainCardCheckbox', 'configureSSOVerifyDomainCardRemoveButton', 'configureSSOVerifyDomainCardTxtRecord', 'configureSSOVerifyDomainCardTxtRecordValue', diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index fec705169b0..29c94e9b1d5 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -380,6 +380,8 @@ export type ElementsConfig = { organizationProfileSecuritySsoDetailRowChip: WithOptions; organizationProfileSecuritySsoDetailRowLink: WithOptions; organizationProfileSecuritySsoProviderIcon: WithOptions; + organizationProfileSecuritySsoConnectionRow: WithOptions; + organizationProfileSecuritySsoConnectionPage: WithOptions; organizationListPreviewItems: WithOptions; organizationListPreviewItem: WithOptions; @@ -728,7 +730,8 @@ export type ElementsConfig = { configureSSOVerifyDomainList: WithOptions; configureSSOVerifyDomainSuggestion: WithOptions; configureSSOVerifyDomainCard: WithOptions<'verified' | 'unverified' | 'expired'>; - configureSSOVerifyDomainCardBadge: WithOptions<'verified' | 'unverified' | 'expired'>; + configureSSOVerifyDomainCardBadge: WithOptions<'verified' | 'unverified' | 'expired' | 'claimed'>; + configureSSOVerifyDomainCardCheckbox: WithOptions; configureSSOVerifyDomainCardRemoveButton: WithOptions; configureSSOVerifyDomainCardTxtRecord: WithOptions; configureSSOVerifyDomainCardTxtRecordValue: WithOptions;