Files
supabase/apps/studio/components/interfaces/Database/Policies/PolicyTableRow/PolicyTableRow.utils.test.ts
Joshen Lim 1de298ff31 Reinstate https://github.com/supabase/supabase/pull/45143 into latest master (#47433)
## Context

Previous PR was [here](https://github.com/supabase/supabase/pull/45143)
but it got stale with lots of conflicts so figured it'll be easier redo
it off the latest master

Moves policies page from Auth to Database under an Access Control
section along with Roles. This moves all existing files, applies
redirects, and updates urls to point to the new route

<img width="274" height="412" alt="image"
src="https://github.com/user-attachments/assets/7952c185-64ae-4355-ba36-45397efe1787"
/>

<img width="453" height="471" alt="image"
src="https://github.com/user-attachments/assets/04b3dcb3-48a5-4049-9893-d01109fb46a9"
/>


## To test
- [ ] Verify that policies now live under Database correctly

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a quick navigation shortcut to open **Database > Policies
(RLS)**.
* **Bug Fixes**
* Updated Policies and RLS-related links across the product to open the
**Database policies** area (menus, command palette, context actions,
alerts, and link-outs).
* Added a permanent redirect from the old **auth policies** URL to the
new **database policies** URL.
* **Documentation**
* Updated RLS Dashboard and security checklist instructions to reference
**Database > Policies**.
* **Tests**
  * Adjusted automated tests to validate the new Policies route.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 18:49:33 +08:00

185 lines
6.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import { getTableAdmonitionMessage, getTableDataApiStatus } from './PolicyTableRow.utils'
import type { TableApiAccessData } from '@/data/privileges/table-api-access-query'
import type { ApiPrivilegesByRole } from '@/lib/data-api-types'
const FULL_PRIVILEGES: ApiPrivilegesByRole = {
anon: ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
authenticated: ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
service_role: ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
}
const PARTIAL_PRIVILEGES: ApiPrivilegesByRole = {
anon: ['SELECT'],
authenticated: [],
service_role: ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
}
const grantedAccess: TableApiAccessData = {
apiAccessType: 'access',
grantStatus: 'granted',
privileges: FULL_PRIVILEGES,
}
const customAccess: TableApiAccessData = {
apiAccessType: 'access',
grantStatus: 'custom',
privileges: PARTIAL_PRIVILEGES,
}
const noGrants: TableApiAccessData = { apiAccessType: 'exposed-schema-no-grants' }
const schemaNotExposedData: TableApiAccessData = { apiAccessType: 'none' }
describe('getTableDataApiStatus', () => {
it('returns schema-not-exposed when the schema is not in the exposed list', () => {
const status = getTableDataApiStatus({
isSchemaExposed: false,
apiAccessData: grantedAccess,
isRLSEnabled: true,
policiesCount: 1,
})
expect(status).toBe('schema-not-exposed')
})
it('returns no-grants when schema is exposed but no API roles have privileges', () => {
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: noGrants,
isRLSEnabled: true,
policiesCount: 0,
})
expect(status).toBe('no-grants')
})
it('returns custom-grants for partial/non-standard grants — even if RLS is off', () => {
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: customAccess,
isRLSEnabled: false,
policiesCount: 0,
})
expect(status).toBe('custom-grants')
})
it('returns publicly-readable when fully granted and RLS is off', () => {
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: grantedAccess,
isRLSEnabled: false,
policiesCount: 3,
})
expect(status).toBe('publicly-readable')
})
it('returns locked-by-rls when fully granted + RLS on + no policies', () => {
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: grantedAccess,
isRLSEnabled: true,
policiesCount: 0,
})
expect(status).toBe('locked-by-rls')
})
it('returns secured when fully granted + RLS on + policies exist', () => {
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: grantedAccess,
isRLSEnabled: true,
policiesCount: 2,
})
expect(status).toBe('secured')
})
it('returns unknown when apiAccessData is still loading or errored', () => {
// apiAccessData is undefined during loading AND on query error (isPending flips false
// but data stays undefined). We must not fall through to 'schema-not-exposed' — that
// would tell the user to reconfigure API settings for a schema that is in fact exposed.
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: undefined,
isRLSEnabled: true,
policiesCount: 0,
})
expect(status).toBe('unknown')
})
it('returns unknown when apiAccessData reports apiAccessType=none on an exposed schema', () => {
// Defensive: the query shouldn't emit apiAccessType=none when schema is exposed,
// but if it does we still don't want the false "schema not exposed" admonition.
const status = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: schemaNotExposedData,
isRLSEnabled: true,
policiesCount: 0,
})
expect(status).toBe('unknown')
})
it('isSchemaExposed=false wins over any apiAccessData value', () => {
const status = getTableDataApiStatus({
isSchemaExposed: false,
apiAccessData: noGrants,
isRLSEnabled: true,
policiesCount: 0,
})
expect(status).toBe('schema-not-exposed')
})
it('custom-grants wins over RLS state — we never claim public-readable for partial grants', () => {
const rlsOff = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: customAccess,
isRLSEnabled: false,
policiesCount: 0,
})
const rlsOnNoPolicies = getTableDataApiStatus({
isSchemaExposed: true,
apiAccessData: customAccess,
isRLSEnabled: true,
policiesCount: 0,
})
expect(rlsOff).toBe('custom-grants')
expect(rlsOnNoPolicies).toBe('custom-grants')
})
})
describe('getTableAdmonitionMessage', () => {
it('returns the custom-grants copy', () => {
expect(getTableAdmonitionMessage('custom-grants')).toBe(
'This table has custom Data API permissions — access may be restricted for some roles or operations.'
)
})
it('returns the no-grants copy', () => {
expect(getTableAdmonitionMessage('no-grants')).toBe(
'This table cannot be accessed via the Data API. Enable access in your project’s Data API settings.'
)
})
it('returns the publicly-readable copy', () => {
expect(getTableAdmonitionMessage('publicly-readable')).toBe(
'This table can be accessed by anyone via the Data API as RLS is disabled.'
)
})
it('returns the locked-by-rls copy', () => {
expect(getTableAdmonitionMessage('locked-by-rls')).toBe(
'No data will be returned via the Data API as no RLS policies exist on this table.'
)
})
it('returns null for secured — no admonition needed', () => {
expect(getTableAdmonitionMessage('secured')).toBeNull()
})
it('returns null for schema-not-exposed — handled by a separate admonition with a link', () => {
expect(getTableAdmonitionMessage('schema-not-exposed')).toBeNull()
})
it('returns null for unknown — caller should stay silent during loading/errored state', () => {
expect(getTableAdmonitionMessage('unknown')).toBeNull()
})
})