@@ -10,12 +10,14 @@ import { BUILTIN_INSTLLATION_ADMIN_USER_ID } from "@gitpod/gitpod-db/lib";
1010import { TeamMemberRole } from "@gitpod/gitpod-protocol" ;
1111import { ApplicationError , ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
1212import {
13+ AllResourceTypes ,
1314 OrganizationPermission ,
1415 Permission ,
1516 ProjectPermission ,
1617 Relation ,
1718 ResourceType ,
1819 UserPermission ,
20+ WorkspacePermission ,
1921 rel ,
2022} from "./definitions" ;
2123import { SpiceDBAuthorizer } from "./spicedb-authorizer" ;
@@ -43,6 +45,12 @@ export function createInitializingAuthorizer(spiceDbAuthorizer: SpiceDBAuthorize
4345 } ) ;
4446}
4547
48+ /**
49+ * We need to call our internal API with system permissions in some cases.
50+ * As we don't have other ways to represent that (e.g. ServiceAccounts), we use this magic constant to designated it.
51+ */
52+ export const SYSTEM_USER = "SYSTEM_USER" ;
53+
4654export class Authorizer {
4755 constructor ( private authorizer : SpiceDBAuthorizer ) { }
4856
@@ -51,6 +59,10 @@ export class Authorizer {
5159 permission : OrganizationPermission ,
5260 orgId : string ,
5361 ) : Promise < boolean > {
62+ if ( userId === "SYSTEM_USER" ) {
63+ return true ;
64+ }
65+
5466 const req = v1 . CheckPermissionRequest . create ( {
5567 subject : subject ( "user" , userId ) ,
5668 permission,
@@ -77,6 +89,10 @@ export class Authorizer {
7789 }
7890
7991 async hasPermissionOnProject ( userId : string , permission : ProjectPermission , projectId : string ) : Promise < boolean > {
92+ if ( userId === "SYSTEM_USER" ) {
93+ return true ;
94+ }
95+
8096 const req = v1 . CheckPermissionRequest . create ( {
8197 subject : subject ( "user" , userId ) ,
8298 permission,
@@ -102,11 +118,15 @@ export class Authorizer {
102118 ) ;
103119 }
104120
105- async hasPermissionOnUser ( userId : string , permission : UserPermission , userResourceId : string ) : Promise < boolean > {
121+ async hasPermissionOnUser ( userId : string , permission : UserPermission , resourceUserId : string ) : Promise < boolean > {
122+ if ( userId === "SYSTEM_USER" ) {
123+ return true ;
124+ }
125+
106126 const req = v1 . CheckPermissionRequest . create ( {
107127 subject : subject ( "user" , userId ) ,
108128 permission,
109- resource : object ( "user" , userResourceId ) ,
129+ resource : object ( "user" , resourceUserId ) ,
110130 consistency,
111131 } ) ;
112132
@@ -127,6 +147,39 @@ export class Authorizer {
127147 ) ;
128148 }
129149
150+ async hasPermissionOnWorkspace (
151+ userId : string ,
152+ permission : WorkspacePermission ,
153+ workspaceId : string ,
154+ ) : Promise < boolean > {
155+ if ( userId === "SYSTEM_USER" ) {
156+ return true ;
157+ }
158+
159+ const req = v1 . CheckPermissionRequest . create ( {
160+ subject : subject ( "user" , userId ) ,
161+ permission,
162+ resource : object ( "workspace" , workspaceId ) ,
163+ consistency,
164+ } ) ;
165+
166+ return this . authorizer . check ( req , { userId } ) ;
167+ }
168+
169+ async checkPermissionOnWorkspace ( userId : string , permission : WorkspacePermission , workspaceId : string ) {
170+ if ( await this . hasPermissionOnWorkspace ( userId , permission , workspaceId ) ) {
171+ return ;
172+ }
173+ if ( "read_info" === permission || ! ( await this . hasPermissionOnWorkspace ( userId , "read_info" , workspaceId ) ) ) {
174+ throw new ApplicationError ( ErrorCodes . NOT_FOUND , `Workspace ${ workspaceId } not found.` ) ;
175+ }
176+
177+ throw new ApplicationError (
178+ ErrorCodes . PERMISSION_DENIED ,
179+ `You do not have ${ permission } on workspace ${ workspaceId } ` ,
180+ ) ;
181+ }
182+
130183 // write operations below
131184 public async removeAllRelationships ( userId : string , type : ResourceType , id : string ) {
132185 if ( await this . isDisabled ( userId ) ) {
@@ -142,7 +195,7 @@ export class Authorizer {
142195 ) ;
143196
144197 // iterate over all resource types and remove by subject
145- for ( const resourcetype of [ "installation" , "user" , "organization" , "project" ] as ResourceType [ ] ) {
198+ for ( const resourcetype of AllResourceTypes as ResourceType [ ] ) {
146199 await this . authorizer . deleteRelationships (
147200 v1 . DeleteRelationshipsRequest . create ( {
148201 relationshipFilter : {
@@ -309,6 +362,26 @@ export class Authorizer {
309362 ) ;
310363 }
311364
365+ async addWorkspaceToOrg ( orgID : string , userID : string , workspaceID : string ) : Promise < void > {
366+ if ( await this . isDisabled ( userID ) ) {
367+ return ;
368+ }
369+ await this . authorizer . writeRelationships (
370+ set ( rel . workspace ( workspaceID ) . org . organization ( orgID ) ) ,
371+ set ( rel . workspace ( workspaceID ) . owner . user ( userID ) ) ,
372+ ) ;
373+ }
374+
375+ async removeWorkspaceFromOrg ( orgID : string , userID : string , workspaceID : string ) : Promise < void > {
376+ if ( await this . isDisabled ( userID ) ) {
377+ return ;
378+ }
379+ await this . authorizer . writeRelationships (
380+ remove ( rel . workspace ( workspaceID ) . org . organization ( orgID ) ) ,
381+ remove ( rel . workspace ( workspaceID ) . owner . user ( userID ) ) ,
382+ ) ;
383+ }
384+
312385 public async find ( relation : v1 . Relationship ) : Promise < v1 . Relationship | undefined > {
313386 const relationships = await this . authorizer . readRelationships ( {
314387 consistency : v1 . Consistency . create ( {
0 commit comments