@@ -356,22 +356,55 @@ export class Enforcer extends ManagementEnforcer {
356356 * But getImplicitRolesForUser("alice") will get: ["role:admin", "role:user"].
357357 */
358358 public async getImplicitRolesForUser ( name : string , ...domain : string [ ] ) : Promise < string [ ] > {
359- const res = new Set < string > ( ) ;
359+ const res : string [ ] = [ ] ;
360+
361+ // Each role definition is a hierarchy of its own, so every role manager is walked
362+ // separately and the results are concatenated. Following a "g" link and then a "g2"
363+ // link off the role it led to would mix two unrelated hierarchies.
364+ for ( const ptype of this . rmMap . keys ( ) ) {
365+ res . push ( ...( await this . getNamedImplicitRolesForUser ( ptype , name , ...domain ) ) ) ;
366+ }
367+
368+ return res ;
369+ }
370+
371+ /**
372+ * getNamedImplicitRolesForUser gets implicit roles that a user has, using only the
373+ * given role definition. Compared to getImplicitRolesForUser(), which walks every
374+ * role manager, this one is restricted to "g", "g2", ...
375+ *
376+ * @param ptype the role definition type, can be "g", "g2", "g3", ..
377+ * @param name the user.
378+ * @param domain the domain, optional.
379+ */
380+ public async getNamedImplicitRolesForUser ( ptype : string , name : string , ...domain : string [ ] ) : Promise < string [ ] > {
381+ const rm = this . rmMap . get ( ptype ) ;
382+ if ( ! rm ) {
383+ throw new Error ( `role manager ${ ptype } is not initialized` ) ;
384+ }
385+
386+ if ( rm . getImplicitRoles ) {
387+ return rm . getImplicitRoles ( name , ...domain ) ;
388+ }
389+
390+ // Fallback for role managers that only expose one hop. It cannot honour a hierarchy
391+ // level limit, since that limit belongs to the role manager.
392+ const res : string [ ] = [ ] ;
393+ const visited = new Set < string > ( [ name ] ) ;
360394 const q = [ name ] ;
361395 let n : string | undefined ;
362396 while ( ( n = q . shift ( ) ) !== undefined ) {
363- for ( const rm of this . rmMap . values ( ) ) {
364- const role = await rm . getRoles ( n , ...domain ) ;
365- role . forEach ( ( r ) => {
366- if ( ! res . has ( r ) ) {
367- res . add ( r ) ;
368- q . push ( r ) ;
369- }
370- } ) ;
371- }
397+ const roles = await rm . getRoles ( n , ...domain ) ;
398+ roles . forEach ( ( r ) => {
399+ if ( ! visited . has ( r ) ) {
400+ visited . add ( r ) ;
401+ res . push ( r ) ;
402+ q . push ( r ) ;
403+ }
404+ } ) ;
372405 }
373406
374- return Array . from ( res ) ;
407+ return res ;
375408 }
376409
377410 /**
@@ -386,16 +419,78 @@ export class Enforcer extends ManagementEnforcer {
386419 * But getImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]].
387420 */
388421 public async getImplicitPermissionsForUser ( user : string , ...domain : string [ ] ) : Promise < string [ ] [ ] > {
389- const roles = await this . getImplicitRolesForUser ( user , ...domain ) ;
390- roles . unshift ( user ) ;
391- const res : string [ ] [ ] = [ ] ;
422+ return this . getNamedImplicitPermissionsForUser ( 'p' , 'g' , user , ...domain ) ;
423+ }
392424
393- for ( const n of roles ) {
394- const p = await this . getPermissionsForUser ( n , ...domain ) ;
395- res . push ( ...p ) ;
425+ /**
426+ * getNamedImplicitPermissionsForUser gets implicit permissions for a user or role
427+ * by the named policy and the named role definition.
428+ *
429+ * When a domain is given, a policy rule is kept if its domain field matches that
430+ * domain according to the role manager, so a rule written for a wildcard domain
431+ * (e.g. "p, admin, data, read, *") is reported for every concrete domain once a
432+ * domain matching function has been registered with addNamedDomainMatchingFunc().
433+ * The returned rule then carries the requested domain instead of the pattern.
434+ *
435+ * @param ptype the policy type, can be "p", "p2", "p3", ..
436+ * @param gtype the role definition type, can be "g", "g2", "g3", ..
437+ * @param user the user.
438+ * @param domain the domain, optional.
439+ */
440+ public async getNamedImplicitPermissionsForUser ( ptype : string , gtype : string , user : string , ...domain : string [ ] ) : Promise < string [ ] [ ] > {
441+ if ( domain . length > 1 ) {
442+ throw new Error ( 'error: domain should be 1 parameter' ) ;
396443 }
397444
398- return res ;
445+ const rm = this . rmMap . get ( gtype ) ;
446+ if ( ! rm ) {
447+ throw new Error ( `role manager ${ gtype } is not initialized` ) ;
448+ }
449+
450+ const roles = await this . getNamedImplicitRolesForUser ( gtype , user , ...domain ) ;
451+ const policyRoles = new Set < string > ( roles ) ;
452+ policyRoles . add ( user ) ;
453+
454+ // The subject and the domain are not necessarily the first two tokens, so both
455+ // have to be looked up in the model instead of being assumed to sit at a fixed index.
456+ const subIndex = this . getFieldIndex ( ptype , FieldIndex . Subject ) ;
457+ if ( subIndex === - 1 ) {
458+ throw new Error ( `${ FieldIndex . Subject } index is not set, please use enforcer.setFieldIndex() to set index` ) ;
459+ }
460+
461+ const permission : string [ ] [ ] = [ ] ;
462+ const policy = await this . getNamedPolicy ( ptype ) ;
463+
464+ if ( domain . length === 0 ) {
465+ for ( const rule of policy ) {
466+ if ( policyRoles . has ( rule [ subIndex ] ) ) {
467+ permission . push ( [ ...rule ] ) ;
468+ }
469+ }
470+ return permission ;
471+ }
472+
473+ const domIndex = this . getFieldIndex ( ptype , FieldIndex . Domain ) ;
474+ if ( domIndex === - 1 ) {
475+ throw new Error ( `${ FieldIndex . Domain } index is not set, please use enforcer.setFieldIndex() to set index` ) ;
476+ }
477+
478+ const d = domain [ 0 ] ;
479+ for ( const rule of policy ) {
480+ // match() falls back to an exact comparison unless a domain matching function
481+ // has been registered, so a "*" rule only spreads across domains on request.
482+ const matched = rm . match ? rm . match ( d , rule [ domIndex ] ) : d === rule [ domIndex ] ;
483+ if ( ! matched ) {
484+ continue ;
485+ }
486+ if ( policyRoles . has ( rule [ subIndex ] ) ) {
487+ const newRule = [ ...rule ] ;
488+ newRule [ domIndex ] = d ;
489+ permission . push ( newRule ) ;
490+ }
491+ }
492+
493+ return permission ;
399494 }
400495
401496 /**
0 commit comments