mirror of
https://github.com/supabase/supabase.git
synced 2026-06-20 13:12:17 +08:00
* index advisor * support button type * supasquad confirmation * reset table * reset table * out of date notice * copywriting * fix inline flow * nit * button type * fix type error * rabbit * fix descendants issue * clearer copy * remove redundant classes * fix conflict with button type
210 lines
7.9 KiB
TypeScript
210 lines
7.9 KiB
TypeScript
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||
import { useState } from 'react'
|
||
import { toast } from 'sonner'
|
||
|
||
import { useParams } from 'common'
|
||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||
import { TextConfirmModal } from 'components/ui/TextConfirmModalWrapper'
|
||
import { useToggleLegacyAPIKeysMutation } from 'data/api-keys/legacy-api-key-toggle-mutation'
|
||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||
import { useLegacyJWTSigningKeyQuery } from 'data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
||
import { useAuthorizedAppsQuery } from 'data/oauth/authorized-apps-query'
|
||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||
import {
|
||
AlertDialog,
|
||
AlertDialogAction,
|
||
AlertDialogCancel,
|
||
AlertDialogContent,
|
||
AlertDialogDescription,
|
||
AlertDialogFooter,
|
||
AlertDialogHeader,
|
||
AlertDialogTitle,
|
||
} from 'ui'
|
||
import Panel from '../Panel'
|
||
|
||
export const ToggleLegacyApiKeysPanel = () => {
|
||
const { ref: projectRef } = useParams()
|
||
const { data: org } = useSelectedOrganizationQuery()
|
||
|
||
const [isConfirmOpen, setIsConfirmOpen] = useState(false)
|
||
const [isAppsWarningOpen, setIsAppsWarningOpen] = useState(false)
|
||
|
||
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
|
||
const { can: canUpdateAPIKeys, isSuccess: isPermissionsSuccess } = useAsyncCheckPermissions(
|
||
PermissionAction.SECRETS_WRITE,
|
||
'*'
|
||
)
|
||
|
||
const { data: legacyAPIKeysStatusData, isSuccess: isLegacyAPIKeysStatusSuccess } =
|
||
useLegacyAPIKeysStatusQuery({ projectRef }, { enabled: canReadAPIKeys })
|
||
|
||
const { data: legacyJWTSecret } = useLegacyJWTSigningKeyQuery(
|
||
{ projectRef },
|
||
{ enabled: canReadAPIKeys }
|
||
)
|
||
|
||
const { data: authorizedApps = [], isSuccess: isAuthorizedAppsSuccess } = useAuthorizedAppsQuery({
|
||
slug: org?.slug,
|
||
})
|
||
|
||
const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {}
|
||
|
||
if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess && isAuthorizedAppsSuccess)) {
|
||
return null
|
||
}
|
||
|
||
return (
|
||
<section>
|
||
<Panel>
|
||
<Panel.Content>
|
||
<div className="flex justify-between">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-sm">
|
||
{isLegacyKeysEnabled ? 'Disable legacy API keys' : 'Re-enabling legacy API keys'}
|
||
</p>
|
||
<p className="text-foreground-light text-sm">
|
||
{isLegacyKeysEnabled
|
||
? 'Make sure you are no longer using your legacy API keys before proceeding.'
|
||
: 'We recommend you use the new API keys whenever possible, but re-enabling is an option.'}
|
||
</p>
|
||
</div>
|
||
<div className="flex items-center">
|
||
<ButtonTooltip
|
||
type="default"
|
||
onClick={
|
||
authorizedApps?.length
|
||
? () => setIsAppsWarningOpen(true)
|
||
: () => setIsConfirmOpen(true)
|
||
}
|
||
disabled={
|
||
!canUpdateAPIKeys ||
|
||
(!isLegacyKeysEnabled && legacyJWTSecret?.status === 'revoked')
|
||
}
|
||
tooltip={{
|
||
content: {
|
||
side: 'bottom',
|
||
text: !canUpdateAPIKeys
|
||
? 'You need additional permissions to enable or disable JWT-based API keys'
|
||
: !isLegacyKeysEnabled && legacyJWTSecret?.status === 'revoked'
|
||
? 'The legacy JWT secret is revoked. Re-enabling is not possible until it is at least moved to previously used.'
|
||
: undefined,
|
||
},
|
||
}}
|
||
>
|
||
{legacyAPIKeysStatusData.enabled
|
||
? 'Disable JWT-based API keys'
|
||
: 'Re-enable JWT-based API keys'}
|
||
</ButtonTooltip>
|
||
</div>
|
||
</div>
|
||
</Panel.Content>
|
||
</Panel>
|
||
|
||
<ToggleApiKeysModal
|
||
visible={isConfirmOpen}
|
||
onClose={() => setIsConfirmOpen(false)}
|
||
legacyAPIKeysStatusData={legacyAPIKeysStatusData}
|
||
/>
|
||
|
||
<AlertDialog open={isAppsWarningOpen} onOpenChange={(value) => setIsAppsWarningOpen(value)}>
|
||
<AlertDialogContent>
|
||
<AlertDialogHeader>
|
||
<AlertDialogTitle>Apps using Supabase may break</AlertDialogTitle>
|
||
<AlertDialogDescription>
|
||
Your project uses apps that integrate with Supabase. Disabling the legacy API keys is
|
||
a brand new feature and the apps you’re using may not have added support for this yet.
|
||
It can cause them to stop functioning. Check before continuing.
|
||
</AlertDialogDescription>
|
||
</AlertDialogHeader>
|
||
<AlertDialogFooter>
|
||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||
<AlertDialogAction variant="danger" onClick={() => setIsConfirmOpen(true)}>
|
||
Disable API keys
|
||
</AlertDialogAction>
|
||
</AlertDialogFooter>
|
||
</AlertDialogContent>
|
||
</AlertDialog>
|
||
</section>
|
||
)
|
||
}
|
||
|
||
const ToggleApiKeysModal = ({
|
||
visible,
|
||
onClose,
|
||
legacyAPIKeysStatusData,
|
||
}: {
|
||
visible: boolean
|
||
onClose: () => void
|
||
legacyAPIKeysStatusData: { enabled: boolean }
|
||
}) => {
|
||
const { ref: projectRef } = useParams()
|
||
const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {}
|
||
|
||
const { mutate: toggleLegacyAPIKey, isPending: isTogglingLegacyAPIKey } =
|
||
useToggleLegacyAPIKeysMutation()
|
||
|
||
const onToggleLegacyAPIKeysEnabled = () => {
|
||
const enabled = !legacyAPIKeysStatusData?.enabled
|
||
|
||
toggleLegacyAPIKey(
|
||
{ projectRef, enabled },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success(
|
||
enabled
|
||
? 'Your anon and service_role keys have been re-enabled!'
|
||
: 'Your anon and service_role keys have been disabled!'
|
||
)
|
||
onClose()
|
||
},
|
||
}
|
||
)
|
||
}
|
||
|
||
return (
|
||
<TextConfirmModal
|
||
size="medium"
|
||
visible={visible}
|
||
onCancel={() => onClose()}
|
||
onConfirm={onToggleLegacyAPIKeysEnabled}
|
||
title={isLegacyKeysEnabled ? 'Disable JWT-based keys' : 'Re-enable JWT-based keys'}
|
||
confirmString={isLegacyKeysEnabled ? 'disable' : 're-enable'}
|
||
confirmLabel={`Confirm to ${isLegacyKeysEnabled ? 'disable' : 're-enable'} anon and service_role`}
|
||
confirmPlaceholder={isLegacyKeysEnabled ? 'disable' : 're-enable'}
|
||
loading={isTogglingLegacyAPIKey}
|
||
variant={isLegacyKeysEnabled ? 'destructive' : 'default'}
|
||
alert={
|
||
isLegacyKeysEnabled
|
||
? {
|
||
title: 'Ensure legacy keys are no longer in use before disabling',
|
||
description: (
|
||
<span className="prose text-sm">
|
||
Disabling <code>anon</code> and <code>service_role</code> keys while they are in
|
||
use will cause downtime for your application. Ensure they are no longer in use
|
||
before proceeding. If you have not created a publishable and at least one secret
|
||
API key, some dashboard functionality may become unavailable.
|
||
<br />
|
||
<br />
|
||
<span className="text-danger">
|
||
This disables API keys when used in the <code>apikey</code> header. They remain
|
||
valid as a JWT.
|
||
</span>
|
||
</span>
|
||
),
|
||
}
|
||
: {
|
||
title: 'Publishable and secret keys are preferred',
|
||
description: (
|
||
<span className="prose text-sm">
|
||
Re-enabling <code>anon</code> and <code>service_role</code> keys may be
|
||
appropriate in certain cases, but using a publishable and secret key is more
|
||
secure. We recommend against re-enabling legacy API keys.
|
||
</span>
|
||
),
|
||
}
|
||
}
|
||
/>
|
||
)
|
||
}
|