@ -813,7 +813,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' ) ;
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' ) ;
const result = await orgQuery . getRawAndEntities ( ) ;
const result = await orgQuery . getRawAndEntities ( ) ;
if ( result . entities . length === 0 ) {
if ( result . entities . length === 0 ) {
// If the query for the doc failed, return the failure result.
// If the query for the org failed, return the failure result.
throw new ApiError ( 'org not found' , 404 ) ;
throw new ApiError ( 'org not found' , 404 ) ;
}
}
org = result . entities [ 0 ] ;
org = result . entities [ 0 ] ;
@ -1073,7 +1073,7 @@ export class HomeDBManager extends EventEmitter {
needRealOrg : true
needRealOrg : true
} ) ;
} ) ;
orgQuery = this . _addFeatures ( orgQuery ) ;
orgQuery = this . _addFeatures ( orgQuery ) ;
const orgQueryResult = await verify IsPermitted ( orgQuery ) ;
const orgQueryResult = await verify Entity ( orgQuery ) ;
const org : Organization = this . unwrapQueryResult ( orgQueryResult ) ;
const org : Organization = this . unwrapQueryResult ( orgQueryResult ) ;
const productFeatures = org . billingAccount . product . features ;
const productFeatures = org . billingAccount . product . features ;
@ -1589,7 +1589,7 @@ export class HomeDBManager extends EventEmitter {
markPermissions ,
markPermissions ,
needRealOrg : true
needRealOrg : true
} ) ;
} ) ;
const queryResult = await verify IsPermitted ( orgQuery ) ;
const queryResult = await verify Entity ( orgQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the workspace failed, return the failure result.
// If the query for the workspace failed, return the failure result.
return queryResult ;
return queryResult ;
@ -1657,7 +1657,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'docs.aclRules' , 'doc_acl_rules' )
. leftJoinAndSelect ( 'docs.aclRules' , 'doc_acl_rules' )
. leftJoinAndSelect ( 'doc_acl_rules.group' , 'doc_group' )
. leftJoinAndSelect ( 'doc_acl_rules.group' , 'doc_group' )
. leftJoinAndSelect ( 'orgs.billingAccount' , 'billing_accounts' ) ;
. leftJoinAndSelect ( 'orgs.billingAccount' , 'billing_accounts' ) ;
const queryResult = await verify IsPermitted ( orgQuery ) ;
const queryResult = await verify Entity ( orgQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the org failed, return the failure result.
// If the query for the org failed, return the failure result.
return queryResult ;
return queryResult ;
@ -1710,7 +1710,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'acl_rules.group' , 'org_group' )
. leftJoinAndSelect ( 'acl_rules.group' , 'org_group' )
. leftJoinAndSelect ( 'orgs.workspaces' , 'workspaces' ) ; // we may want to count workspaces.
. leftJoinAndSelect ( 'orgs.workspaces' , 'workspaces' ) ; // we may want to count workspaces.
orgQuery = this . _addFeatures ( orgQuery ) ; // add features to access optional workspace limit.
orgQuery = this . _addFeatures ( orgQuery ) ; // add features to access optional workspace limit.
const queryResult = await verify IsPermitted ( orgQuery ) ;
const queryResult = await verify Entity ( orgQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the organization failed, return the failure result.
// If the query for the organization failed, return the failure result.
return queryResult ;
return queryResult ;
@ -1750,7 +1750,7 @@ export class HomeDBManager extends EventEmitter {
manager ,
manager ,
markPermissions : Permissions.UPDATE
markPermissions : Permissions.UPDATE
} ) ;
} ) ;
const queryResult = await verify IsPermitted ( wsQuery ) ;
const queryResult = await verify Entity ( wsQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the workspace failed, return the failure result.
// If the query for the workspace failed, return the failure result.
return queryResult ;
return queryResult ;
@ -1782,7 +1782,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'docs.aclRules' , 'doc_acl_rules' )
. leftJoinAndSelect ( 'docs.aclRules' , 'doc_acl_rules' )
. leftJoinAndSelect ( 'doc_acl_rules.group' , 'doc_groups' )
. leftJoinAndSelect ( 'doc_acl_rules.group' , 'doc_groups' )
. leftJoinAndSelect ( 'workspaces.org' , 'orgs' ) ;
. leftJoinAndSelect ( 'workspaces.org' , 'orgs' ) ;
const queryResult = await verify IsPermitted ( wsQuery ) ;
const queryResult = await verify Entity ( wsQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the workspace failed, return the failure result.
// If the query for the workspace failed, return the failure result.
return queryResult ;
return queryResult ;
@ -1835,7 +1835,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'workspaces.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'workspaces.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'acl_rules.group' , 'workspace_group' ) ;
. leftJoinAndSelect ( 'acl_rules.group' , 'workspace_group' ) ;
wsQuery = this . _addFeatures ( wsQuery ) ;
wsQuery = this . _addFeatures ( wsQuery ) ;
const queryResult = await verify IsPermitted ( wsQuery ) ;
const queryResult = await verify Entity ( wsQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the organization failed, return the failure result.
// If the query for the organization failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2009,7 +2009,7 @@ export class HomeDBManager extends EventEmitter {
markPermissions ,
markPermissions ,
} ) ;
} ) ;
}
}
const queryResult = await verify IsPermitted ( query ) ;
const queryResult = await verify Entity ( query ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the doc or fork failed, return the failure result.
// If the query for the doc or fork failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2062,7 +2062,7 @@ export class HomeDBManager extends EventEmitter {
manager ,
manager ,
allowSpecialPermit : true ,
allowSpecialPermit : true ,
} ) ;
} ) ;
const queryResult = await verify IsPermitted ( forkQuery ) ;
const queryResult = await verify Entity ( forkQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the fork failed, return the failure result.
// If the query for the fork failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2080,7 +2080,7 @@ export class HomeDBManager extends EventEmitter {
// Join the workspace and org to get their ids.
// Join the workspace and org to get their ids.
. leftJoinAndSelect ( 'docs.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'docs.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'acl_rules.group' , 'groups' ) ;
. leftJoinAndSelect ( 'acl_rules.group' , 'groups' ) ;
const queryResult = await verify IsPermitted ( docQuery ) ;
const queryResult = await verify Entity ( docQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the doc failed, return the failure result.
// If the query for the doc failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2218,7 +2218,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' ) ;
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' ) ;
orgQuery = this . _addFeatures ( orgQuery ) ;
orgQuery = this . _addFeatures ( orgQuery ) ;
orgQuery = this . _withAccess ( orgQuery , userId , 'orgs' ) ;
orgQuery = this . _withAccess ( orgQuery , userId , 'orgs' ) ;
const queryResult = await verify IsPermitted ( orgQuery ) ;
const queryResult = await verify Entity ( orgQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the organization failed, return the failure result.
// If the query for the organization failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2279,7 +2279,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
wsQuery = this . _addFeatures ( wsQuery , 'org' ) ;
wsQuery = this . _addFeatures ( wsQuery , 'org' ) ;
wsQuery = this . _withAccess ( wsQuery , userId , 'workspaces' ) ;
wsQuery = this . _withAccess ( wsQuery , userId , 'workspaces' ) ;
const queryResult = await verify IsPermitted ( wsQuery ) ;
const queryResult = await verify Entity ( wsQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the workspace failed, return the failure result.
// If the query for the workspace failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2380,17 +2380,7 @@ export class HomeDBManager extends EventEmitter {
// Returns UserAccessData for all users with any permissions on the org.
// Returns UserAccessData for all users with any permissions on the org.
public async getOrgAccess ( scope : Scope , orgKey : string | number ) : Promise < QueryResult < PermissionData > > {
public async getOrgAccess ( scope : Scope , orgKey : string | number ) : Promise < QueryResult < PermissionData > > {
const orgQuery = this . org ( scope , orgKey , {
const queryResult = await this . _getOrgWithACLRules ( scope , orgKey ) ;
markPermissions : Permissions.VIEW ,
needRealOrg : true ,
allowSpecialPermit : true
} )
// Join the org's ACL rules (with 1st level groups/users listed).
. leftJoinAndSelect ( 'orgs.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' )
. leftJoinAndSelect ( 'org_member_users.logins' , 'user_logins' ) ;
const queryResult = await verifyIsPermitted ( orgQuery ) ;
if ( queryResult . status !== 200 ) {
if ( queryResult . status !== 200 ) {
// If the query for the doc failed, return the failure result.
// If the query for the doc failed, return the failure result.
return queryResult ;
return queryResult ;
@ -2419,33 +2409,41 @@ export class HomeDBManager extends EventEmitter {
// maxInheritedRole set on the workspace. Note that information for all users in the org
// maxInheritedRole set on the workspace. Note that information for all users in the org
// is given to indicate which users have access to the org but not to this particular workspace.
// is given to indicate which users have access to the org but not to this particular workspace.
public async getWorkspaceAccess ( scope : Scope , wsId : number ) : Promise < QueryResult < PermissionData > > {
public async getWorkspaceAccess ( scope : Scope , wsId : number ) : Promise < QueryResult < PermissionData > > {
const wsQuery = this . _workspace ( scope , wsId , {
// Run the query for the workspace and org in a transaction. This brings some isolation protection
markPermissions : Permissions.VIEW
// against changes to the workspace or org while we are querying.
} )
const { workspace , org , queryFailure } = await this . _connection . transaction ( async manager = > {
// Join the workspace's ACL rules (with 1st level groups/users listed).
const wsQueryResult = await this . _getWorkspaceWithACLRules ( scope , wsId , { manager } ) ;
. leftJoinAndSelect ( 'workspaces.aclRules' , 'acl_rules' )
if ( wsQueryResult . status !== 200 ) {
. leftJoinAndSelect ( 'acl_rules.group' , 'workspace_groups' )
// If the query for the workspace failed, return the failure result.
. leftJoinAndSelect ( 'workspace_groups.memberUsers' , 'workspace_group_users' )
return { queryFailure : wsQueryResult } ;
. leftJoinAndSelect ( 'workspace_groups.memberGroups' , 'workspace_group_groups' )
}
. leftJoinAndSelect ( 'workspace_group_users.logins' , 'workspace_user_logins' )
// Join the org and groups/users.
const orgQuery = this . _buildOrgWithACLRulesQuery ( scope , wsQueryResult . data . org . id , { manager } ) ;
. leftJoinAndSelect ( 'workspaces.org' , 'org' )
const orgQueryResult = await verifyEntity ( orgQuery , { skipPermissionCheck : true } ) ;
. leftJoinAndSelect ( 'org.aclRules' , 'org_acl_rules' )
if ( orgQueryResult . status !== 200 ) {
. leftJoinAndSelect ( 'org_acl_rules.group' , 'org_groups' )
// If the query for the org failed, return the failure result.
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_group_users' )
return { queryFailure : orgQueryResult } ;
. leftJoinAndSelect ( 'org_group_users.logins' , 'org_user_logins' ) ;
}
const queryResult = await verifyIsPermitted ( wsQuery ) ;
if ( queryResult . status !== 200 ) {
return {
// If the query for the doc failed, return the failure result.
workspace : wsQueryResult.data ,
return queryResult ;
org : orgQueryResult.data
} ;
} ) ;
if ( queryFailure ) {
return queryFailure ;
}
}
const workspace : Workspace = queryResult . data ;
const wsMap = getMemberUserRoles ( workspace , this . defaultCommonGroupNames ) ;
const wsMap = getMemberUserRoles ( workspace , this . defaultCommonGroupNames ) ;
// Also fetch the organization ACLs so we can determine inherited rights.
// The orgMap gives the org access inherited by each user.
// The orgMap gives the org access inherited by each user.
const orgMap = getMemberUserRoles ( workspace . org , this . defaultBasicGroupNames ) ;
const orgMap = getMemberUserRoles ( org, this . defaultBasicGroupNames ) ;
const orgMapWithMembership = getMemberUserRoles ( workspace . org , this . defaultGroupNames ) ;
const orgMapWithMembership = getMemberUserRoles ( org, this . defaultGroupNames ) ;
// Iterate through the org since all users will be in the org.
// Iterate through the org since all users will be in the org.
const users : UserAccessData [ ] = getResourceUsers ( [ workspace , workspace . org ] ) . map ( u = > {
const users : UserAccessData [ ] = getResourceUsers ( [ workspace , org ] ) . map ( u = > {
const orgAccess = orgMapWithMembership [ u . id ] || null ;
const orgAccess = orgMapWithMembership [ u . id ] || null ;
return {
return {
. . . this . makeFullUser ( u ) ,
. . . this . makeFullUser ( u ) ,
@ -2576,7 +2574,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'orgs.aclRules' , 'org_acl_rules' )
. leftJoinAndSelect ( 'orgs.aclRules' , 'org_acl_rules' )
. leftJoinAndSelect ( 'org_acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
const docQueryResult = await verify IsPermitted ( docQuery ) ;
const docQueryResult = await verify Entity ( docQuery ) ;
if ( docQueryResult . status !== 200 ) {
if ( docQueryResult . status !== 200 ) {
// If the query for the doc failed, return the failure result.
// If the query for the doc failed, return the failure result.
return docQueryResult ;
return docQueryResult ;
@ -2603,7 +2601,7 @@ export class HomeDBManager extends EventEmitter {
. leftJoinAndSelect ( 'org_acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_users' ) ;
wsQuery = this . _addFeatures ( wsQuery ) ;
wsQuery = this . _addFeatures ( wsQuery ) ;
const wsQueryResult = await verify IsPermitted ( wsQuery ) ;
const wsQueryResult = await verify Entity ( wsQuery ) ;
if ( wsQueryResult . status !== 200 ) {
if ( wsQueryResult . status !== 200 ) {
// If the query for the organization failed, return the failure result.
// If the query for the organization failed, return the failure result.
return wsQueryResult ;
return wsQueryResult ;
@ -2681,7 +2679,7 @@ export class HomeDBManager extends EventEmitter {
manager
manager
} )
} )
. addSelect ( this . _markIsPermitted ( 'orgs' , scope . userId , 'open' , permissions ) , 'is_permitted' ) ;
. addSelect ( this . _markIsPermitted ( 'orgs' , scope . userId , 'open' , permissions ) , 'is_permitted' ) ;
const docQueryResult = await verify IsPermitted ( docQuery ) ;
const docQueryResult = await verify Entity ( docQuery ) ;
if ( docQueryResult . status !== 200 ) {
if ( docQueryResult . status !== 200 ) {
// If the query for the doc failed, return the failure result.
// If the query for the doc failed, return the failure result.
return docQueryResult ;
return docQueryResult ;
@ -4669,7 +4667,7 @@ export class HomeDBManager extends EventEmitter {
// SQL results are flattened, and multiplying the number of rows we have already
// SQL results are flattened, and multiplying the number of rows we have already
// by the number of workspace users could get excessive.
// by the number of workspace users could get excessive.
. leftJoinAndSelect ( 'docs.workspace' , 'workspace' ) ;
. leftJoinAndSelect ( 'docs.workspace' , 'workspace' ) ;
const queryResult = await verify IsPermitted ( docQuery ) ;
const queryResult = await verify Entity ( docQuery ) ;
const doc : Document = this . unwrapQueryResult ( queryResult ) ;
const doc : Document = this . unwrapQueryResult ( queryResult ) ;
// Load the workspace's member groups/users.
// Load the workspace's member groups/users.
@ -4777,7 +4775,7 @@ export class HomeDBManager extends EventEmitter {
manager ,
manager ,
markPermissions : Permissions.REMOVE
markPermissions : Permissions.REMOVE
} ) ;
} ) ;
const workspace : Workspace = this . unwrapQueryResult ( await verify IsPermitted ( wsQuery ) ) ;
const workspace : Workspace = this . unwrapQueryResult ( await verify Entity ( wsQuery ) ) ;
await manager . createQueryBuilder ( )
await manager . createQueryBuilder ( )
. update ( Workspace ) . set ( { removedAt } ) . where ( { id : workspace.id } )
. update ( Workspace ) . set ( { removedAt } ) . where ( { id : workspace.id } )
. execute ( ) ;
. execute ( ) ;
@ -4795,7 +4793,7 @@ export class HomeDBManager extends EventEmitter {
if ( ! removedAt ) {
if ( ! removedAt ) {
docQuery = this . _addFeatures ( docQuery ) ; // pull in billing information for doc count limits
docQuery = this . _addFeatures ( docQuery ) ; // pull in billing information for doc count limits
}
}
const doc : Document = this . unwrapQueryResult ( await verify IsPermitted ( docQuery ) ) ;
const doc : Document = this . unwrapQueryResult ( await verify Entity ( docQuery ) ) ;
if ( ! removedAt ) {
if ( ! removedAt ) {
await this . _checkRoomForAnotherDoc ( doc . workspace , manager ) ;
await this . _checkRoomForAnotherDoc ( doc . workspace , manager ) ;
}
}
@ -4829,16 +4827,59 @@ export class HomeDBManager extends EventEmitter {
if ( thisUser ) { users . push ( thisUser ) ; }
if ( thisUser ) { users . push ( thisUser ) ; }
return { personal : true , public : ! realAccess } ;
return { personal : true , public : ! realAccess } ;
}
}
private _getWorkspaceWithACLRules ( scope : Scope , wsId : number , options : Partial < QueryOptions > = { } ) {
const query = this . _workspace ( scope , wsId , {
markPermissions : Permissions.VIEW ,
. . . options
} )
// Join the workspace's ACL rules (with 1st level groups/users listed).
. leftJoinAndSelect ( 'workspaces.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'acl_rules.group' , 'workspace_groups' )
. leftJoinAndSelect ( 'workspace_groups.memberUsers' , 'workspace_group_users' )
. leftJoinAndSelect ( 'workspace_groups.memberGroups' , 'workspace_group_groups' )
. leftJoinAndSelect ( 'workspace_group_users.logins' , 'workspace_user_logins' )
. leftJoinAndSelect ( 'workspaces.org' , 'org' ) ;
return verifyEntity ( query ) ;
}
private _buildOrgWithACLRulesQuery ( scope : Scope , org : number | string , opts : Partial < QueryOptions > = { } ) {
return this . org ( scope , org , {
needRealOrg : true ,
. . . opts
} )
// Join the org's ACL rules (with 1st level groups/users listed).
. leftJoinAndSelect ( 'orgs.aclRules' , 'acl_rules' )
. leftJoinAndSelect ( 'acl_rules.group' , 'org_groups' )
. leftJoinAndSelect ( 'org_groups.memberUsers' , 'org_member_users' )
. leftJoinAndSelect ( 'org_member_users.logins' , 'user_logins' ) ;
}
private _getOrgWithACLRules ( scope : Scope , org : number | string ) {
const orgQuery = this . _buildOrgWithACLRulesQuery ( scope , org , {
markPermissions : Permissions.VIEW ,
allowSpecialPermit : true ,
} ) ;
return verifyEntity ( orgQuery ) ;
}
}
}
// Return a QueryResult reflecting the output of a query builder.
// Return a QueryResult reflecting the output of a query builder.
// Checks on the "is_permitted" field which select queries set on resources to
// Checks on the "is_permitted" field which select queries set on resources to
// indicate whether the user has access.
// indicate whether the user has access.
//
// If the output is empty, we signal that the desired resource does not exist.
// If the output is empty, we signal that the desired resource does not exist.
// If the "is_permitted" field is falsy, we signal that the resource is forbidden.
//
// If we retrieve more than 1 entity, we signal that the request is ambiguous.
//
// If the "is_permitted" field is falsy, we signal that the resource is forbidden,
// unless skipPermissionCheck is set.
//
// Returns the resource fetched by the queryBuilder.
// Returns the resource fetched by the queryBuilder.
async function verifyIsPermitted (
async function verifyEntity (
queryBuilder : SelectQueryBuilder < any >
queryBuilder : SelectQueryBuilder < any > ,
options : { skipPermissionCheck? : boolean } = { }
) : Promise < QueryResult < any > > {
) : Promise < QueryResult < any > > {
const results = await queryBuilder . getRawAndEntities ( ) ;
const results = await queryBuilder . getRawAndEntities ( ) ;
if ( results . entities . length === 0 ) {
if ( results . entities . length === 0 ) {
@ -4851,7 +4892,7 @@ async function verifyIsPermitted(
status : 400 ,
status : 400 ,
errMessage : ` ambiguous ${ getFrom ( queryBuilder ) } request `
errMessage : ` ambiguous ${ getFrom ( queryBuilder ) } request `
} ;
} ;
} else if ( ! results. raw [ 0 ] . is_permitted ) {
} else if ( ! options. skipPermissionCheck && ! results. raw [ 0 ] . is_permitted ) {
return {
return {
status : 403 ,
status : 403 ,
errMessage : "access denied"
errMessage : "access denied"