diff --git a/studio/components/interfaces/Auth/Policies/Policies.utils.ts b/studio/components/interfaces/Auth/Policies/Policies.utils.ts index 563d08d7dac..ec00c22466d 100644 --- a/studio/components/interfaces/Auth/Policies/Policies.utils.ts +++ b/studio/components/interfaces/Auth/Policies/Policies.utils.ts @@ -147,7 +147,8 @@ export const createPayloadForUpdatePolicy = ( payload.check = formattedPolicyFormFields.check || undefined } if (!isEqual(formattedPolicyFormFields.roles, originalPolicyFormFields.roles)) { - payload.roles = formattedPolicyFormFields.roles || undefined + if (formattedPolicyFormFields.roles.length === 0) payload.roles = ['public'] + else payload.roles = formattedPolicyFormFields.roles || undefined } return payload diff --git a/studio/components/to-be-cleaned/ProductEmptyState.tsx b/studio/components/to-be-cleaned/ProductEmptyState.tsx index 61d02ad2287..22b4feeca52 100644 --- a/studio/components/to-be-cleaned/ProductEmptyState.tsx +++ b/studio/components/to-be-cleaned/ProductEmptyState.tsx @@ -4,6 +4,7 @@ import * as Tooltip from '@radix-ui/react-tooltip' interface Props { title?: string + size?: 'medium' | 'large' children?: ReactNode ctaButtonLabel?: string infoButtonLabel?: string @@ -15,6 +16,7 @@ interface Props { const ProductEmptyState: FC = ({ title = '', + size = 'medium', children, ctaButtonLabel = '', infoButtonLabel = '', @@ -30,7 +32,7 @@ const ProductEmptyState: FC = ({
{/* A graphic can probably be placed here as a sibling to the div below*/}
-
+
{title}
{children}
{hasAction && ( diff --git a/studio/pages/project/[ref]/auth/policies.tsx b/studio/pages/project/[ref]/auth/policies.tsx index 5942bbd54e7..0904cddb03b 100644 --- a/studio/pages/project/[ref]/auth/policies.tsx +++ b/studio/pages/project/[ref]/auth/policies.tsx @@ -1,6 +1,7 @@ -import React, { useState, PropsWithChildren, FC } from 'react' +import React, { useState, FC } from 'react' import { isEmpty } from 'lodash' -import { Button, IconSearch, Input } from '@supabase/ui' +import { useRouter } from 'next/router' +import { Button, IconHelpCircle, IconSearch, Input } from '@supabase/ui' import { observer } from 'mobx-react-lite' import { checkPermissions, useStore } from 'hooks' import { AuthLayout } from 'components/layouts' @@ -14,6 +15,8 @@ import NoTableState from 'components/ui/States/NoTableState' import NoSearchResults from 'components/to-be-cleaned/NoSearchResults' import { PermissionAction } from '@supabase/shared-types/out/constants' import NoPermission from 'components/ui/NoPermission' +import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' +import InformationBox from 'components/ui/InformationBox' /** * Filter tables by table name and policy name @@ -50,6 +53,7 @@ const onFilterTables = ( const AuthPoliciesPage: NextPageWithLayout = () => { const { meta } = useStore() + const [policiesFilter, setPoliciesFilter] = useState(undefined) const publicTables = meta.tables.list((table: { schema: string }) => table.schema === 'public') const policies = meta.policies.list() @@ -62,31 +66,31 @@ const AuthPoliciesPage: NextPageWithLayout = () => { } return ( - <> -
-
- setPoliciesFilter(e.target.value)} - icon={} - /> - +
+ {(publicTables || []).length > 0 && ( +
+
+ setPoliciesFilter(e.target.value)} + icon={} + /> + +
-
-
- 0} tables={filteredTables} /> -
- + )} + 0} tables={filteredTables} /> +
) } @@ -103,6 +107,9 @@ interface AuthPoliciesTablesProps { tables: any[] } const AuthPoliciesTables: FC = observer(({ tables, hasPublicTables }) => { + const router = useRouter() + const { ref } = router.query + const { ui, meta } = useStore() const roles = meta.roles.list((role: PostgresRole) => !meta.roles.systemRoles.includes(role.name)) @@ -198,7 +205,7 @@ const AuthPoliciesTables: FC = observer(({ tables, hasP } return ( -
+ <> {tables.length > 0 ? ( tables.map((table: any) => (
@@ -214,7 +221,39 @@ const AuthPoliciesTables: FC = observer(({ tables, hasP ) : hasPublicTables ? ( ) : ( - +
+ router.push(`/project/${ref}/editor`)} + > +
+ } + description={ +
+

+ Policies restrict, on a per-user basis, which rows can be returned by normal + queries, or inserted, updated, or deleted by data modification commands. +

+

+ This is also known as Row-Level Security (RLS). Each policy is attached to a + table, and the policy is executed everytime a time is accessed. +

+
+ } + /> +

+ A table within the public schema is required before you can create a Row-Level + Security policy. +

+
+
+
)} = observer(({ tables, hasP onSelectCancel={closeConfirmModal} onSelectConfirm={onToggleRLS} /> -
+ ) })