7
7
import { v1 } from "@authzed/authzed-node" ;
8
8
9
9
import { BUILTIN_INSTLLATION_ADMIN_USER_ID } from "@gitpod/gitpod-db/lib" ;
10
- import { Organization , Project , TeamMemberInfo , TeamMemberRole } from "@gitpod/gitpod-protocol" ;
10
+ import { TeamMemberRole } from "@gitpod/gitpod-protocol" ;
11
11
import { ApplicationError , ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
12
12
import {
13
13
OrganizationPermission ,
@@ -157,14 +157,34 @@ export class Authorizer {
157
157
}
158
158
159
159
async addUser ( userId : string , owningOrgId ?: string ) {
160
- await this . authorizer . writeRelationships (
161
- set ( rel . user ( userId ) . self . user ( userId ) ) , //
162
- set (
163
- owningOrgId
164
- ? rel . user ( userId ) . organization . organization ( owningOrgId )
165
- : rel . user ( userId ) . installation . installation ,
166
- ) ,
160
+ const oldOrgs = await this . findAll ( rel . user ( userId ) . organization . organization ( "" ) ) ;
161
+ const updates = [ set ( rel . user ( userId ) . self . user ( userId ) ) ] ;
162
+ updates . push (
163
+ ... oldOrgs
164
+ . map ( ( r ) => r . subject ?. object ?. objectId )
165
+ . filter ( ( orgId ) => ! ! orgId && orgId !== owningOrgId )
166
+ . map ( ( orgId ) => remove ( rel . user ( userId ) . organization . organization ( orgId ! ) ) ) ,
167
167
) ;
168
+
169
+ if ( owningOrgId ) {
170
+ updates . push (
171
+ set ( rel . user ( userId ) . organization . organization ( owningOrgId ) ) , //
172
+ remove ( rel . user ( userId ) . installation . installation ) ,
173
+ remove ( rel . installation . member . user ( userId ) ) ,
174
+ remove ( rel . installation . admin . user ( userId ) ) ,
175
+ ) ;
176
+ } else {
177
+ updates . push (
178
+ set ( rel . user ( userId ) . installation . installation ) , //
179
+ set ( rel . installation . member . user ( userId ) ) ,
180
+ ) ;
181
+ }
182
+
183
+ await this . authorizer . writeRelationships ( ...updates ) ;
184
+ }
185
+
186
+ async removeUser ( userId : string ) {
187
+ await this . removeAllRelationships ( "user" , userId ) ;
168
188
}
169
189
170
190
async addOrganizationRole ( orgID : string , userID : string , role : TeamMemberRole ) : Promise < void > {
@@ -197,30 +217,45 @@ export class Authorizer {
197
217
) ;
198
218
}
199
219
200
- async addOrganization ( org : Organization , members : TeamMemberInfo [ ] , projects : Project [ ] ) : Promise < void > {
201
- for ( const member of members ) {
202
- await this . addOrganizationRole ( org . id , member . userId , member . role ) ;
203
- }
220
+ async addOrganization (
221
+ orgId : string ,
222
+ members : { userId : string ; role : TeamMemberRole } [ ] ,
223
+ projectIds : string [ ] ,
224
+ ) : Promise < void > {
225
+ await this . addOrganizationMembers ( orgId , members ) ;
204
226
205
- for ( const project of projects ) {
206
- await this . addProjectToOrg ( org . id , project . id ) ;
207
- }
227
+ await this . addOrganizationProjects ( orgId , projectIds ) ;
208
228
209
229
await this . authorizer . writeRelationships (
210
- set ( rel . organization ( org . id ) . installation . installation ) , //
230
+ set ( rel . organization ( orgId ) . installation . installation ) , //
211
231
) ;
212
232
}
213
233
214
- async addInstallationMemberRole ( userID : string ) {
215
- await this . authorizer . writeRelationships (
216
- set ( rel . installation . member . user ( userID ) ) , //
217
- ) ;
234
+ private async addOrganizationProjects ( orgID : string , projectIds : string [ ] ) : Promise < void > {
235
+ const existing = await this . findAll ( rel . project ( "" ) . org . organization ( orgID ) ) ;
236
+ const toBeRemoved = asSet ( existing . map ( ( r ) => r . resource ?. objectId ) ) ;
237
+ for ( const projectId of projectIds ) {
238
+ await this . addProjectToOrg ( orgID , projectId ) ;
239
+ toBeRemoved . delete ( projectId ) ;
240
+ }
241
+ for ( const projectId of toBeRemoved ) {
242
+ await this . removeProjectFromOrg ( orgID , projectId ) ;
243
+ }
218
244
}
219
245
220
- async removeInstallationMemberRole ( userID : string ) {
221
- await this . authorizer . writeRelationships (
222
- remove ( rel . installation . member . user ( userID ) ) , //
223
- ) ;
246
+ private async addOrganizationMembers (
247
+ orgID : string ,
248
+ members : { userId : string ; role : TeamMemberRole } [ ] ,
249
+ ) : Promise < void > {
250
+ const existing = await this . findAll ( rel . organization ( orgID ) . member . user ( "" ) ) ;
251
+ const toBeRemoved = asSet ( existing . map ( ( r ) => r . subject ?. object ?. objectId ) ) ;
252
+ for ( const member of members ) {
253
+ await this . addOrganizationRole ( orgID , member . userId , member . role ) ;
254
+ toBeRemoved . delete ( member . userId ) ;
255
+ }
256
+ for ( const userId of toBeRemoved ) {
257
+ await this . removeOrganizationRole ( orgID , userId , "member" ) ;
258
+ }
224
259
}
225
260
226
261
async addInstallationAdminRole ( userID : string ) {
@@ -258,6 +293,27 @@ export class Authorizer {
258
293
}
259
294
return relationships [ 0 ] . relationship ;
260
295
}
296
+
297
+ async findAll ( relation : v1 . Relationship ) : Promise < v1 . Relationship [ ] > {
298
+ const relationships = await this . authorizer . readRelationships ( {
299
+ consistency : v1 . Consistency . create ( {
300
+ requirement : {
301
+ oneofKind : "fullyConsistent" ,
302
+ fullyConsistent : true ,
303
+ } ,
304
+ } ) ,
305
+ relationshipFilter : {
306
+ resourceType : relation . resource ?. objectType || "" ,
307
+ optionalResourceId : relation . resource ?. objectId || "" ,
308
+ optionalRelation : relation . relation ,
309
+ optionalSubjectFilter : relation . subject ?. object && {
310
+ subjectType : relation . subject . object . objectType ,
311
+ optionalSubjectId : relation . subject . object . objectId ,
312
+ } ,
313
+ } ,
314
+ } ) ;
315
+ return relationships . map ( ( r ) => r . relationship ! ) ;
316
+ }
261
317
}
262
318
263
319
function set ( rs : v1 . Relationship ) : v1 . RelationshipUpdate {
@@ -274,14 +330,14 @@ function remove(rs: v1.Relationship): v1.RelationshipUpdate {
274
330
} ) ;
275
331
}
276
332
277
- function object ( type : ResourceType , id : string ) : v1 . ObjectReference {
333
+ function object ( type : ResourceType , id ? : string ) : v1 . ObjectReference {
278
334
return v1 . ObjectReference . create ( {
279
335
objectId : id ,
280
336
objectType : type ,
281
337
} ) ;
282
338
}
283
339
284
- function subject ( type : ResourceType , id : string , relation ?: Relation | Permission ) : v1 . SubjectReference {
340
+ function subject ( type : ResourceType , id ? : string , relation ?: Relation | Permission ) : v1 . SubjectReference {
285
341
return v1 . SubjectReference . create ( {
286
342
object : object ( type , id ) ,
287
343
optionalRelation : relation ,
@@ -294,3 +350,13 @@ const consistency = v1.Consistency.create({
294
350
fullyConsistent : true ,
295
351
} ,
296
352
} ) ;
353
+
354
+ function asSet < T > ( array : ( T | undefined ) [ ] ) : Set < T > {
355
+ const result = new Set < T > ( ) ;
356
+ for ( const r of array ) {
357
+ if ( r ) {
358
+ result . add ( r ) ;
359
+ }
360
+ }
361
+ return result ;
362
+ }
0 commit comments