Enforce system wide sso providers (#9058)
We have recently introduced the possibility to specify workspace specific auth providers. I'm: - introducing system wide auth providers (provided by clientConfig) - making sure workspace specific auth providers belong to system wide auth providers set
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import * as Apollo from '@apollo/client';
|
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
export type Maybe<T> = T | null;
|
export type Maybe<T> = T | null;
|
||||||
export type InputMaybe<T> = Maybe<T>;
|
export type InputMaybe<T> = Maybe<T>;
|
||||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||||
@ -169,6 +169,7 @@ export type ClientConfig = {
|
|||||||
__typename?: 'ClientConfig';
|
__typename?: 'ClientConfig';
|
||||||
analyticsEnabled: Scalars['Boolean'];
|
analyticsEnabled: Scalars['Boolean'];
|
||||||
api: ApiConfig;
|
api: ApiConfig;
|
||||||
|
authProviders: AuthProviders;
|
||||||
billing: Billing;
|
billing: Billing;
|
||||||
captcha: Captcha;
|
captcha: Captcha;
|
||||||
chromeExtensionId?: Maybe<Scalars['String']>;
|
chromeExtensionId?: Maybe<Scalars['String']>;
|
||||||
@ -1577,7 +1578,7 @@ export type Field = {
|
|||||||
id: Scalars['UUID'];
|
id: Scalars['UUID'];
|
||||||
isActive?: Maybe<Scalars['Boolean']>;
|
isActive?: Maybe<Scalars['Boolean']>;
|
||||||
isCustom?: Maybe<Scalars['Boolean']>;
|
isCustom?: Maybe<Scalars['Boolean']>;
|
||||||
isLabelSyncedWithName: Scalars['Boolean'];
|
isLabelSyncedWithName?: Maybe<Scalars['Boolean']>;
|
||||||
isNullable?: Maybe<Scalars['Boolean']>;
|
isNullable?: Maybe<Scalars['Boolean']>;
|
||||||
isSystem?: Maybe<Scalars['Boolean']>;
|
isSystem?: Maybe<Scalars['Boolean']>;
|
||||||
isUnique?: Maybe<Scalars['Boolean']>;
|
isUnique?: Maybe<Scalars['Boolean']>;
|
||||||
@ -1970,7 +1971,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
|
|||||||
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isSSOEnabled: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
|
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isSSOEnabled: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
|
||||||
|
|
||||||
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
|
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
@ -3355,6 +3356,18 @@ export const GetClientConfigDocument = gql`
|
|||||||
billingUrl
|
billingUrl
|
||||||
billingFreeTrialDurationInDays
|
billingFreeTrialDurationInDays
|
||||||
}
|
}
|
||||||
|
authProviders {
|
||||||
|
google
|
||||||
|
password
|
||||||
|
microsoft
|
||||||
|
sso {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
status
|
||||||
|
issuer
|
||||||
|
}
|
||||||
|
}
|
||||||
signInPrefilled
|
signInPrefilled
|
||||||
isMultiWorkspaceEnabled
|
isMultiWorkspaceEnabled
|
||||||
isSSOEnabled
|
isSSOEnabled
|
||||||
|
|||||||
@ -7,14 +7,14 @@ import { RecoilRoot, useRecoilValue } from 'recoil';
|
|||||||
import { iconsState } from 'twenty-ui';
|
import { iconsState } from 'twenty-ui';
|
||||||
|
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
import { billingState } from '@/client-config/states/billingState';
|
import { billingState } from '@/client-config/states/billingState';
|
||||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
|
|
||||||
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
|
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||||
|
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
|
||||||
|
|
||||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
<MockedProvider mocks={mocks} addTypename={false}>
|
<MockedProvider mocks={mocks} addTypename={false}>
|
||||||
@ -77,7 +77,9 @@ describe('useAuth', () => {
|
|||||||
() => {
|
() => {
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const icons = useRecoilValue(iconsState);
|
const icons = useRecoilValue(iconsState);
|
||||||
const authProviders = useRecoilValue(authProvidersState);
|
const workspaceAuthProviders = useRecoilValue(
|
||||||
|
workspaceAuthProvidersState,
|
||||||
|
);
|
||||||
const billing = useRecoilValue(billingState);
|
const billing = useRecoilValue(billingState);
|
||||||
const isDeveloperDefaultSignInPrefilled = useRecoilValue(
|
const isDeveloperDefaultSignInPrefilled = useRecoilValue(
|
||||||
isDeveloperDefaultSignInPrefilledState,
|
isDeveloperDefaultSignInPrefilledState,
|
||||||
@ -92,7 +94,7 @@ describe('useAuth', () => {
|
|||||||
client,
|
client,
|
||||||
state: {
|
state: {
|
||||||
icons,
|
icons,
|
||||||
authProviders,
|
workspaceAuthProviders,
|
||||||
billing,
|
billing,
|
||||||
isDeveloperDefaultSignInPrefilled,
|
isDeveloperDefaultSignInPrefilled,
|
||||||
supportChat,
|
supportChat,
|
||||||
@ -118,7 +120,7 @@ describe('useAuth', () => {
|
|||||||
const { state } = result.current;
|
const { state } = result.current;
|
||||||
|
|
||||||
expect(state.icons).toEqual({});
|
expect(state.icons).toEqual({});
|
||||||
expect(state.authProviders).toEqual({
|
expect(state.workspaceAuthProviders).toEqual({
|
||||||
google: true,
|
google: true,
|
||||||
microsoft: false,
|
microsoft: false,
|
||||||
magicLink: false,
|
magicLink: false,
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|||||||
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
|
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
|
||||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||||
import { workspacesState } from '@/auth/states/workspaces';
|
import { workspacesState } from '@/auth/states/workspaces';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
import { billingState } from '@/client-config/states/billingState';
|
import { billingState } from '@/client-config/states/billingState';
|
||||||
import { captchaProviderState } from '@/client-config/states/captchaProviderState';
|
import { captchaProviderState } from '@/client-config/states/captchaProviderState';
|
||||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||||
@ -48,6 +47,7 @@ import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useL
|
|||||||
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
|
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
|
||||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||||
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
|
|
||||||
export const useAuth = () => {
|
export const useAuth = () => {
|
||||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||||
@ -90,7 +90,7 @@ export const useAuth = () => {
|
|||||||
const emptySnapshot = snapshot_UNSTABLE();
|
const emptySnapshot = snapshot_UNSTABLE();
|
||||||
const iconsValue = snapshot.getLoadable(iconsState).getValue();
|
const iconsValue = snapshot.getLoadable(iconsState).getValue();
|
||||||
const authProvidersValue = snapshot
|
const authProvidersValue = snapshot
|
||||||
.getLoadable(authProvidersState)
|
.getLoadable(workspaceAuthProvidersState)
|
||||||
.getValue();
|
.getValue();
|
||||||
const billing = snapshot.getLoadable(billingState).getValue();
|
const billing = snapshot.getLoadable(billingState).getValue();
|
||||||
const isDeveloperDefaultSignInPrefilled = snapshot
|
const isDeveloperDefaultSignInPrefilled = snapshot
|
||||||
@ -115,7 +115,7 @@ export const useAuth = () => {
|
|||||||
.getValue();
|
.getValue();
|
||||||
const initialSnapshot = emptySnapshot.map(({ set }) => {
|
const initialSnapshot = emptySnapshot.map(({ set }) => {
|
||||||
set(iconsState, iconsValue);
|
set(iconsState, iconsValue);
|
||||||
set(authProvidersState, authProvidersValue);
|
set(workspaceAuthProvidersState, authProvidersValue);
|
||||||
set(billingState, billing);
|
set(billingState, billing);
|
||||||
set(
|
set(
|
||||||
isDeveloperDefaultSignInPrefilledState,
|
isDeveloperDefaultSignInPrefilledState,
|
||||||
|
|||||||
@ -1,37 +1,31 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {
|
|
||||||
IconGoogle,
|
|
||||||
IconMicrosoft,
|
|
||||||
Loader,
|
|
||||||
MainButton,
|
|
||||||
HorizontalSeparator,
|
|
||||||
} from 'twenty-ui';
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle';
|
|
||||||
import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft';
|
|
||||||
import { FormProvider } from 'react-hook-form';
|
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { FormProvider } from 'react-hook-form';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
import { HorizontalSeparator, Loader, MainButton } from 'twenty-ui';
|
||||||
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
|
import { SignInUpEmailField } from '@/auth/sign-in-up/components/SignInUpEmailField';
|
||||||
|
import { SignInUpPasswordField } from '@/auth/sign-in-up/components/SignInUpPasswordField';
|
||||||
|
import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle';
|
||||||
|
import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft';
|
||||||
|
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
||||||
|
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||||
|
import { signInUpModeState } from '@/auth/states/signInUpModeState';
|
||||||
import {
|
import {
|
||||||
SignInUpStep,
|
SignInUpStep,
|
||||||
signInUpStepState,
|
signInUpStepState,
|
||||||
} from '@/auth/states/signInUpStepState';
|
} from '@/auth/states/signInUpStepState';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
|
||||||
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
|
||||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
|
||||||
import { SignInUpEmailField } from '@/auth/sign-in-up/components/SignInUpEmailField';
|
|
||||||
import { SignInUpPasswordField } from '@/auth/sign-in-up/components/SignInUpPasswordField';
|
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
|
||||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
|
||||||
import { signInUpModeState } from '@/auth/states/signInUpModeState';
|
|
||||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
|
||||||
import { SignInUpMode } from '@/auth/types/signInUpMode';
|
import { SignInUpMode } from '@/auth/types/signInUpMode';
|
||||||
|
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||||
|
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||||
|
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||||
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
const StyledContentContainer = styled(motion.div)`
|
const StyledContentContainer = styled(motion.div)`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
@ -46,11 +40,9 @@ const StyledForm = styled.form`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SignInUpGlobalScopeForm = () => {
|
export const SignInUpGlobalScopeForm = () => {
|
||||||
const theme = useTheme();
|
const authProviders = useRecoilValue(authProvidersState);
|
||||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||||
|
|
||||||
const { signInWithGoogle } = useSignInWithGoogle();
|
|
||||||
const { signInWithMicrosoft } = useSignInWithMicrosoft();
|
|
||||||
const { checkUserExists } = useAuth();
|
const { checkUserExists } = useAuth();
|
||||||
const { readCaptchaToken } = useReadCaptchaToken();
|
const { readCaptchaToken } = useReadCaptchaToken();
|
||||||
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
||||||
@ -116,20 +108,9 @@ export const SignInUpGlobalScopeForm = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledContentContainer>
|
<StyledContentContainer>
|
||||||
<MainButton
|
{authProviders.google && <SignInUpWithGoogle />}
|
||||||
Icon={() => <IconGoogle size={theme.icon.size.lg} />}
|
|
||||||
title="Continue with Google"
|
{authProviders.microsoft && <SignInUpWithMicrosoft />}
|
||||||
onClick={signInWithGoogle}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<HorizontalSeparator visible={false} />
|
|
||||||
<MainButton
|
|
||||||
Icon={() => <IconMicrosoft size={theme.icon.size.lg} />}
|
|
||||||
title="Continue with Microsoft"
|
|
||||||
onClick={signInWithMicrosoft}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<HorizontalSeparator visible={false} />
|
|
||||||
<HorizontalSeparator visible />
|
<HorizontalSeparator visible />
|
||||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
|
|||||||
import { guessSSOIdentityProviderIconByUrl } from '@/settings/security/utils/guessSSOIdentityProviderIconByUrl';
|
import { guessSSOIdentityProviderIconByUrl } from '@/settings/security/utils/guessSSOIdentityProviderIconByUrl';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { MainButton, HorizontalSeparator } from 'twenty-ui';
|
import { HorizontalSeparator, MainButton } from 'twenty-ui';
|
||||||
|
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
@ -15,15 +15,15 @@ const StyledContentContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SignInUpSSOIdentityProviderSelection = () => {
|
export const SignInUpSSOIdentityProviderSelection = () => {
|
||||||
const authProviders = useRecoilValue(authProvidersState);
|
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||||
|
|
||||||
const { redirectToSSOLoginPage } = useSSO();
|
const { redirectToSSOLoginPage } = useSSO();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledContentContainer>
|
<StyledContentContainer>
|
||||||
{isDefined(authProviders?.sso) &&
|
{isDefined(workspaceAuthProviders?.sso) &&
|
||||||
authProviders?.sso.map((idp) => (
|
workspaceAuthProviders?.sso.map((idp) => (
|
||||||
<>
|
<>
|
||||||
<MainButton
|
<MainButton
|
||||||
key={idp.id}
|
key={idp.id}
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
import { IconLock, MainButton, HorizontalSeparator } from 'twenty-ui';
|
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
|
||||||
import {
|
import {
|
||||||
SignInUpStep,
|
SignInUpStep,
|
||||||
signInUpStepState,
|
signInUpStepState,
|
||||||
} from '@/auth/states/signInUpStepState';
|
} from '@/auth/states/signInUpStepState';
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
|
import { HorizontalSeparator, IconLock, MainButton } from 'twenty-ui';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
|
|
||||||
export const SignInUpWithSSO = () => {
|
export const SignInUpWithSSO = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||||
const authProviders = useRecoilValue(authProvidersState);
|
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||||
|
|
||||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||||
|
|
||||||
const { redirectToSSOLoginPage } = useSSO();
|
const { redirectToSSOLoginPage } = useSSO();
|
||||||
|
|
||||||
const signInWithSSO = () => {
|
const signInWithSSO = () => {
|
||||||
if (authProviders.sso.length === 1) {
|
if (workspaceAuthProviders.sso.length === 1) {
|
||||||
return redirectToSSOLoginPage(authProviders.sso[0].id);
|
return redirectToSSOLoginPage(workspaceAuthProviders.sso[0].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSignInUpStep(SignInUpStep.SSOIdentityProviderSelection);
|
setSignInUpStep(SignInUpStep.SSOIdentityProviderSelection);
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
|
import { SignInUpWithCredentials } from '@/auth/sign-in-up/components/SignInUpWithCredentials';
|
||||||
|
import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle';
|
||||||
|
import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft';
|
||||||
|
import { SignInUpWithSSO } from '@/auth/sign-in-up/components/SignInUpWithSSO';
|
||||||
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
|
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
|
||||||
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
||||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||||
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { ActionLink, HorizontalSeparator } from 'twenty-ui';
|
import { ActionLink, HorizontalSeparator } from 'twenty-ui';
|
||||||
import { SignInUpWithGoogle } from '@/auth/sign-in-up/components/SignInUpWithGoogle';
|
|
||||||
import { SignInUpWithMicrosoft } from '@/auth/sign-in-up/components/SignInUpWithMicrosoft';
|
|
||||||
import { SignInUpWithSSO } from '@/auth/sign-in-up/components/SignInUpWithSSO';
|
|
||||||
import { SignInUpWithCredentials } from '@/auth/sign-in-up/components/SignInUpWithCredentials';
|
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
@ -17,7 +17,7 @@ const StyledContentContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SignInUpWorkspaceScopeForm = () => {
|
export const SignInUpWorkspaceScopeForm = () => {
|
||||||
const [authProviders] = useRecoilState(authProvidersState);
|
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||||
|
|
||||||
const { form } = useSignInUpForm();
|
const { form } = useSignInUpForm();
|
||||||
const { handleResetPassword } = useHandleResetPassword();
|
const { handleResetPassword } = useHandleResetPassword();
|
||||||
@ -27,20 +27,20 @@ export const SignInUpWorkspaceScopeForm = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledContentContainer>
|
<StyledContentContainer>
|
||||||
{authProviders.google && <SignInUpWithGoogle />}
|
{workspaceAuthProviders.google && <SignInUpWithGoogle />}
|
||||||
|
|
||||||
{authProviders.microsoft && <SignInUpWithMicrosoft />}
|
{workspaceAuthProviders.microsoft && <SignInUpWithMicrosoft />}
|
||||||
|
|
||||||
{authProviders.sso.length > 0 && <SignInUpWithSSO />}
|
{workspaceAuthProviders.sso.length > 0 && <SignInUpWithSSO />}
|
||||||
|
|
||||||
{(authProviders.google ||
|
{(workspaceAuthProviders.google ||
|
||||||
authProviders.microsoft ||
|
workspaceAuthProviders.microsoft ||
|
||||||
authProviders.sso.length > 0) &&
|
workspaceAuthProviders.sso.length > 0) &&
|
||||||
authProviders.password ? (
|
workspaceAuthProviders.password ? (
|
||||||
<HorizontalSeparator visible />
|
<HorizontalSeparator visible />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{authProviders.password && <SignInUpWithCredentials />}
|
{workspaceAuthProviders.password && <SignInUpWithCredentials />}
|
||||||
</StyledContentContainer>
|
</StyledContentContainer>
|
||||||
{signInUpStep === SignInUpStep.Password && (
|
{signInUpStep === SignInUpStep.Password && (
|
||||||
<ActionLink onClick={handleResetPassword(form.getValues('email'))}>
|
<ActionLink onClick={handleResetPassword(form.getValues('email'))}>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
import { useSignInUp } from '@/auth/sign-in-up/hooks/useSignInUp';
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||||
import { useRecoilState } from 'recoil';
|
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const email = searchParams.get('email');
|
const email = searchParams.get('email');
|
||||||
|
|
||||||
export const SignInUpWorkspaceScopeFormEffect = () => {
|
export const SignInUpWorkspaceScopeFormEffect = () => {
|
||||||
const [authProviders] = useRecoilState(authProvidersState);
|
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||||
|
|
||||||
const { form } = useSignInUpForm();
|
const { form } = useSignInUpForm();
|
||||||
|
|
||||||
@ -20,22 +20,22 @@ export const SignInUpWorkspaceScopeFormEffect = () => {
|
|||||||
const checkAuthProviders = useCallback(() => {
|
const checkAuthProviders = useCallback(() => {
|
||||||
if (
|
if (
|
||||||
signInUpStep === SignInUpStep.Init &&
|
signInUpStep === SignInUpStep.Init &&
|
||||||
!authProviders.google &&
|
!workspaceAuthProviders.google &&
|
||||||
!authProviders.microsoft &&
|
!workspaceAuthProviders.microsoft &&
|
||||||
!authProviders.sso
|
!workspaceAuthProviders.sso
|
||||||
) {
|
) {
|
||||||
return continueWithEmail();
|
return continueWithEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefined(email) && authProviders.password) {
|
if (isDefined(email) && workspaceAuthProviders.password) {
|
||||||
return continueWithCredentials();
|
return continueWithCredentials();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
signInUpStep,
|
signInUpStep,
|
||||||
authProviders.google,
|
workspaceAuthProviders.google,
|
||||||
authProviders.microsoft,
|
workspaceAuthProviders.microsoft,
|
||||||
authProviders.sso,
|
workspaceAuthProviders.sso,
|
||||||
authProviders.password,
|
workspaceAuthProviders.password,
|
||||||
continueWithEmail,
|
continueWithEmail,
|
||||||
continueWithCredentials,
|
continueWithCredentials,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
||||||
|
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||||
import { billingState } from '@/client-config/states/billingState';
|
import { billingState } from '@/client-config/states/billingState';
|
||||||
import { captchaProviderState } from '@/client-config/states/captchaProviderState';
|
import { captchaProviderState } from '@/client-config/states/captchaProviderState';
|
||||||
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
|
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
|
||||||
@ -7,19 +8,20 @@ import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabl
|
|||||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||||
|
import { isSSOEnabledState } from '@/client-config/states/isSSOEnabledState';
|
||||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||||
|
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { useGetClientConfigQuery } from '~/generated/graphql';
|
import { useGetClientConfigQuery } from '~/generated/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
|
||||||
import { isSSOEnabledState } from '@/client-config/states/isSSOEnabledState';
|
|
||||||
|
|
||||||
export const ClientConfigProviderEffect = () => {
|
export const ClientConfigProviderEffect = () => {
|
||||||
const setIsDebugMode = useSetRecoilState(isDebugModeState);
|
const setIsDebugMode = useSetRecoilState(isDebugModeState);
|
||||||
const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState);
|
const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState);
|
||||||
const setDomainConfiguration = useSetRecoilState(domainConfigurationState);
|
const setDomainConfiguration = useSetRecoilState(domainConfigurationState);
|
||||||
|
const setAuthProviders = useSetRecoilState(authProvidersState);
|
||||||
|
|
||||||
const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState(
|
const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState(
|
||||||
isDeveloperDefaultSignInPrefilledState,
|
isDeveloperDefaultSignInPrefilledState,
|
||||||
@ -73,6 +75,13 @@ export const ClientConfigProviderEffect = () => {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
setAuthProviders({
|
||||||
|
google: data?.clientConfig.authProviders.google,
|
||||||
|
microsoft: data?.clientConfig.authProviders.microsoft,
|
||||||
|
password: data?.clientConfig.authProviders.password,
|
||||||
|
magicLink: false,
|
||||||
|
sso: data?.clientConfig.authProviders.sso,
|
||||||
|
});
|
||||||
setIsDebugMode(data?.clientConfig.debugMode);
|
setIsDebugMode(data?.clientConfig.debugMode);
|
||||||
setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled);
|
setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled);
|
||||||
setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled);
|
setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled);
|
||||||
@ -115,6 +124,7 @@ export const ClientConfigProviderEffect = () => {
|
|||||||
error,
|
error,
|
||||||
setDomainConfiguration,
|
setDomainConfiguration,
|
||||||
setIsSSOEnabledState,
|
setIsSSOEnabledState,
|
||||||
|
setAuthProviders,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -8,6 +8,18 @@ export const GET_CLIENT_CONFIG = gql`
|
|||||||
billingUrl
|
billingUrl
|
||||||
billingFreeTrialDurationInDays
|
billingFreeTrialDurationInDays
|
||||||
}
|
}
|
||||||
|
authProviders {
|
||||||
|
google
|
||||||
|
password
|
||||||
|
microsoft
|
||||||
|
sso {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
type
|
||||||
|
status
|
||||||
|
issuer
|
||||||
|
}
|
||||||
|
}
|
||||||
signInPrefilled
|
signInPrefilled
|
||||||
isMultiWorkspaceEnabled
|
isMultiWorkspaceEnabled
|
||||||
isSSOEnabled
|
isSSOEnabled
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import { useGetPublicWorkspaceDataBySubdomainQuery } from '~/generated/graphql';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
||||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
|
||||||
import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain';
|
|
||||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||||
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
||||||
|
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||||
|
import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain';
|
||||||
|
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||||
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
import { useGetPublicWorkspaceDataBySubdomainQuery } from '~/generated/graphql';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const useGetPublicWorkspaceDataBySubdomain = () => {
|
export const useGetPublicWorkspaceDataBySubdomain = () => {
|
||||||
const { isDefaultDomain } = useIsCurrentLocationOnDefaultDomain();
|
const { isDefaultDomain } = useIsCurrentLocationOnDefaultDomain();
|
||||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||||
const setAuthProviders = useSetRecoilState(authProvidersState);
|
const setWorkspaceAuthProviders = useSetRecoilState(
|
||||||
|
workspaceAuthProvidersState,
|
||||||
|
);
|
||||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||||
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
||||||
const setWorkspacePublicDataState = useSetRecoilState(
|
const setWorkspacePublicDataState = useSetRecoilState(
|
||||||
@ -25,7 +27,9 @@ export const useGetPublicWorkspaceDataBySubdomain = () => {
|
|||||||
(isMultiWorkspaceEnabled && isDefaultDomain) ||
|
(isMultiWorkspaceEnabled && isDefaultDomain) ||
|
||||||
isDefined(workspacePublicData),
|
isDefined(workspacePublicData),
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
setAuthProviders(data.getPublicWorkspaceDataBySubdomain.authProviders);
|
setWorkspaceAuthProviders(
|
||||||
|
data.getPublicWorkspaceDataBySubdomain.authProviders,
|
||||||
|
);
|
||||||
setWorkspacePublicDataState(data.getPublicWorkspaceDataBySubdomain);
|
setWorkspacePublicDataState(data.getPublicWorkspaceDataBySubdomain);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDeco
|
|||||||
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
||||||
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
||||||
|
|
||||||
|
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext';
|
import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext';
|
||||||
import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||||
@ -63,87 +64,98 @@ const meta: Meta = {
|
|||||||
(Story) => {
|
(Story) => {
|
||||||
return (
|
return (
|
||||||
<RecordFieldValueSelectorContextProvider>
|
<RecordFieldValueSelectorContextProvider>
|
||||||
<RecordTableContextProvider
|
<RecordIndexContextProvider
|
||||||
value={{
|
value={{
|
||||||
recordTableId: 'recordTableId',
|
indexIdentifierUrl: (_recordId: string) => '',
|
||||||
viewBarId: mockPerformance.recordId,
|
onIndexRecordsLoaded: () => {},
|
||||||
|
objectNamePlural: 'companies',
|
||||||
|
objectNameSingular: 'company',
|
||||||
objectMetadataItem: mockPerformance.objectMetadataItem as any,
|
objectMetadataItem: mockPerformance.objectMetadataItem as any,
|
||||||
visibleTableColumns: mockPerformance.visibleTableColumns as any,
|
recordIndexId: 'recordIndexId',
|
||||||
objectNameSingular:
|
|
||||||
mockPerformance.objectMetadataItem.nameSingular,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RecordTableComponentInstance
|
<RecordTableContextProvider
|
||||||
recordTableId="asd"
|
value={{
|
||||||
onColumnsChange={() => {}}
|
recordTableId: 'recordTableId',
|
||||||
|
viewBarId: mockPerformance.recordId,
|
||||||
|
objectMetadataItem: mockPerformance.objectMetadataItem as any,
|
||||||
|
visibleTableColumns: mockPerformance.visibleTableColumns as any,
|
||||||
|
objectNameSingular:
|
||||||
|
mockPerformance.objectMetadataItem.nameSingular,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<RecordTableBodyContextProvider
|
<RecordTableComponentInstance
|
||||||
value={{
|
recordTableId="asd"
|
||||||
onUpsertRecord: () => {},
|
onColumnsChange={() => {}}
|
||||||
onOpenTableCell: () => {},
|
|
||||||
onMoveFocus: () => {},
|
|
||||||
onCloseTableCell: () => {},
|
|
||||||
onMoveSoftFocusToCell: () => {},
|
|
||||||
onActionMenuDropdownOpened: () => {},
|
|
||||||
onCellMouseEnter: () => {},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<RecordTableRowContextProvider
|
<RecordTableBodyContextProvider
|
||||||
value={{
|
value={{
|
||||||
objectNameSingular:
|
onUpsertRecord: () => {},
|
||||||
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
onOpenTableCell: () => {},
|
||||||
recordId: mockPerformance.recordId,
|
onMoveFocus: () => {},
|
||||||
rowIndex: 0,
|
onCloseTableCell: () => {},
|
||||||
pathToShowPage:
|
onMoveSoftFocusToCell: () => {},
|
||||||
getBasePathToShowPage({
|
onActionMenuDropdownOpened: () => {},
|
||||||
objectNameSingular:
|
onCellMouseEnter: () => {},
|
||||||
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
|
||||||
}) + mockPerformance.recordId,
|
|
||||||
isSelected: false,
|
|
||||||
isPendingRow: false,
|
|
||||||
inView: true,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RecordTableRowDraggableContextProvider
|
<RecordTableRowContextProvider
|
||||||
value={{
|
value={{
|
||||||
isDragging: false,
|
objectNameSingular:
|
||||||
dragHandleProps: null,
|
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
||||||
|
recordId: mockPerformance.recordId,
|
||||||
|
rowIndex: 0,
|
||||||
|
pathToShowPage:
|
||||||
|
getBasePathToShowPage({
|
||||||
|
objectNameSingular:
|
||||||
|
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
||||||
|
}) + mockPerformance.recordId,
|
||||||
|
isSelected: false,
|
||||||
|
isPendingRow: false,
|
||||||
|
inView: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RecordTableCellContext.Provider
|
<RecordTableRowDraggableContextProvider
|
||||||
value={{
|
value={{
|
||||||
columnDefinition: mockPerformance.fieldDefinition,
|
isDragging: false,
|
||||||
columnIndex: 0,
|
dragHandleProps: null,
|
||||||
cellPosition: { row: 0, column: 0 },
|
|
||||||
hasSoftFocus: false,
|
|
||||||
isInEditMode: false,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldContext.Provider
|
<RecordTableCellContext.Provider
|
||||||
value={{
|
value={{
|
||||||
recordId: mockPerformance.recordId,
|
columnDefinition: mockPerformance.fieldDefinition,
|
||||||
isLabelIdentifier: false,
|
columnIndex: 0,
|
||||||
fieldDefinition: {
|
cellPosition: { row: 0, column: 0 },
|
||||||
...mockPerformance.fieldDefinition,
|
hasSoftFocus: false,
|
||||||
},
|
isInEditMode: false,
|
||||||
hotkeyScope: 'hotkey-scope',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RelationFieldValueSetterEffect />
|
<FieldContext.Provider
|
||||||
<table>
|
value={{
|
||||||
<tbody>
|
recordId: mockPerformance.recordId,
|
||||||
<tr>
|
isLabelIdentifier: false,
|
||||||
<Story />
|
fieldDefinition: {
|
||||||
</tr>
|
...mockPerformance.fieldDefinition,
|
||||||
</tbody>
|
},
|
||||||
</table>
|
hotkeyScope: 'hotkey-scope',
|
||||||
</FieldContext.Provider>
|
}}
|
||||||
</RecordTableCellContext.Provider>
|
>
|
||||||
</RecordTableRowDraggableContextProvider>
|
<RelationFieldValueSetterEffect />
|
||||||
</RecordTableRowContextProvider>
|
<table>
|
||||||
</RecordTableBodyContextProvider>
|
<tbody>
|
||||||
</RecordTableComponentInstance>
|
<tr>
|
||||||
</RecordTableContextProvider>
|
<Story />
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</FieldContext.Provider>
|
||||||
|
</RecordTableCellContext.Provider>
|
||||||
|
</RecordTableRowDraggableContextProvider>
|
||||||
|
</RecordTableRowContextProvider>
|
||||||
|
</RecordTableBodyContextProvider>
|
||||||
|
</RecordTableComponentInstance>
|
||||||
|
</RecordTableContextProvider>
|
||||||
|
</RecordIndexContextProvider>
|
||||||
</RecordFieldValueSelectorContextProvider>
|
</RecordFieldValueSelectorContextProvider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||||
|
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||||
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
@ -25,6 +26,7 @@ const StyledSettingsSecurityOptionsList = styled.div`
|
|||||||
export const SettingsSecurityOptionsList = () => {
|
export const SettingsSecurityOptionsList = () => {
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const SSOIdentitiesProviders = useRecoilValue(SSOIdentitiesProvidersState);
|
const SSOIdentitiesProviders = useRecoilValue(SSOIdentitiesProvidersState);
|
||||||
|
const authProviders = useRecoilValue(authProvidersState);
|
||||||
|
|
||||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||||
currentWorkspaceState,
|
currentWorkspaceState,
|
||||||
@ -123,32 +125,38 @@ export const SettingsSecurityOptionsList = () => {
|
|||||||
{currentWorkspace && (
|
{currentWorkspace && (
|
||||||
<>
|
<>
|
||||||
<Card rounded>
|
<Card rounded>
|
||||||
<SettingsOptionCardContentToggle
|
{authProviders.google === true && (
|
||||||
Icon={IconGoogle}
|
<SettingsOptionCardContentToggle
|
||||||
title="Google"
|
Icon={IconGoogle}
|
||||||
description="Allow logins through Google's single sign-on functionality."
|
title="Google"
|
||||||
checked={currentWorkspace.isGoogleAuthEnabled}
|
description="Allow logins through Google's single sign-on functionality."
|
||||||
advancedMode
|
checked={currentWorkspace.isGoogleAuthEnabled}
|
||||||
divider
|
advancedMode
|
||||||
onChange={() => toggleAuthMethod('google')}
|
divider
|
||||||
/>
|
onChange={() => toggleAuthMethod('google')}
|
||||||
<SettingsOptionCardContentToggle
|
/>
|
||||||
Icon={IconMicrosoft}
|
)}
|
||||||
title="Microsoft"
|
{authProviders.microsoft === true && (
|
||||||
description="Allow logins through Microsoft's single sign-on functionality."
|
<SettingsOptionCardContentToggle
|
||||||
checked={currentWorkspace.isMicrosoftAuthEnabled}
|
Icon={IconMicrosoft}
|
||||||
advancedMode
|
title="Microsoft"
|
||||||
divider
|
description="Allow logins through Microsoft's single sign-on functionality."
|
||||||
onChange={() => toggleAuthMethod('microsoft')}
|
checked={currentWorkspace.isMicrosoftAuthEnabled}
|
||||||
/>
|
advancedMode
|
||||||
<SettingsOptionCardContentToggle
|
divider
|
||||||
Icon={IconPassword}
|
onChange={() => toggleAuthMethod('microsoft')}
|
||||||
title="Password"
|
/>
|
||||||
description="Allow users to sign in with an email and password."
|
)}
|
||||||
checked={currentWorkspace.isPasswordAuthEnabled}
|
{authProviders.password === true && (
|
||||||
advancedMode
|
<SettingsOptionCardContentToggle
|
||||||
onChange={() => toggleAuthMethod('password')}
|
Icon={IconPassword}
|
||||||
/>
|
title="Password"
|
||||||
|
description="Allow users to sign in with an email and password."
|
||||||
|
checked={currentWorkspace.isPasswordAuthEnabled}
|
||||||
|
advancedMode
|
||||||
|
onChange={() => toggleAuthMethod('password')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
<Card rounded>
|
<Card rounded>
|
||||||
<SettingsOptionCardContentToggle
|
<SettingsOptionCardContentToggle
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { createState } from 'twenty-ui';
|
||||||
|
|
||||||
|
import { AuthProviders } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export const workspaceAuthProvidersState = createState<AuthProviders>({
|
||||||
|
key: 'workspaceAuthProvidersState',
|
||||||
|
defaultValue: {
|
||||||
|
google: true,
|
||||||
|
magicLink: false,
|
||||||
|
password: true,
|
||||||
|
microsoft: false,
|
||||||
|
sso: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,10 +1,17 @@
|
|||||||
import { ClientConfig } from '~/generated-metadata/graphql';
|
import { CaptchaDriverType, ClientConfig } from '~/generated/graphql';
|
||||||
import { CaptchaDriverType } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export const mockedClientConfig: ClientConfig = {
|
export const mockedClientConfig: ClientConfig = {
|
||||||
signInPrefilled: true,
|
signInPrefilled: true,
|
||||||
isMultiWorkspaceEnabled: false,
|
isMultiWorkspaceEnabled: false,
|
||||||
isSSOEnabled: false,
|
isSSOEnabled: false,
|
||||||
|
authProviders: {
|
||||||
|
google: true,
|
||||||
|
magicLink: false,
|
||||||
|
password: true,
|
||||||
|
microsoft: false,
|
||||||
|
sso: [],
|
||||||
|
__typename: 'AuthProviders',
|
||||||
|
},
|
||||||
frontDomain: 'localhost',
|
frontDomain: 'localhost',
|
||||||
defaultSubdomain: 'app',
|
defaultSubdomain: 'app',
|
||||||
chromeExtensionId: 'MOCKED_EXTENSION_ID',
|
chromeExtensionId: 'MOCKED_EXTENSION_ID',
|
||||||
|
|||||||
@ -6,9 +6,10 @@ import { Repository } from 'typeorm';
|
|||||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
||||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
||||||
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
|
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
|
||||||
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
|
||||||
|
|
||||||
import { SwitchWorkspaceService } from './switch-workspace.service';
|
import { SwitchWorkspaceService } from './switch-workspace.service';
|
||||||
|
|
||||||
@ -50,6 +51,12 @@ describe('SwitchWorkspaceService', () => {
|
|||||||
saveDefaultWorkspaceIfUserHasAccessOrThrow: jest.fn(),
|
saveDefaultWorkspaceIfUserHasAccessOrThrow: jest.fn(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: EnvironmentService,
|
||||||
|
useValue: {
|
||||||
|
get: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
|||||||
@ -7,13 +7,15 @@ import {
|
|||||||
AuthException,
|
AuthException,
|
||||||
AuthExceptionCode,
|
AuthExceptionCode,
|
||||||
} from 'src/engine/core-modules/auth/auth.exception';
|
} from 'src/engine/core-modules/auth/auth.exception';
|
||||||
|
import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity';
|
||||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
||||||
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
|
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { AuthTokens } from 'src/engine/core-modules/auth/dto/token.entity';
|
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
|
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
|
||||||
|
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SwitchWorkspaceService {
|
export class SwitchWorkspaceService {
|
||||||
@ -25,6 +27,7 @@ export class SwitchWorkspaceService {
|
|||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly accessTokenService: AccessTokenService,
|
private readonly accessTokenService: AccessTokenService,
|
||||||
private readonly refreshTokenService: RefreshTokenService,
|
private readonly refreshTokenService: RefreshTokenService,
|
||||||
|
private readonly environmentService: EnvironmentService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async switchWorkspace(user: User, workspaceId: string) {
|
async switchWorkspace(user: User, workspaceId: string) {
|
||||||
@ -65,12 +68,23 @@ export class SwitchWorkspaceService {
|
|||||||
defaultWorkspace: workspace,
|
defaultWorkspace: workspace,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const systemEnabledProviders: AuthProviders = {
|
||||||
|
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
||||||
|
magicLink: false,
|
||||||
|
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
|
||||||
|
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
|
||||||
|
sso: [],
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: workspace.id,
|
id: workspace.id,
|
||||||
subdomain: workspace.subdomain,
|
subdomain: workspace.subdomain,
|
||||||
logo: workspace.logo,
|
logo: workspace.logo,
|
||||||
displayName: workspace.displayName,
|
displayName: workspace.displayName,
|
||||||
authProviders: getAuthProvidersByWorkspace(workspace),
|
authProviders: getAuthProvidersByWorkspace({
|
||||||
|
workspace,
|
||||||
|
systemEnabledProviders,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Field, ObjectType } from '@nestjs/graphql';
|
import { Field, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||||
|
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
class Billing {
|
class Billing {
|
||||||
@ -52,6 +53,9 @@ class ApiConfig {
|
|||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class ClientConfig {
|
export class ClientConfig {
|
||||||
|
@Field(() => AuthProviders, { nullable: false })
|
||||||
|
authProviders: AuthProviders;
|
||||||
|
|
||||||
@Field(() => Billing, { nullable: false })
|
@Field(() => Billing, { nullable: false })
|
||||||
billing: Billing;
|
billing: Billing;
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,13 @@ export class ClientConfigResolver {
|
|||||||
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
|
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
authProviders: {
|
||||||
|
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
||||||
|
magicLink: false,
|
||||||
|
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
|
||||||
|
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
|
||||||
|
sso: [],
|
||||||
|
},
|
||||||
isSSOEnabled: this.environmentService.get('AUTH_SSO_ENABLED'),
|
isSSOEnabled: this.environmentService.get('AUTH_SSO_ENABLED'),
|
||||||
signInPrefilled: this.environmentService.get('SIGN_IN_PREFILLED'),
|
signInPrefilled: this.environmentService.get('SIGN_IN_PREFILLED'),
|
||||||
isMultiWorkspaceEnabled: this.environmentService.get(
|
isMultiWorkspaceEnabled: this.environmentService.get(
|
||||||
|
|||||||
@ -23,19 +23,24 @@ export class DomainManagerService {
|
|||||||
|
|
||||||
getFrontUrl() {
|
getFrontUrl() {
|
||||||
let baseUrl: URL;
|
let baseUrl: URL;
|
||||||
|
const frontPort = this.environmentService.get('FRONT_PORT');
|
||||||
|
const frontDomain = this.environmentService.get('FRONT_DOMAIN');
|
||||||
|
const frontProtocol = this.environmentService.get('FRONT_PROTOCOL');
|
||||||
|
|
||||||
if (!this.environmentService.get('FRONT_DOMAIN')) {
|
const serverUrl = this.environmentService.get('SERVER_URL');
|
||||||
baseUrl = new URL(this.environmentService.get('SERVER_URL'));
|
|
||||||
|
if (!frontDomain) {
|
||||||
|
baseUrl = new URL(serverUrl);
|
||||||
} else {
|
} else {
|
||||||
baseUrl = new URL(
|
baseUrl = new URL(`${frontProtocol}://${frontDomain}`);
|
||||||
`${this.environmentService.get('FRONT_PROTOCOL')}://${this.environmentService.get('FRONT_DOMAIN')}`,
|
}
|
||||||
);
|
|
||||||
|
|
||||||
const port = this.environmentService.get('FRONT_PORT');
|
if (frontPort) {
|
||||||
|
baseUrl.port = frontPort.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (port) {
|
if (frontProtocol) {
|
||||||
baseUrl.port = port.toString();
|
baseUrl.protocol = frontProtocol;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
|
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
|
||||||
import { getAuthProvidersByWorkspace } from './getAuthProvidersByWorkspace';
|
|
||||||
|
|
||||||
describe('getAuthProvidersByWorkspace', () => {
|
describe('getAuthProvidersByWorkspace', () => {
|
||||||
const mockWorkspace = {
|
const mockWorkspace = {
|
||||||
isGoogleAuthEnabled: true,
|
isGoogleAuthEnabled: true,
|
||||||
@ -20,7 +19,14 @@ describe('getAuthProvidersByWorkspace', () => {
|
|||||||
|
|
||||||
it('should return correct auth providers for given workspace', () => {
|
it('should return correct auth providers for given workspace', () => {
|
||||||
const result = getAuthProvidersByWorkspace({
|
const result = getAuthProvidersByWorkspace({
|
||||||
...mockWorkspace,
|
workspace: mockWorkspace,
|
||||||
|
systemEnabledProviders: {
|
||||||
|
google: true,
|
||||||
|
magicLink: false,
|
||||||
|
password: true,
|
||||||
|
microsoft: true,
|
||||||
|
sso: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@ -42,8 +48,14 @@ describe('getAuthProvidersByWorkspace', () => {
|
|||||||
|
|
||||||
it('should handle workspace with no SSO providers', () => {
|
it('should handle workspace with no SSO providers', () => {
|
||||||
const result = getAuthProvidersByWorkspace({
|
const result = getAuthProvidersByWorkspace({
|
||||||
...mockWorkspace,
|
workspace: { ...mockWorkspace, workspaceSSOIdentityProviders: [] },
|
||||||
workspaceSSOIdentityProviders: [],
|
systemEnabledProviders: {
|
||||||
|
google: true,
|
||||||
|
magicLink: false,
|
||||||
|
password: true,
|
||||||
|
microsoft: true,
|
||||||
|
sso: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@ -57,8 +69,14 @@ describe('getAuthProvidersByWorkspace', () => {
|
|||||||
|
|
||||||
it('should disable Microsoft auth if isMicrosoftAuthEnabled is false', () => {
|
it('should disable Microsoft auth if isMicrosoftAuthEnabled is false', () => {
|
||||||
const result = getAuthProvidersByWorkspace({
|
const result = getAuthProvidersByWorkspace({
|
||||||
...mockWorkspace,
|
workspace: { ...mockWorkspace, isMicrosoftAuthEnabled: false },
|
||||||
isMicrosoftAuthEnabled: false,
|
systemEnabledProviders: {
|
||||||
|
google: true,
|
||||||
|
magicLink: false,
|
||||||
|
password: true,
|
||||||
|
microsoft: true,
|
||||||
|
sso: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@ -3,13 +3,12 @@ import {
|
|||||||
InternalServerError,
|
InternalServerError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||||
|
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util';
|
||||||
import {
|
import {
|
||||||
WorkspaceException,
|
WorkspaceException,
|
||||||
WorkspaceExceptionCode,
|
WorkspaceExceptionCode,
|
||||||
} from 'src/engine/core-modules/workspace/workspace.exception';
|
} from 'src/engine/core-modules/workspace/workspace.exception';
|
||||||
|
|
||||||
import { workspaceGraphqlApiExceptionHandler } from './workspaceGraphqlApiExceptionHandler';
|
|
||||||
|
|
||||||
describe('workspaceGraphqlApiExceptionHandler', () => {
|
describe('workspaceGraphqlApiExceptionHandler', () => {
|
||||||
it('should throw NotFoundError when WorkspaceExceptionCode is SUBDOMAIN_NOT_FOUND', () => {
|
it('should throw NotFoundError when WorkspaceExceptionCode is SUBDOMAIN_NOT_FOUND', () => {
|
||||||
const error = new WorkspaceException(
|
const error = new WorkspaceException(
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
|
||||||
|
export const getAuthProvidersByWorkspace = ({
|
||||||
|
workspace,
|
||||||
|
systemEnabledProviders,
|
||||||
|
}: {
|
||||||
|
workspace: Pick<
|
||||||
|
Workspace,
|
||||||
|
| 'isGoogleAuthEnabled'
|
||||||
|
| 'isPasswordAuthEnabled'
|
||||||
|
| 'isMicrosoftAuthEnabled'
|
||||||
|
| 'workspaceSSOIdentityProviders'
|
||||||
|
>;
|
||||||
|
systemEnabledProviders: AuthProviders;
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
google: workspace.isGoogleAuthEnabled && systemEnabledProviders.google,
|
||||||
|
magicLink: false,
|
||||||
|
password:
|
||||||
|
workspace.isPasswordAuthEnabled && systemEnabledProviders.password,
|
||||||
|
microsoft:
|
||||||
|
workspace.isMicrosoftAuthEnabled && systemEnabledProviders.microsoft,
|
||||||
|
sso: workspace.workspaceSSOIdentityProviders.map((identityProvider) => ({
|
||||||
|
id: identityProvider.id,
|
||||||
|
name: identityProvider.name,
|
||||||
|
type: identityProvider.type,
|
||||||
|
status: identityProvider.status,
|
||||||
|
issuer: identityProvider.issuer,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
|
|
||||||
export const getAuthProvidersByWorkspace = (workspace: Workspace) => {
|
|
||||||
return {
|
|
||||||
google: workspace.isGoogleAuthEnabled,
|
|
||||||
magicLink: false,
|
|
||||||
password: workspace.isPasswordAuthEnabled,
|
|
||||||
microsoft: workspace.isMicrosoftAuthEnabled,
|
|
||||||
sso: workspace.workspaceSSOIdentityProviders.map((identityProvider) => ({
|
|
||||||
id: identityProvider.id,
|
|
||||||
name: identityProvider.name,
|
|
||||||
type: identityProvider.type,
|
|
||||||
status: identityProvider.status,
|
|
||||||
issuer: identityProvider.issuer,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -23,10 +23,13 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use
|
|||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
|
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
|
||||||
import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output';
|
import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output';
|
||||||
import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
|
import {
|
||||||
|
AuthProviders,
|
||||||
|
PublicWorkspaceDataOutput,
|
||||||
|
} from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
|
||||||
import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input';
|
import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input';
|
||||||
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace';
|
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util';
|
||||||
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler';
|
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util';
|
||||||
import {
|
import {
|
||||||
WorkspaceException,
|
WorkspaceException,
|
||||||
WorkspaceExceptionCode,
|
WorkspaceExceptionCode,
|
||||||
@ -207,12 +210,23 @@ export class WorkspaceResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const systemEnabledProviders: AuthProviders = {
|
||||||
|
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
||||||
|
magicLink: false,
|
||||||
|
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
|
||||||
|
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
|
||||||
|
sso: [],
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: workspace.id,
|
id: workspace.id,
|
||||||
logo: workspaceLogoWithToken,
|
logo: workspaceLogoWithToken,
|
||||||
displayName: workspace.displayName,
|
displayName: workspace.displayName,
|
||||||
subdomain: workspace.subdomain,
|
subdomain: workspace.subdomain,
|
||||||
authProviders: getAuthProvidersByWorkspace(workspace),
|
authProviders: getAuthProvidersByWorkspace({
|
||||||
|
workspace,
|
||||||
|
systemEnabledProviders,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user