0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-24 22:41:28 -05:00

refactor(console): hide saml social connector entrance and fix SSO creation paywall ()

This commit is contained in:
Darcy Ye 2023-12-05 10:50:49 +08:00 committed by GitHub
parent a1725669d4
commit 774de5cc7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 39 deletions
packages
console/src
components/CreateConnectorForm
pages/EnterpriseSso
SsoCreationModal
index.tsx
integration-tests/src/tests/console/connectors

View file

@ -54,7 +54,11 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
}
const allGroups = getConnectorGroups<ConnectorFactoryResponse>(
factories.filter(({ type: factoryType, isDemo }) => factoryType === type && !isDemo)
factories
.filter(({ type: factoryType, isDemo }) => factoryType === type && !isDemo)
// Hide the entrance of adding SAML social connectors, users should go to Enterprise SSO if they want to use SAML.
// Should not remove the SAML factory from GET /connector-factories API, since that could break the existing SAML connectors.
.filter(({ id }) => id !== 'saml')
);
return allGroups

View file

@ -4,14 +4,18 @@ import {
type RequestErrorBody,
} from '@logto/schemas';
import { HTTPError } from 'ky';
import { useMemo, useState } from 'react';
import { useMemo, useState, useContext } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { Trans, useTranslation } from 'react-i18next';
import Modal from 'react-modal';
import useSWR from 'swr';
import ContactUsPhraseLink from '@/components/ContactUsPhraseLink';
import Skeleton from '@/components/CreateConnectorForm/Skeleton';
import { getConnectorRadioGroupSize } from '@/components/CreateConnectorForm/utils';
import QuotaGuardFooter from '@/components/QuotaGuardFooter';
import { isCloud } from '@/consts/env';
import { TenantsContext } from '@/contexts/TenantsProvider';
import Button from '@/ds-components/Button';
import DynamicT from '@/ds-components/DynamicT';
import FormField from '@/ds-components/FormField';
@ -19,6 +23,7 @@ import ModalLayout from '@/ds-components/ModalLayout';
import TextInput from '@/ds-components/TextInput';
import { type RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
import * as modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
@ -39,7 +44,12 @@ const duplicateConnectorNameErrorCode = 'single_sign_on.duplicate_connector_name
function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { currentTenantId } = useContext(TenantsContext);
const { data: currentPlan } = useSubscriptionPlan(currentTenantId);
const [selectedProviderName, setSelectedProviderName] = useState<string>();
const isSsoEnabled = !isCloud || currentPlan?.quota.ssoEnabled;
const { data, error } = useSWR<SsoConnectorProvidersResponse, RequestError>(
'api/sso-connector-providers'
);
@ -122,18 +132,30 @@ function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
<ModalLayout
title="enterprise_sso.create_modal.title"
footer={
<Button
title="enterprise_sso.create_modal.create_button_text"
type="primary"
disabled={
// The button is available only when:
// 1. `connectorName` field is not empty.
// 2. At least one connector is selected.
// 3. Error is resolved. Since `connectorName` is the only field of this form, it means `connectorName` field error is resolved.
!(watch('connectorName') && isAnyConnectorSelected) || Boolean(errors.connectorName)
}
onClick={onSubmit}
/>
isSsoEnabled ? (
<Button
title="enterprise_sso.create_modal.create_button_text"
type="primary"
disabled={
// The button is available only when:
// 1. `connectorName` field is not empty.
// 2. At least one connector is selected.
// 3. Error is resolved. Since `connectorName` is the only field of this form, it means `connectorName` field error is resolved.
!(watch('connectorName') && isAnyConnectorSelected) || Boolean(errors.connectorName)
}
onClick={onSubmit}
/>
) : (
<QuotaGuardFooter>
<Trans
components={{
a: <ContactUsPhraseLink />,
}}
>
{t('upsell.paywall.organizations')}
</Trans>
</QuotaGuardFooter>
)
}
size={radioGroupSize}
onClose={onClose}

View file

@ -72,9 +72,10 @@ function EnterpriseSsoConnectors() {
pageMeta={{ titleKey: 'enterprise_sso.page_title' }}
createButton={conditional(
ssoConnectors?.length && {
title: isSsoEnabled ? 'enterprise_sso.create' : 'upsell.upgrade_plan',
onClick: handleButtonClick,
icon: conditional(isSsoEnabled && <Plus />),
title: 'enterprise_sso.create',
onClick: () => {
navigate(createEnterpriseSsoPathname);
},
}
)}
table={{

View file

@ -87,35 +87,36 @@ const wechatWeb: SocialConnectorCase = {
},
};
const saml: SocialConnectorCase = {
factoryId: 'saml',
name: 'SAML',
const oidc: SocialConnectorCase = {
factoryId: 'oidc',
name: 'OIDC',
initialFormData: {
'formConfig.entityID': 'entity-id',
'formConfig.signInEndpoint': 'sign-in-endpoint',
'formConfig.x509Certificate': 'x509-certificate',
'formConfig.idpMetadataXml': 'idp-metadata-xml',
'formConfig.assertionConsumerServiceUrl': 'assertion-consumer-service-url',
'formConfig.authorizationEndpoint': 'authorization-endpoint',
'formConfig.tokenEndpoint': 'token-endpoint',
'formConfig.clientId': 'client-id',
'formConfig.clientSecret': 'client-secret',
'formConfig.scope': 'scope1 scope2',
},
updateFormData: {
'formConfig.entityID': 'new-entity-id',
'formConfig.signInEndpoint': 'new-sign-in-endpoint',
'formConfig.x509Certificate': 'new-x509-certificate',
'formConfig.idpMetadataXml': 'new-idp-metadata-xml',
'formConfig.assertionConsumerServiceUrl': 'new-assertion-consumer-service-url',
'formConfig.authorizationEndpoint': 'new-authorization-endpoint',
'formConfig.tokenEndpoint': 'new-token-endpoint',
'formConfig.clientId': 'new-client-id',
'formConfig.clientSecret': 'new-client-secret',
'formConfig.scope': 'scope1 scope2 scope3',
},
errorFormData: {
'formConfig.entityID': '',
'formConfig.signInEndpoint': '',
'formConfig.x509Certificate': '',
'formConfig.idpMetadataXml': '',
'formConfig.assertionConsumerServiceUrl': '',
'formConfig.authorizationEndpoint': '',
'formConfig.tokenEndpoint': '',
'formConfig.clientId': '',
'formConfig.clientSecret': '',
'formConfig.scope': '',
},
standardBasicFormData: {
name: 'SAML',
target: 'saml',
name: 'OIDC',
target: 'oidc-test',
},
};
export const socialConnectorTestCases = [
// Universal
google,
@ -126,5 +127,5 @@ export const socialConnectorTestCases = [
// Group - Web
wechatWeb,
// Standard
saml,
oidc,
];