@@ -6,13 +6,91 @@ import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
66
77const logger = createLogger ( 'TableUtils' )
88
9+ export interface TableAccessResult {
10+ hasAccess : true
11+ table : TableDefinition
12+ }
13+
14+ export interface TableAccessDenied {
15+ hasAccess : false
16+ notFound ?: boolean
17+ reason ?: string
18+ }
19+
20+ export type TableAccessCheck = TableAccessResult | TableAccessDenied
21+
922export type AccessResult = { ok : true ; table : TableDefinition } | { ok : false ; status : 404 | 403 }
1023
1124export interface ApiErrorResponse {
1225 error : string
1326 details ?: unknown
1427}
1528
29+ /**
30+ * Check if a user has read access to a table.
31+ * Read access is granted if:
32+ * 1. User created the table, OR
33+ * 2. User has any permission on the table's workspace (read, write, or admin)
34+ *
35+ * Follows the same pattern as Knowledge Base access checks.
36+ */
37+ export async function checkTableAccess ( tableId : string , userId : string ) : Promise < TableAccessCheck > {
38+ const table = await getTableById ( tableId )
39+
40+ if ( ! table ) {
41+ return { hasAccess : false , notFound : true }
42+ }
43+
44+ // Case 1: User created the table
45+ if ( table . createdBy === userId ) {
46+ return { hasAccess : true , table }
47+ }
48+
49+ // Case 2: Table belongs to a workspace the user has permissions for
50+ const userPermission = await getUserEntityPermissions ( userId , 'workspace' , table . workspaceId )
51+ if ( userPermission !== null ) {
52+ return { hasAccess : true , table }
53+ }
54+
55+ return { hasAccess : false , reason : 'User does not have access to this table' }
56+ }
57+
58+ /**
59+ * Check if a user has write access to a table.
60+ * Write access is granted if:
61+ * 1. User created the table, OR
62+ * 2. User has write or admin permissions on the table's workspace
63+ *
64+ * Follows the same pattern as Knowledge Base write access checks.
65+ */
66+ export async function checkTableWriteAccess (
67+ tableId : string ,
68+ userId : string
69+ ) : Promise < TableAccessCheck > {
70+ const table = await getTableById ( tableId )
71+
72+ if ( ! table ) {
73+ return { hasAccess : false , notFound : true }
74+ }
75+
76+ // Case 1: User created the table
77+ if ( table . createdBy === userId ) {
78+ return { hasAccess : true , table }
79+ }
80+
81+ // Case 2: Table belongs to a workspace and user has write/admin permissions
82+ const userPermission = await getUserEntityPermissions ( userId , 'workspace' , table . workspaceId )
83+ if ( userPermission === 'write' || userPermission === 'admin' ) {
84+ return { hasAccess : true , table }
85+ }
86+
87+ return { hasAccess : false , reason : 'User does not have write access to this table' }
88+ }
89+
90+ /**
91+ * @deprecated Use checkTableAccess or checkTableWriteAccess instead.
92+ * Legacy access check function for backwards compatibility.
93+ */
1694export async function checkAccess (
1795 tableId : string ,
1896 userId : string ,
@@ -48,6 +126,21 @@ export function accessError(
48126 return NextResponse . json ( { error : message } , { status : result . status } )
49127}
50128
129+ /**
130+ * Converts a TableAccessDenied result to an appropriate HTTP response.
131+ * Use with checkTableAccess or checkTableWriteAccess.
132+ */
133+ export function tableAccessError (
134+ result : TableAccessDenied ,
135+ requestId : string ,
136+ context ?: string
137+ ) : NextResponse {
138+ const status = result . notFound ? 404 : 403
139+ const message = result . notFound ? 'Table not found' : ( result . reason ?? 'Access denied' )
140+ logger . warn ( `[${ requestId } ] ${ message } ${ context ? `: ${ context } ` : '' } ` )
141+ return NextResponse . json ( { error : message } , { status } )
142+ }
143+
51144export async function verifyTableWorkspace ( tableId : string , workspaceId : string ) : Promise < boolean > {
52145 const table = await getTableById ( tableId )
53146 return table ?. workspaceId === workspaceId
0 commit comments