@@ -13,12 +13,14 @@ module.exports = class AccessUtils {
13
13
this . options = _defaults ( { } , options , {
14
14
userModel : 'User' ,
15
15
roleModel : 'Role' ,
16
- groupModel : 'Group' ,
17
16
groupAccessModel : 'GroupAccess' ,
17
+ groupModel : 'Group' ,
18
+ foreignKey : 'groupId' ,
18
19
groupRoles : [
19
20
'$group:admin' ,
20
21
'$group:member'
21
- ]
22
+ ] ,
23
+ applyToStatic : false
22
24
} ) ;
23
25
// Default the foreignKey to the group model name + Id.
24
26
this . options . foreignKey = this . options . foreignKey || `${ this . options . groupModel . toLowerCase ( ) } Id` ;
@@ -279,21 +281,8 @@ module.exports = class AccessUtils {
279
281
const GroupAccess = this . app . models [ this . options . groupAccessModel ] ;
280
282
const scope = { } ;
281
283
282
- if ( userId ) {
283
- this . app . loopback . getCurrentContext ( ) . set ( 'groupAccessApplied' , true ) ;
284
- }
285
-
286
284
debug ( `Role resolver for ${ role } : evaluate ${ modelClass . modelName } with id: ${ modelId } for user: ${ userId } ` ) ;
287
285
288
- if ( ! context || ! context . model || ! context . modelId ) {
289
- process . nextTick ( ( ) => {
290
- debug ( 'Deny access (context: %s, context.model: %s, context.modelId: %s)' ,
291
- ! ! context , ! ! context . model , ! ! context . modelId ) ;
292
- if ( cb ) cb ( null , false ) ;
293
- } ) ;
294
- return ;
295
- }
296
-
297
286
// No userId is present
298
287
if ( ! userId ) {
299
288
process . nextTick ( ( ) => {
@@ -303,53 +292,92 @@ module.exports = class AccessUtils {
303
292
return ;
304
293
}
305
294
306
- return this . isGroupMemberWithRole ( modelClass , modelId , userId , roleName )
307
- . then ( res => {
308
- cb ( null , res ) ;
295
+ this . app . loopback . getCurrentContext ( ) . set ( 'groupAccessApplied' , true ) ;
296
+
297
+ /**
298
+ * Basic application that does not cover static methods.
299
+ * Similar to $owner.
300
+ */
301
+ if ( ! this . options . applyToStatic ) {
302
+ if ( ! context || ! context . model || ! context . modelId ) {
303
+ process . nextTick ( ( ) => {
304
+ debug ( 'Deny access (context: %s, context.model: %s, context.modelId: %s)' ,
305
+ ! ! context , ! ! context . model , ! ! context . modelId ) ;
306
+ if ( cb ) cb ( null , false ) ;
307
+ } ) ;
308
+ return ;
309
+ }
310
+
311
+ return this . isGroupMemberWithRole ( modelClass , modelId , userId , roleName )
312
+ . then ( res => {
313
+ cb ( null , res ) ;
314
+ } )
315
+ . catch ( cb ) ;
316
+ }
317
+
318
+ /**
319
+ * More complex application that also covers static methods.
320
+ */
321
+ return Promise . join ( this . getCurrentGroupId ( context ) , this . getTargetGroupId ( context ) ,
322
+ ( currentGroupId , targetGroupId ) => {
323
+ if ( ! currentGroupId ) {
324
+ // TODO: Use promise cancellation to abort the chain early.
325
+ // Causes the access check to be bypassed (see below).
326
+ return [ false ] ;
327
+ }
328
+
329
+ scope . currentGroupId = currentGroupId ;
330
+ scope . targetGroupId = targetGroupId ;
331
+ const actions = [ ] ;
332
+ const conditions = {
333
+ userId : userId ,
334
+ role : roleName
335
+ } ;
336
+
337
+ conditions [ this . options . foreignKey ] = currentGroupId ;
338
+ actions . push ( GroupAccess . count ( conditions ) ) ;
339
+
340
+ // If this is an attempt to save the item into a new group, check the user has access to the target group.
341
+ if ( targetGroupId && targetGroupId !== currentGroupId ) {
342
+ conditions [ this . options . foreignKey ] = targetGroupId ;
343
+ actions . push ( GroupAccess . count ( conditions ) ) ;
344
+ }
345
+
346
+ return actions ;
347
+ } )
348
+ . spread ( ( currentGroupCount , targetGroupCount ) => {
349
+ let res = false ;
350
+
351
+ if ( currentGroupCount === false ) {
352
+ // No group context was determined, so allow passthrough access.
353
+ res = true ;
354
+ }
355
+ else {
356
+ // Determine grant based on the current/target group context.
357
+ res = currentGroupCount > 0 ;
358
+
359
+ debug ( `user ${ userId } ${ res ? 'is a' : 'is not a' } ${ roleName } of group ${ scope . currentGroupId } ` ) ;
360
+
361
+ // If it's an attempt to save into a new group, also ensure the user has access to the target group.
362
+ if ( scope . targetGroupId && scope . targetGroupId !== scope . currentGroupId ) {
363
+ const tMember = targetGroupCount > 0 ;
364
+
365
+ debug ( `user ${ userId } ${ tMember ? 'is a' : 'is not a' } ${ roleName } of group ${ scope . targetGroupId } ` ) ;
366
+ res = res && tMember ;
367
+ }
368
+ }
369
+
370
+ // Note the fact that we are allowing access due to passing an ACL.
371
+ if ( res ) {
372
+ this . app . loopback . getCurrentContext ( ) . set ( 'groupAccessApplied' , true ) ;
373
+ }
374
+
375
+ return cb ( null , res ) ;
309
376
} )
310
377
. catch ( cb ) ;
311
378
312
- // return this.getTargetGroupId(context)
313
- // .then(targetGroupId => {
314
- // const actions = [ ];
315
- //
316
- // actions.push(this.isGroupMemberWithRole(modelClass, modelId, userId, roleName));
317
- //
318
- // // If this is an attempt to save the item into a new group, check the user has access to the target group.
319
- // if (targetGroupId && targetGroupId !== modelId) {
320
- // scope.targetGroupId = targetGroupId;
321
- // actions.push(this.isGroupMemberWithRole(modelClass, targetGroupId, userId, roleName));
322
- // }
323
- //
324
- // return actions;
325
- // })
326
- // .spread((currentGroupCount, targetGroupCount) => {
327
- // let res = false;
328
- //
329
- // // Determine grant based on the current/target group context.
330
- // res = currentGroupCount > 0;
331
- //
332
- // debug(`user ${userId} ${res ? 'is a' : 'is not a'} ${roleName} of group ${modelId}`);
333
- //
334
- // // If it's an attempt to save into a new group, also ensure the user has access to the target group.
335
- // if (scope.targetGroupId && scope.targetGroupId !== modelId) {
336
- // const tMember = targetGroupCount > 0;
337
- //
338
- // debug(`user ${userId} ${tMember ? 'is a' : 'is not a'} ${roleName} of group ${scope.targetGroupId}`);
339
- // res = res && tMember;
340
- // }
341
- //
342
- // // Note the fact that we are allowing access due to passing an ACL.
343
- // if (res) {
344
- // this.app.loopback.getCurrentContext().set('groupAccessApplied', true);
345
- // }
346
- //
347
- // debug(`${accessGroup} role resolver returns ${res} for user ${userId}`);
348
- // return cb(null, res);
349
- // })
350
- // .catch(cb);
351
- } ) ;
352
- }
379
+ } ) ;
380
+ } ;
353
381
354
382
/**
355
383
* Check if a given user ID has a given role in the model instances group.
0 commit comments