| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // @ts-nocheck
- import { zodResolver } from '@hookform/resolvers/zod'
- import { PermissionAction } from '@supabase/shared-types/out/constants'
- import { useParams } from 'common'
- import { useEffect } from 'react'
- import { useForm } from 'react-hook-form'
- import { toast } from 'sonner'
- import {
- Button,
- Card,
- CardContent,
- CardFooter,
- Form,
- FormControl,
- FormField,
- Switch,
- Tooltip,
- TooltipContent,
- TooltipTrigger,
- } from 'ui'
- import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
- import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
- import { z } from 'zod'
- import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
- import AlertError from '@/components/ui/AlertError'
- import { InlineLink } from '@/components/ui/InlineLink'
- import NoPermission from '@/components/ui/NoPermission'
- import { UpgradeToPro } from '@/components/ui/UpgradeToPro'
- import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query'
- import { useOrganizationMfaToggleMutation } from '@/data/organizations/organization-mfa-mutation'
- import { useOrganizationMfaQuery } from '@/data/organizations/organization-mfa-query'
- import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
- import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
- import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
- import { useProfile } from '@/lib/profile'
- const schema = z.object({
- enforceMfa: z.boolean(),
- })
- export const SecuritySettings = () => {
- const { slug } = useParams()
- const { profile } = useProfile()
- const { data: members } = useOrganizationMembersQuery({ slug })
- const { can: canReadMfaConfig, isLoading: isLoadingPermissions } = useAsyncCheckPermissions(
- PermissionAction.READ,
- 'organizations'
- )
- const { can: canUpdateMfaConfig } = useAsyncCheckPermissions(
- PermissionAction.UPDATE,
- 'organizations'
- )
- const { mutate: sendEvent } = useSendEventMutation()
- const { hasAccess: hasAccessToEnforceMfa, isLoading: isLoadingEntitlement } =
- useCheckEntitlements('security.enforce_mfa')
- const {
- data: mfaConfig,
- error: mfaError,
- isPending: isLoadingMfa,
- isError: isErrorMfa,
- isSuccess: isSuccessMfa,
- } = useOrganizationMfaQuery({ slug }, { enabled: hasAccessToEnforceMfa && canReadMfaConfig })
- const { mutate: toggleMfa, isPending: isUpdatingMfa } = useOrganizationMfaToggleMutation({
- onError: (error) => {
- toast.error(`Failed to update MFA enforcement: ${error.message}`)
- if (mfaConfig !== undefined) form.reset({ enforceMfa: mfaConfig })
- },
- onSuccess: (data) => {
- toast.success('Successfully updated organization MFA settings')
- sendEvent({
- action: 'organization_mfa_enforcement_updated',
- properties: {
- mfaEnforced: data.enforced,
- },
- groups: {
- organization: slug ?? 'Unknown',
- },
- })
- },
- })
- const form = useForm<z.infer<typeof schema>>({
- resolver: zodResolver(schema as any),
- defaultValues: {
- enforceMfa: false,
- },
- })
- useEffect(() => {
- if (mfaConfig !== undefined) {
- form.reset({ enforceMfa: mfaConfig })
- }
- }, [mfaConfig, form])
- const hasMFAEnabled =
- members?.find((member) => member.primary_email == profile?.primary_email)?.mfa_enabled || false
- const onSubmit = (values: { enforceMfa: boolean }) => {
- if (!slug || !hasAccessToEnforceMfa) return
- toggleMfa({ slug, setEnforced: values.enforceMfa })
- }
- return (
- <ScaffoldContainer size="small" className="px-6 xl:px-10">
- <ScaffoldSection isFullWidth>
- {!hasAccessToEnforceMfa && !isLoadingEntitlement ? (
- <UpgradeToPro
- source="organizationMfa"
- primaryText="Organization MFA enforcement is not available on Free Plan"
- secondaryText="Upgrade to Pro or above to enforce MFA requirements for your organization."
- featureProposition="enforce MFA requirements"
- />
- ) : (
- <>
- {isLoadingMfa || isLoadingPermissions || isLoadingEntitlement ? (
- <Card>
- <CardContent>
- <GenericSkeletonLoader />
- </CardContent>
- </Card>
- ) : !canReadMfaConfig ? (
- <NoPermission resourceText="view organization security settings" />
- ) : null}
- {(isErrorMfa || mfaError) && hasAccessToEnforceMfa && (
- <AlertError error={mfaError} subject="Failed to retrieve MFA enforcement status" />
- )}
- {isSuccessMfa && hasAccessToEnforceMfa && (
- <Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)}>
- <Card>
- <CardContent>
- <FormField
- control={form.control}
- name="enforceMfa"
- render={({ field }) => (
- <FormItemLayout
- layout="flex-row-reverse"
- label="Require MFA to access organization"
- description="Team members must have MFA enabled and a valid MFA session to access the organization and any projects."
- >
- <FormControl>
- <Tooltip>
- <TooltipTrigger asChild>
- <Switch
- checked={field.value}
- onCheckedChange={field.onChange}
- disabled={
- !hasAccessToEnforceMfa ||
- !canUpdateMfaConfig ||
- !hasMFAEnabled ||
- isUpdatingMfa
- }
- />
- </TooltipTrigger>
- {(!canUpdateMfaConfig || !hasMFAEnabled) && (
- <TooltipContent side="bottom">
- {!canUpdateMfaConfig ? (
- "You don't have permission to update MFA settings"
- ) : (
- <>
- <InlineLink href="/account/security">Enable MFA</InlineLink>{' '}
- on your own account first
- </>
- )}
- </TooltipContent>
- )}
- </Tooltip>
- </FormControl>
- </FormItemLayout>
- )}
- />
- </CardContent>
- <CardFooter className="justify-end space-x-2">
- {form.formState.isDirty && (
- <Button
- type="default"
- disabled={isLoadingMfa || isUpdatingMfa}
- onClick={() =>
- form.reset({ enforceMfa: hasAccessToEnforceMfa ? mfaConfig : false })
- }
- >
- Cancel
- </Button>
- )}
- <Button
- type="primary"
- htmlType="submit"
- disabled={
- !hasAccessToEnforceMfa ||
- !canUpdateMfaConfig ||
- isUpdatingMfa ||
- isLoadingMfa ||
- !form.formState.isDirty
- }
- loading={isUpdatingMfa}
- >
- Save changes
- </Button>
- </CardFooter>
- </Card>
- </form>
- </Form>
- )}
- </>
- )}
- </ScaffoldSection>
- </ScaffoldContainer>
- )
- }
|