From b5fd1ab201ceb9b1d29fb0f57f53afdc4ab8f226 Mon Sep 17 00:00:00 2001 From: "Jose I. Paris" Date: Thu, 10 Jul 2025 17:16:40 +0200 Subject: [PATCH 1/2] inherit group roles in referrer and casredirect services Signed-off-by: Jose I. Paris --- app/controlplane/cmd/wire_gen.go | 6 ++--- app/controlplane/pkg/biz/casmapping.go | 13 +++++----- app/controlplane/pkg/biz/membership.go | 12 +++------ app/controlplane/pkg/biz/project.go | 17 ++++-------- app/controlplane/pkg/biz/referrer.go | 26 +++++++++---------- .../pkg/biz/referrer_integration_test.go | 4 +-- .../pkg/biz/testhelpers/wire_gen.go | 4 +-- 7 files changed, 34 insertions(+), 48 deletions(-) diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index 05efc269b..9f9ff8a1d 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -98,9 +98,8 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l v2 := _wireValue casClientUseCase := biz.NewCASClientUseCase(casCredentialsUseCase, bootstrap_CASServer, logger, v2...) referrerRepo := data.NewReferrerRepo(dataData, workflowRepo, logger) - projectsRepo := data.NewProjectsRepo(dataData, logger) referrerSharedIndex := bootstrap.ReferrerSharedIndex - referrerUseCase, err := biz.NewReferrerUseCase(referrerRepo, workflowRepo, membershipRepo, projectsRepo, referrerSharedIndex, logger) + referrerUseCase, err := biz.NewReferrerUseCase(referrerRepo, workflowRepo, membershipUseCase, referrerSharedIndex, logger) if err != nil { cleanup() return nil, nil, err @@ -118,6 +117,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l cleanup() return nil, nil, err } + projectsRepo := data.NewProjectsRepo(dataData, logger) workflowContractRepo := data.NewWorkflowContractRepo(dataData, logger) v3 := bootstrap.PolicyProviders v4 := newPolicyProviderConfig(v3) @@ -168,7 +168,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l attestationUseCase := biz.NewAttestationUseCase(casClientUseCase, logger) fanOutDispatcher := dispatcher.New(integrationUseCase, workflowUseCase, workflowRunUseCase, readerWriter, casClientUseCase, availablePlugins, logger) casMappingRepo := data.NewCASMappingRepo(dataData, casBackendRepo, logger) - casMappingUseCase := biz.NewCASMappingUseCase(casMappingRepo, membershipRepo, projectsRepo, logger) + casMappingUseCase := biz.NewCASMappingUseCase(casMappingRepo, membershipUseCase, logger) v6 := bootstrap.PrometheusIntegration orgMetricsRepo := data.NewOrgMetricsRepo(dataData, logger) orgMetricsUseCase, err := biz.NewOrgMetricsUseCase(orgMetricsRepo, organizationRepo, workflowUseCase, logger) diff --git a/app/controlplane/pkg/biz/casmapping.go b/app/controlplane/pkg/biz/casmapping.go index 50307d6c8..9eb88a87e 100644 --- a/app/controlplane/pkg/biz/casmapping.go +++ b/app/controlplane/pkg/biz/casmapping.go @@ -52,14 +52,13 @@ type CASMappingRepo interface { } type CASMappingUseCase struct { - repo CASMappingRepo - membershipRepo MembershipRepo - projectsRepo ProjectsRepo - logger *log.Helper + repo CASMappingRepo + membershipUC *MembershipUseCase + logger *log.Helper } -func NewCASMappingUseCase(repo CASMappingRepo, mRepo MembershipRepo, pRepo ProjectsRepo, logger log.Logger) *CASMappingUseCase { - return &CASMappingUseCase{repo, mRepo, pRepo, servicelogger.ScopedHelper(logger, "cas-mapping-usecase")} +func NewCASMappingUseCase(repo CASMappingRepo, membershipUC *MembershipUseCase, logger log.Logger) *CASMappingUseCase { + return &CASMappingUseCase{repo, membershipUC, servicelogger.ScopedHelper(logger, "cas-mapping-usecase")} } type CASMappingCreateOpts struct { @@ -99,7 +98,7 @@ func (uc *CASMappingUseCase) FindCASMappingForDownloadByUser(ctx context.Context return nil, NewErrInvalidUUID(err) } - userOrgs, projectIDs, err := getOrgsAndRBACInfoForUser(ctx, userUUID, uc.membershipRepo, uc.projectsRepo) + userOrgs, projectIDs, err := uc.membershipUC.GetOrgsAndRBACInfoForUser(ctx, userUUID) if err != nil { return nil, err } diff --git a/app/controlplane/pkg/biz/membership.go b/app/controlplane/pkg/biz/membership.go index 9379767b9..c8b49654d 100644 --- a/app/controlplane/pkg/biz/membership.go +++ b/app/controlplane/pkg/biz/membership.go @@ -361,9 +361,9 @@ func (uc *MembershipUseCase) SetProjectOwner(ctx context.Context, orgID, project return nil } -func getOrgsAndRBACInfoForUser(ctx context.Context, userID uuid.UUID, mRepo MembershipRepo, pRepo ProjectsRepo) ([]uuid.UUID, map[uuid.UUID][]uuid.UUID, error) { +func (uc *MembershipUseCase) GetOrgsAndRBACInfoForUser(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, map[uuid.UUID][]uuid.UUID, error) { // Load ALL memberships for the given user - memberships, err := mRepo.ListAllByUser(ctx, userID) + memberships, err := uc.ListAllMembershipsForUser(ctx, userID) if err != nil { return nil, nil, fmt.Errorf("failed to list memberships: %w", err) } @@ -376,13 +376,9 @@ func getOrgsAndRBACInfoForUser(ctx context.Context, userID uuid.UUID, mRepo Memb userOrgs = append(userOrgs, m.ResourceID) // If the role in the org is member, we must enable RBAC for projects. if m.Role == authz.RoleOrgMember { - // get list of projects in org, and match it with the memberships to build a filter - orgProjects, err := getProjectsWithMembership(ctx, pRepo, m.ResourceID, memberships) - if err != nil { - return nil, nil, err - } + // get the list of projects in org, and match it with the memberships to build a filter. // note that appending an empty slice to a nil slice doesn't change it (it's still nil) - projectIDs[m.ResourceID] = orgProjects + projectIDs[m.ResourceID] = getProjectsWithMembershipInOrg(m.ResourceID, memberships) } } } diff --git a/app/controlplane/pkg/biz/project.go b/app/controlplane/pkg/biz/project.go index 7539c79ee..8c1666c2f 100644 --- a/app/controlplane/pkg/biz/project.go +++ b/app/controlplane/pkg/biz/project.go @@ -18,7 +18,6 @@ package biz import ( "context" "fmt" - "slices" "time" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/auditor/events" @@ -649,21 +648,15 @@ func (uc *ProjectUseCase) verifyRequesterHasPermissions(ctx context.Context, org } // getProjectsWithMembership returns the list of project IDs in the org for which the user has a membership -func getProjectsWithMembership(ctx context.Context, projectsRepo ProjectsRepo, orgID uuid.UUID, memberships []*Membership) ([]uuid.UUID, error) { +func getProjectsWithMembershipInOrg(orgID uuid.UUID, memberships []*Membership) []uuid.UUID { ids := make([]uuid.UUID, 0) - projects, err := projectsRepo.ListProjectsByOrgID(ctx, orgID) - if err != nil { - return nil, fmt.Errorf("listing projects: %w", err) - } - for _, p := range projects { - if slices.ContainsFunc(memberships, func(m *Membership) bool { - return m.ResourceType == authz.ResourceTypeProject && m.ResourceID == p.ID - }) { - ids = append(ids, p.ID) + for _, m := range memberships { + if m.ResourceType == authz.ResourceTypeProject && m.OrganizationID == orgID { + ids = append(ids, m.ResourceID) } } - return ids, nil + return ids } // UpdateMemberRole updates the role of a user or group in a project. diff --git a/app/controlplane/pkg/biz/referrer.go b/app/controlplane/pkg/biz/referrer.go index e8f3e6c03..9879128da 100644 --- a/app/controlplane/pkg/biz/referrer.go +++ b/app/controlplane/pkg/biz/referrer.go @@ -35,15 +35,14 @@ import ( ) type ReferrerUseCase struct { - repo ReferrerRepo - membershipRepo MembershipRepo - workflowRepo WorkflowRepo - projectsRepo ProjectsRepo - logger *log.Helper - indexConfig *conf.ReferrerSharedIndex + repo ReferrerRepo + membershipUseCase *MembershipUseCase + workflowRepo WorkflowRepo + logger *log.Helper + indexConfig *conf.ReferrerSharedIndex } -func NewReferrerUseCase(repo ReferrerRepo, wfRepo WorkflowRepo, mRepo MembershipRepo, projectsRepo ProjectsRepo, indexCfg *conf.ReferrerSharedIndex, l log.Logger) (*ReferrerUseCase, error) { +func NewReferrerUseCase(repo ReferrerRepo, wfRepo WorkflowRepo, membershipUseCase *MembershipUseCase, indexCfg *conf.ReferrerSharedIndex, l log.Logger) (*ReferrerUseCase, error) { if l == nil { l = log.NewStdLogger(io.Discard) } @@ -60,12 +59,11 @@ func NewReferrerUseCase(repo ReferrerRepo, wfRepo WorkflowRepo, mRepo Membership } return &ReferrerUseCase{ - repo: repo, - membershipRepo: mRepo, - indexConfig: indexCfg, - workflowRepo: wfRepo, - projectsRepo: projectsRepo, - logger: logger, + repo: repo, + membershipUseCase: membershipUseCase, + indexConfig: indexCfg, + workflowRepo: wfRepo, + logger: logger, }, nil } @@ -172,7 +170,7 @@ func (s *ReferrerUseCase) GetFromRootUser(ctx context.Context, digest, rootKind, return nil, NewErrInvalidUUID(err) } - userOrgs, projectIDs, err := getOrgsAndRBACInfoForUser(ctx, userUUID, s.membershipRepo, s.projectsRepo) + userOrgs, projectIDs, err := s.membershipUseCase.GetOrgsAndRBACInfoForUser(ctx, userUUID) if err != nil { return nil, err } diff --git a/app/controlplane/pkg/biz/referrer_integration_test.go b/app/controlplane/pkg/biz/referrer_integration_test.go index a88f152cc..fb736815e 100644 --- a/app/controlplane/pkg/biz/referrer_integration_test.go +++ b/app/controlplane/pkg/biz/referrer_integration_test.go @@ -87,7 +87,7 @@ func (s *referrerIntegrationTestSuite) TestGetFromRootInPublicSharedIndex() { }) s.T().Run("it should appear if we whitelist org2", func(t *testing.T) { - uc, err := biz.NewReferrerUseCase(s.Repos.Referrer, s.Repos.Workflow, s.Repos.Membership, nil, + uc, err := biz.NewReferrerUseCase(s.Repos.Referrer, s.Repos.Workflow, s.Membership, &conf.ReferrerSharedIndex{ Enabled: true, AllowedOrgs: []string{s.org2.ID}, @@ -463,7 +463,7 @@ func (s *referrerIntegrationTestSuite) SetupTest() { _, err = s.Membership.Create(ctx, s.org2.ID, s.user2.ID, biz.WithCurrentMembership()) require.NoError(s.T(), err) - s.sharedEnabledUC, err = biz.NewReferrerUseCase(s.Repos.Referrer, s.Repos.Workflow, s.Repos.Membership, nil, + s.sharedEnabledUC, err = biz.NewReferrerUseCase(s.Repos.Referrer, s.Repos.Workflow, s.Membership, &conf.ReferrerSharedIndex{ Enabled: true, AllowedOrgs: []string{s.org1.ID}, diff --git a/app/controlplane/pkg/biz/testhelpers/wire_gen.go b/app/controlplane/pkg/biz/testhelpers/wire_gen.go index ab073804e..786e6afb8 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire_gen.go +++ b/app/controlplane/pkg/biz/testhelpers/wire_gen.go @@ -116,7 +116,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r robotAccountRepo := data.NewRobotAccountRepo(dataData, logger) robotAccountUseCase := biz.NewRootAccountUseCase(robotAccountRepo, workflowRepo, auth, logger) casMappingRepo := data.NewCASMappingRepo(dataData, casBackendRepo, logger) - casMappingUseCase := biz.NewCASMappingUseCase(casMappingRepo, membershipRepo, projectsRepo, logger) + casMappingUseCase := biz.NewCASMappingUseCase(casMappingRepo, membershipUseCase, logger) orgInvitationRepo := data.NewOrgInvitation(dataData, logger) groupRepo := data.NewGroupRepo(dataData, logger) orgInvitationUseCase, err := biz.NewOrgInvitationUseCase(orgInvitationRepo, membershipRepo, userRepo, auditorUseCase, groupRepo, projectsRepo, logger) @@ -126,7 +126,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r } referrerRepo := data.NewReferrerRepo(dataData, workflowRepo, logger) referrerSharedIndex := _wireReferrerSharedIndexValue - referrerUseCase, err := biz.NewReferrerUseCase(referrerRepo, workflowRepo, membershipRepo, projectsRepo, referrerSharedIndex, logger) + referrerUseCase, err := biz.NewReferrerUseCase(referrerRepo, workflowRepo, membershipUseCase, referrerSharedIndex, logger) if err != nil { cleanup() return nil, nil, err From 8a6d49217d235decc3c1583ea94f5577229c4ba0 Mon Sep 17 00:00:00 2001 From: "Jose I. Paris" Date: Thu, 10 Jul 2025 17:31:25 +0200 Subject: [PATCH 2/2] fix tests Signed-off-by: Jose I. Paris --- app/controlplane/pkg/biz/casmapping_test.go | 2 +- app/controlplane/pkg/biz/referrer_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controlplane/pkg/biz/casmapping_test.go b/app/controlplane/pkg/biz/casmapping_test.go index 3979a4ead..ea3cf11b2 100644 --- a/app/controlplane/pkg/biz/casmapping_test.go +++ b/app/controlplane/pkg/biz/casmapping_test.go @@ -162,7 +162,7 @@ type casMappingSuite struct { func (s *casMappingSuite) SetupTest() { s.repo = repoM.NewCASMappingRepo(s.T()) - s.useCase = biz.NewCASMappingUseCase(s.repo, nil, nil, nil) + s.useCase = biz.NewCASMappingUseCase(s.repo, nil, nil) } func TestCASMapping(t *testing.T) { diff --git a/app/controlplane/pkg/biz/referrer_test.go b/app/controlplane/pkg/biz/referrer_test.go index 7cff735be..b13131eea 100644 --- a/app/controlplane/pkg/biz/referrer_test.go +++ b/app/controlplane/pkg/biz/referrer_test.go @@ -70,7 +70,7 @@ func (s *referrerTestSuite) TestInitialization() { for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { - _, err := NewReferrerUseCase(nil, nil, nil, nil, tc.conf, nil) + _, err := NewReferrerUseCase(nil, nil, nil, tc.conf, nil) if tc.wantErrMsg != "" { assert.EqualError(t, err, tc.wantErrMsg) } else {