11import { Injectable } from '@nestjs/common' ;
22
3- import { isDefined } from 'twenty-shared/utils' ;
43import { PermissionFlagType } from 'twenty-shared/constants' ;
4+ import { isDefined } from 'twenty-shared/utils' ;
55
66import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service' ;
77import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity' ;
@@ -79,44 +79,26 @@ export class ViewAccessService {
7979 workspaceId : string ,
8080 apiKeyId ?: string ,
8181 ) : Promise < boolean > {
82- // For WORKSPACE visibility views, check VIEWS permission
83- if ( visibility === ViewVisibility . WORKSPACE ) {
84- let hasViewsPermission = false ;
85-
86- if ( isDefined ( userWorkspaceId ) ) {
87- const permissions =
88- await this . permissionsService . getUserWorkspacePermissions ( {
89- userWorkspaceId,
90- workspaceId,
91- } ) ;
92-
93- hasViewsPermission =
94- permissions . permissionFlags [ PermissionFlagType . VIEWS ] ?? false ;
95- } else if ( isDefined ( apiKeyId ) ) {
96- hasViewsPermission =
97- await this . permissionsService . userHasWorkspaceSettingPermission ( {
98- workspaceId,
99- apiKeyId,
100- setting : PermissionFlagType . VIEWS ,
101- } ) ;
82+ // UNLISTED views can only be created by users (not API keys)
83+ if ( visibility === ViewVisibility . UNLISTED ) {
84+ if ( ! isDefined ( userWorkspaceId ) ) {
85+ this . throwCreatePermissionDenied ( ) ;
10286 }
10387
104- if ( ! hasViewsPermission ) {
105- throw new ViewException (
106- generateViewExceptionMessage (
107- ViewExceptionMessageKey . VIEW_CREATE_PERMISSION_DENIED ,
108- ) ,
109- ViewExceptionCode . VIEW_CREATE_PERMISSION_DENIED ,
110- {
111- userFriendlyMessage : generateViewUserFriendlyExceptionMessage (
112- ViewExceptionMessageKey . VIEW_CREATE_PERMISSION_DENIED ,
113- ) ,
114- } ,
115- ) ;
116- }
88+ return true ;
89+ }
90+
91+ // WORKSPACE visibility views require VIEWS permission
92+ const hasPermission = await this . hasViewsPermission (
93+ userWorkspaceId ,
94+ workspaceId ,
95+ apiKeyId ,
96+ ) ;
97+
98+ if ( ! hasPermission ) {
99+ this . throwCreatePermissionDenied ( ) ;
117100 }
118101
119- // For UNLISTED views, allow creation
120102 return true ;
121103 }
122104
@@ -126,50 +108,79 @@ export class ViewAccessService {
126108 workspaceId : string ,
127109 apiKeyId ?: string ,
128110 ) : Promise < boolean > {
129- let hasViewsPermission = false ;
111+ const hasPermission = await this . hasViewsPermission (
112+ userWorkspaceId ,
113+ workspaceId ,
114+ apiKeyId ,
115+ ) ;
116+
117+ if ( hasPermission ) {
118+ return true ;
119+ }
120+
121+ // Users without VIEWS permission can only manipulate their own unlisted views
122+ const isOwnUnlistedView =
123+ view . visibility === ViewVisibility . UNLISTED &&
124+ view . createdByUserWorkspaceId === userWorkspaceId ;
125+
126+ if ( isOwnUnlistedView ) {
127+ return true ;
128+ }
130129
130+ this . throwModifyPermissionDenied ( ) ;
131+ }
132+
133+ private async hasViewsPermission (
134+ userWorkspaceId : string | undefined ,
135+ workspaceId : string ,
136+ apiKeyId ?: string ,
137+ ) : Promise < boolean > {
131138 if ( isDefined ( userWorkspaceId ) ) {
132139 const permissions =
133140 await this . permissionsService . getUserWorkspacePermissions ( {
134141 userWorkspaceId,
135142 workspaceId,
136143 } ) ;
137144
138- hasViewsPermission =
139- permissions . permissionFlags [ PermissionFlagType . VIEWS ] ?? false ;
140- } else if ( isDefined ( apiKeyId ) ) {
141- hasViewsPermission =
142- await this . permissionsService . userHasWorkspaceSettingPermission ( {
143- workspaceId,
144- apiKeyId,
145- setting : PermissionFlagType . VIEWS ,
146- } ) ;
145+ return permissions . permissionFlags [ PermissionFlagType . VIEWS ] ?? false ;
147146 }
148147
149- // Users/API keys with VIEWS permission can manipulate all views
150- if ( hasViewsPermission ) {
151- return true ;
148+ if ( isDefined ( apiKeyId ) ) {
149+ return this . permissionsService . userHasWorkspaceSettingPermission ( {
150+ workspaceId,
151+ apiKeyId,
152+ setting : PermissionFlagType . VIEWS ,
153+ } ) ;
152154 }
153155
154- // Users without VIEWS permission can only manipulate their own unlisted views
155- const canAccess =
156- view . visibility === ViewVisibility . UNLISTED &&
157- view . createdByUserWorkspaceId === userWorkspaceId ;
156+ return false ;
157+ }
158158
159- if ( ! canAccess ) {
160- throw new ViewException (
161- generateViewExceptionMessage (
162- ViewExceptionMessageKey . VIEW_MODIFY_PERMISSION_DENIED ,
159+ private throwCreatePermissionDenied ( ) : never {
160+ throw new ViewException (
161+ generateViewExceptionMessage (
162+ ViewExceptionMessageKey . VIEW_CREATE_PERMISSION_DENIED ,
163+ ) ,
164+ ViewExceptionCode . VIEW_CREATE_PERMISSION_DENIED ,
165+ {
166+ userFriendlyMessage : generateViewUserFriendlyExceptionMessage (
167+ ViewExceptionMessageKey . VIEW_CREATE_PERMISSION_DENIED ,
163168 ) ,
164- ViewExceptionCode . VIEW_MODIFY_PERMISSION_DENIED ,
165- {
166- userFriendlyMessage : generateViewUserFriendlyExceptionMessage (
167- ViewExceptionMessageKey . VIEW_MODIFY_PERMISSION_DENIED ,
168- ) ,
169- } ,
170- ) ;
171- }
169+ } ,
170+ ) ;
171+ }
172172
173- return true ;
173+ private throwModifyPermissionDenied ( ) : never {
174+ throw new ViewException (
175+ generateViewExceptionMessage (
176+ ViewExceptionMessageKey . VIEW_MODIFY_PERMISSION_DENIED ,
177+ ) ,
178+ ViewExceptionCode . VIEW_MODIFY_PERMISSION_DENIED ,
179+ {
180+ userFriendlyMessage : generateViewUserFriendlyExceptionMessage (
181+ ViewExceptionMessageKey . VIEW_MODIFY_PERMISSION_DENIED ,
182+ ) ,
183+ } ,
184+ ) ;
174185 }
175186}
0 commit comments