@@ -14,6 +14,7 @@ import (
1414 "github.com/raystack/frontier/core/policy"
1515 "github.com/raystack/frontier/core/relation"
1616 "github.com/raystack/frontier/core/user"
17+ pat "github.com/raystack/frontier/core/userpat/models"
1718 "github.com/raystack/frontier/internal/bootstrap/schema"
1819 "github.com/stretchr/testify/assert"
1920 "github.com/stretchr/testify/mock"
@@ -262,3 +263,107 @@ func TestService_Update(t *testing.T) {
262263 assert .Equal (t , err , group .ErrInvalidID )
263264 })
264265}
266+
267+ func TestService_ListByUser (t * testing.T ) {
268+ ctx := context .Background ()
269+
270+ t .Run ("should resolve PAT to user and intersect with PAT group scope" , func (t * testing.T ) {
271+ mockRepo := mocks .NewRepository (t )
272+ mockRelationSvc := mocks .NewRelationService (t )
273+ mockAuthnSvc := mocks .NewAuthnService (t )
274+ mockPolicySvc := mocks .NewPolicyService (t )
275+
276+ svc := group .NewService (mockRepo , mockRelationSvc , mockAuthnSvc , mockPolicySvc )
277+
278+ // LookupResources for user's group memberships
279+ mockRelationSvc .On ("LookupResources" , ctx , relation.Relation {
280+ Object : relation.Object {Namespace : schema .GroupNamespace },
281+ Subject : relation.Subject {Namespace : schema .UserPrincipal , ID : "user-123" },
282+ RelationName : schema .MembershipPermission ,
283+ }).Return ([]string {"group-1" , "group-2" , "group-3" }, nil ).Once ()
284+
285+ // LookupResources for PAT's group scope
286+ mockRelationSvc .On ("LookupResources" , ctx , relation.Relation {
287+ Object : relation.Object {Namespace : schema .GroupNamespace },
288+ Subject : relation.Subject {ID : "pat-456" , Namespace : schema .PATPrincipal },
289+ RelationName : schema .GetPermission ,
290+ }).Return ([]string {"group-1" , "group-3" }, nil ).Once ()
291+
292+ // Repo should be called with intersection
293+ mockRepo .On ("List" , ctx , group.Filter {
294+ GroupIDs : []string {"group-1" , "group-3" },
295+ }).Return ([]group.Group {
296+ {ID : "group-1" , Name : "group-one" },
297+ {ID : "group-3" , Name : "group-three" },
298+ }, nil ).Once ()
299+
300+ result , err := svc .ListByUser (ctx , authenticate.Principal {
301+ ID : "pat-456" ,
302+ Type : schema .PATPrincipal ,
303+ PAT : & pat.PAT {ID : "pat-456" , UserID : "user-123" , OrgID : "org-1" },
304+ }, group.Filter {})
305+
306+ assert .NoError (t , err )
307+ assert .Len (t , result , 2 )
308+ })
309+
310+ t .Run ("should return nil when PAT has no group scope overlap" , func (t * testing.T ) {
311+ mockRepo := mocks .NewRepository (t )
312+ mockRelationSvc := mocks .NewRelationService (t )
313+ mockAuthnSvc := mocks .NewAuthnService (t )
314+ mockPolicySvc := mocks .NewPolicyService (t )
315+
316+ svc := group .NewService (mockRepo , mockRelationSvc , mockAuthnSvc , mockPolicySvc )
317+
318+ mockRelationSvc .On ("LookupResources" , ctx , relation.Relation {
319+ Object : relation.Object {Namespace : schema .GroupNamespace },
320+ Subject : relation.Subject {Namespace : schema .UserPrincipal , ID : "user-123" },
321+ RelationName : schema .MembershipPermission ,
322+ }).Return ([]string {"group-1" }, nil ).Once ()
323+
324+ mockRelationSvc .On ("LookupResources" , ctx , relation.Relation {
325+ Object : relation.Object {Namespace : schema .GroupNamespace },
326+ Subject : relation.Subject {ID : "pat-456" , Namespace : schema .PATPrincipal },
327+ RelationName : schema .GetPermission ,
328+ }).Return ([]string {"group-2" }, nil ).Once ()
329+
330+ result , err := svc .ListByUser (ctx , authenticate.Principal {
331+ ID : "pat-456" ,
332+ Type : schema .PATPrincipal ,
333+ PAT : & pat.PAT {ID : "pat-456" , UserID : "user-123" , OrgID : "org-1" },
334+ }, group.Filter {})
335+
336+ assert .NoError (t , err )
337+ assert .Nil (t , result )
338+ })
339+
340+ t .Run ("should pass through for regular user principal" , func (t * testing.T ) {
341+ mockRepo := mocks .NewRepository (t )
342+ mockRelationSvc := mocks .NewRelationService (t )
343+ mockAuthnSvc := mocks .NewAuthnService (t )
344+ mockPolicySvc := mocks .NewPolicyService (t )
345+
346+ svc := group .NewService (mockRepo , mockRelationSvc , mockAuthnSvc , mockPolicySvc )
347+
348+ mockRelationSvc .On ("LookupResources" , ctx , relation.Relation {
349+ Object : relation.Object {Namespace : schema .GroupNamespace },
350+ Subject : relation.Subject {Namespace : schema .UserPrincipal , ID : "user-123" },
351+ RelationName : schema .MembershipPermission ,
352+ }).Return ([]string {"group-1" , "group-2" }, nil ).Once ()
353+
354+ mockRepo .On ("List" , ctx , group.Filter {
355+ GroupIDs : []string {"group-1" , "group-2" },
356+ }).Return ([]group.Group {
357+ {ID : "group-1" , Name : "group-one" },
358+ {ID : "group-2" , Name : "group-two" },
359+ }, nil ).Once ()
360+
361+ result , err := svc .ListByUser (ctx , authenticate.Principal {
362+ ID : "user-123" ,
363+ Type : schema .UserPrincipal ,
364+ }, group.Filter {})
365+
366+ assert .NoError (t , err )
367+ assert .Len (t , result , 2 )
368+ })
369+ }
0 commit comments