77import { v1 } from "@authzed/authzed-node" ;
88
99import { 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" ;
1111import { ApplicationError , ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
1212import {
1313 OrganizationPermission ,
@@ -157,14 +157,34 @@ export class Authorizer {
157157 }
158158
159159 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 ! ) ) ) ,
167167 ) ;
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 ) ;
168188 }
169189
170190 async addOrganizationRole ( orgID : string , userID : string , role : TeamMemberRole ) : Promise < void > {
@@ -197,30 +217,45 @@ export class Authorizer {
197217 ) ;
198218 }
199219
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 ) ;
204226
205- for ( const project of projects ) {
206- await this . addProjectToOrg ( org . id , project . id ) ;
207- }
227+ await this . addOrganizationProjects ( orgId , projectIds ) ;
208228
209229 await this . authorizer . writeRelationships (
210- set ( rel . organization ( org . id ) . installation . installation ) , //
230+ set ( rel . organization ( orgId ) . installation . installation ) , //
211231 ) ;
212232 }
213233
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+ }
218244 }
219245
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+ }
224259 }
225260
226261 async addInstallationAdminRole ( userID : string ) {
@@ -258,6 +293,27 @@ export class Authorizer {
258293 }
259294 return relationships [ 0 ] . relationship ;
260295 }
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+ }
261317}
262318
263319function set ( rs : v1 . Relationship ) : v1 . RelationshipUpdate {
@@ -274,14 +330,14 @@ function remove(rs: v1.Relationship): v1.RelationshipUpdate {
274330 } ) ;
275331}
276332
277- function object ( type : ResourceType , id : string ) : v1 . ObjectReference {
333+ function object ( type : ResourceType , id ? : string ) : v1 . ObjectReference {
278334 return v1 . ObjectReference . create ( {
279335 objectId : id ,
280336 objectType : type ,
281337 } ) ;
282338}
283339
284- function subject ( type : ResourceType , id : string , relation ?: Relation | Permission ) : v1 . SubjectReference {
340+ function subject ( type : ResourceType , id ? : string , relation ?: Relation | Permission ) : v1 . SubjectReference {
285341 return v1 . SubjectReference . create ( {
286342 object : object ( type , id ) ,
287343 optionalRelation : relation ,
@@ -294,3 +350,13 @@ const consistency = v1.Consistency.create({
294350 fullyConsistent : true ,
295351 } ,
296352} ) ;
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