From fa2223c3931ee11789feda85fa43f9882fefb4bb Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Fri, 7 Aug 2026 23:16:07 +0100 Subject: [PATCH] skip fga check for support user when cloning into a program --- internal/graphapi/control_clone.go | 30 +++++++++++++++++------------- internal/graphapi/control_test.go | 12 ++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/internal/graphapi/control_clone.go b/internal/graphapi/control_clone.go index 04a551091a..93acea99bb 100644 --- a/internal/graphapi/control_clone.go +++ b/internal/graphapi/control_clone.go @@ -214,21 +214,25 @@ func (r *mutationResolver) cloneControls(ctx context.Context, controlsToClone [] return nil, generated.ErrPermissionDenied } - allow, err := r.db.Authz.CheckAccess(ctx, fgax.AccessCheck{ - ObjectType: generated.TypeProgram, - ObjectID: *programID, - Relation: fgax.CanEdit, - SubjectID: caller.SubjectID, - SubjectType: caller.SubjectType(), - }) - if err != nil { - return nil, err - } + // support users should skip this check but for other users, + // we still want to verify they have edit access to the program + if !caller.Has(auth.CapOrgSupport) { + allow, err := r.db.Authz.CheckAccess(ctx, fgax.AccessCheck{ + ObjectType: generated.TypeProgram, + ObjectID: *programID, + Relation: fgax.CanEdit, + SubjectID: caller.SubjectID, + SubjectType: caller.SubjectType(), + }) + if err != nil { + return nil, err + } - if !allow { - logger.Error().Str("organization_id", caller.OrganizationID).Str("user_id", caller.SubjectID).Msg("no access to edit specified program") + if !allow { + logger.Error().Str("organization_id", caller.OrganizationID).Str("user_id", caller.SubjectID).Msg("no access to edit specified program") - return nil, generated.ErrPermissionDenied + return nil, generated.ErrPermissionDenied + } } } diff --git a/internal/graphapi/control_test.go b/internal/graphapi/control_test.go index 3cc2dbd043..39b5087ca6 100644 --- a/internal/graphapi/control_test.go +++ b/internal/graphapi/control_test.go @@ -821,6 +821,18 @@ func TestMutationCreateControlsByClone(t *testing.T) { client: suite.client.api, ctx: sharedTestUser1.UserCtx, }, + { + name: "happy path for support user, all controls under standard with program", + request: testclient.CloneControlInput{ + ControlIDs: controlIDs, + ProgramID: &program.ID, + }, + expectedControls: controls, + expectedStandard: &publicStandard.ShortName, + expectedNumProgram: 1, + client: suite.client.api, + ctx: newSupportCtx(sharedTestUser1.UserCtx, sharedTestUser1.OrganizationID), + }, { name: "all controls under standard with program no access", request: testclient.CloneControlInput{