feat(sso): allow to use OIDC and SAML (#7246)
## What it does ### Backend - [x] Add a mutation to create OIDC and SAML configuration - [x] Add a mutation to delete an SSO config - [x] Add a feature flag to toggle SSO - [x] Add a mutation to activate/deactivate an SSO config - [x] Add a mutation to delete an SSO config - [x] Add strategy to use OIDC or SAML - [ ] Improve error management ### Frontend - [x] Add section "security" in settings - [x] Add page to list SSO configurations - [x] Add page and forms to create OIDC or SAML configuration - [x] Add field to "connect with SSO" in the signin/signup process - [x] Trigger auth when a user switch to a workspace with SSO enable - [x] Add an option on the security page to activate/deactivate the global invitation link - [ ] Add new Icons for SSO Identity Providers (okta, Auth0, Azure, Microsoft) --------- Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,36 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { useCreateWorkspaceInvitation } from '@/workspace-invitation/hooks/useCreateWorkspaceInvitation';
|
||||
|
||||
const mutationSendInvitationsCallSpy = jest.fn();
|
||||
|
||||
jest.mock('~/generated/graphql', () => ({
|
||||
useSendInvitationsMutation: () => [mutationSendInvitationsCallSpy],
|
||||
}));
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useCreateWorkspaceInvitation', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('Send invitations', async () => {
|
||||
const invitationParams = { emails: ['test@twenty.com'] };
|
||||
renderHook(
|
||||
() => {
|
||||
const { sendInvitation } = useCreateWorkspaceInvitation();
|
||||
sendInvitation(invitationParams);
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(mutationSendInvitationsCallSpy).toHaveBeenCalledWith({
|
||||
onCompleted: expect.any(Function),
|
||||
variables: invitationParams,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { useDeleteWorkspaceInvitation } from '@/workspace-invitation/hooks/useDeleteWorkspaceInvitation';
|
||||
|
||||
const mutationDeleteWorspaceInvitationCallSpy = jest.fn();
|
||||
|
||||
jest.mock('~/generated/graphql', () => ({
|
||||
useDeleteWorkspaceInvitationMutation: () => [
|
||||
mutationDeleteWorspaceInvitationCallSpy,
|
||||
],
|
||||
}));
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useDeleteWorkspaceInvitation', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('Delete Workspace Invitation', async () => {
|
||||
const params = { appTokenId: 'test' };
|
||||
renderHook(
|
||||
() => {
|
||||
const { deleteWorkspaceInvitation } = useDeleteWorkspaceInvitation();
|
||||
deleteWorkspaceInvitation(params);
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(mutationDeleteWorspaceInvitationCallSpy).toHaveBeenCalledWith({
|
||||
onCompleted: expect.any(Function),
|
||||
variables: params,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { useResendWorkspaceInvitation } from '@/workspace-invitation/hooks/useResendWorkspaceInvitation';
|
||||
|
||||
const mutationResendWorspaceInvitationCallSpy = jest.fn();
|
||||
|
||||
jest.mock('~/generated/graphql', () => ({
|
||||
useResendWorkspaceInvitationMutation: () => [
|
||||
mutationResendWorspaceInvitationCallSpy,
|
||||
],
|
||||
}));
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useResendWorkspaceInvitation', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('Resend Workspace Invitation', async () => {
|
||||
const params = { appTokenId: 'test' };
|
||||
renderHook(
|
||||
() => {
|
||||
const { resendInvitation } = useResendWorkspaceInvitation();
|
||||
resendInvitation(params);
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(mutationResendWorspaceInvitationCallSpy).toHaveBeenCalledWith({
|
||||
onCompleted: expect.any(Function),
|
||||
variables: params,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,8 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useSendInvitationsMutation } from '~/generated/graphql';
|
||||
import { SendInvitationsMutationVariables } from '../../../generated/graphql';
|
||||
import {
|
||||
useSendInvitationsMutation,
|
||||
SendInvitationsMutationVariables,
|
||||
} from '~/generated/graphql';
|
||||
import { workspaceInvitationsState } from '../states/workspaceInvitationsStates';
|
||||
|
||||
export const useCreateWorkspaceInvitation = () => {
|
||||
|
||||
Reference in New Issue
Block a user