mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
[FE-3493] fix(studio): respect role impersonation when copying truncated rows (#46442)
Copy/export of selected rows in the Table Editor refetches full values for cells truncated in the grid (via `getCellValue`), but that refetch was bypassing role impersonation. The main grid query respects the impersonated role; the truncated-cell hydration didn't, so the copy could fetch as the service role even when "View as <role>" was active – an inconsistency, since the UI still indicates the impersonated role is in effect. Threads `roleImpersonationState` through `hydrateTruncatedRows` → `getCellValue`, and wraps the SQL in `wrapWithRoleImpersonation` (matching how `getTableRows` does it). Addresses FE-3493. **Changed:** - `getCellValue` accepts an optional `roleImpersonationState` and wraps its SQL with `wrapWithRoleImpersonation` + flags `isRoleImpersonationEnabled` on `executeSql` - `hydrateTruncatedRows` threads `roleImpersonationState` through to `getCellValue` - `Header.tsx`'s `onCopyRows` passes the in-scope `roleImpersonationState` into `hydrateTruncatedRows` ## To test 1. Open the Table Editor on a table with a row containing a large/truncated string value and a primary key 2. Enable role impersonation → "View as role" → pick any role with read access to the table 3. Select the row, then `Copy → Copy as JSON` (also try CSV / SQL) 4. The copy should succeed and contain the full (non-truncated) value 5. Inspect the SQL request – it should now be wrapped with the impersonation context, matching how the main grid query is wrapped Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
This commit is contained in:
1 parent
cc35414ed6
commit
29af5308f3
3 files changed
+19
-2
No files matched your search
@@ -235,6 +235,7 @@ const RowHeader = ({ tableQueriesEnabled = true }: RowHeaderProps) => {
|
||||
table: snap.table,
|
||||
projectRef: project.ref,
|
||||
connectionString: project.connectionString ?? null,
|
||||
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
|
||||
})
|
||||
if (hydrated.status !== 'ok') {
|
||||
throw new Error('Failed to fetch full values for truncated cells')
|
||||
|
||||
@@ -3,6 +3,7 @@ import Papa from 'papaparse'
|
||||
import type { SupaTable } from '@/components/grid/types'
|
||||
import { isValueTruncated } from '@/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
|
||||
import { getCellValue } from '@/data/table-rows/get-cell-value-mutation'
|
||||
import type { RoleImpersonationState } from '@/lib/role-impersonation'
|
||||
|
||||
export const formatRowsForCSV = ({ rows, columns }: { rows: any[]; columns: string[] }) => {
|
||||
const formattedRows = rows.map((row) => {
|
||||
@@ -34,11 +35,13 @@ export const hydrateTruncatedRows = async ({
|
||||
table,
|
||||
projectRef,
|
||||
connectionString,
|
||||
roleImpersonationState,
|
||||
}: {
|
||||
rows: Record<string, unknown>[]
|
||||
table: SupaTable
|
||||
projectRef: string
|
||||
connectionString: string | null
|
||||
roleImpersonationState?: RoleImpersonationState
|
||||
}): Promise<HydrateTruncatedRowsResult> => {
|
||||
const jobs: { rowIdx: number; column: string }[] = []
|
||||
rows.forEach((row, rowIdx) => {
|
||||
@@ -73,6 +76,7 @@ export const hydrateTruncatedRows = async ({
|
||||
table: { schema: table.schema ?? 'public', name: table.name },
|
||||
column,
|
||||
pkMatch,
|
||||
roleImpersonationState,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useMutation } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { executeSql } from '@/data/sql/execute-sql-query'
|
||||
import { RoleImpersonationState, wrapWithRoleImpersonation } from '@/lib/role-impersonation'
|
||||
import { isRoleImpersonationEnabled } from '@/state/role-impersonation-state'
|
||||
import type { ResponseError, UseCustomMutationOptions } from '@/types'
|
||||
|
||||
export type GetCellValueVariables = {
|
||||
@@ -12,6 +14,7 @@ export type GetCellValueVariables = {
|
||||
table: { schema: string; name: string }
|
||||
column: string
|
||||
pkMatch: { [key: string]: any }
|
||||
roleImpersonationState?: RoleImpersonationState
|
||||
}
|
||||
|
||||
export function getCellValueSql({
|
||||
@@ -32,9 +35,18 @@ export async function getCellValue({
|
||||
table,
|
||||
column,
|
||||
pkMatch,
|
||||
roleImpersonationState,
|
||||
}: GetCellValueVariables) {
|
||||
const sql = getCellValueSql({ table, column, pkMatch })
|
||||
const { result } = await executeSql({ projectRef, connectionString, sql })
|
||||
const sql = wrapWithRoleImpersonation(
|
||||
getCellValueSql({ table, column, pkMatch }),
|
||||
roleImpersonationState
|
||||
)
|
||||
const { result } = await executeSql({
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql,
|
||||
isRoleImpersonationEnabled: isRoleImpersonationEnabled(roleImpersonationState?.role),
|
||||
})
|
||||
return result?.[0][column]
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user