From da1637e1d3f93ca9128bca72d10afc8b4ed72ef7 Mon Sep 17 00:00:00 2001 From: Chlins Zhang Date: Mon, 20 Feb 2023 14:22:27 +0800 Subject: [PATCH] fix: resolve the oidc or ldap group user cannot export cve (#18219) Remove the project filter in the scan data export job as they have been validated by API handler, fix the oidc or ldap group users cannot export cve. Fixes: #18112 Signed-off-by: chlins --- src/controller/scandataexport/execution.go | 13 +- .../scandataexport/execution_test.go | 1 + .../impl/scandataexport/scan_data_export.go | 9 +- .../scandataexport/scan_data_export_test.go | 121 +----------------- src/pkg/scan/export/filter_processor.go | 60 --------- src/pkg/scan/export/filter_processor_test.go | 70 ---------- src/server/v2.0/handler/scanexport.go | 6 +- .../pkg/scan/export/filter_processor.go | 23 ---- 8 files changed, 22 insertions(+), 281 deletions(-) diff --git a/src/controller/scandataexport/execution.go b/src/controller/scandataexport/execution.go index 430c3aec9..c1953f43c 100644 --- a/src/controller/scandataexport/execution.go +++ b/src/controller/scandataexport/execution.go @@ -7,7 +7,6 @@ import ( "time" "github.com/goharbor/harbor/src/jobservice/job" - "github.com/goharbor/harbor/src/jobservice/logger" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" @@ -66,6 +65,7 @@ func (c *controller) ListExecutions(ctx context.Context, userName string) ([]*ex } func (c *controller) GetTask(ctx context.Context, executionID int64) (*task.Task, error) { + logger := log.GetLogger(ctx) query := q2.New(q2.KeyWords{}) keywords := make(map[string]interface{}) @@ -90,6 +90,7 @@ func (c *controller) GetTask(ctx context.Context, executionID int64) (*task.Task } func (c *controller) GetExecution(ctx context.Context, executionID int64) (*export.Execution, error) { + logger := log.GetLogger(ctx) exec, err := c.execMgr.Get(ctx, executionID) if err != nil { logger.Errorf("Error when fetching execution status for ExecutionId: %d error : %v", executionID, err) @@ -103,6 +104,7 @@ func (c *controller) GetExecution(ctx context.Context, executionID int64) (*expo } func (c *controller) DeleteExecution(ctx context.Context, executionID int64) error { + logger := log.GetLogger(ctx) err := c.execMgr.Delete(ctx, executionID) if err != nil { logger.Errorf("Error when deleting execution for ExecutionId: %d, error : %v", executionID, err) @@ -188,12 +190,17 @@ func (c *controller) convertToExportExecStatus(ctx context.Context, exec *task.E if statusMessage, ok := exec.ExtraAttrs[export.StatusMessageAttribute]; ok { execStatus.StatusMessage = statusMessage.(string) } - artifactExists := c.isCsvArtifactPresent(ctx, exec.ID, execStatus.ExportDataDigest) - execStatus.FilePresent = artifactExists + + if len(execStatus.ExportDataDigest) > 0 { + artifactExists := c.isCsvArtifactPresent(ctx, exec.ID, execStatus.ExportDataDigest) + execStatus.FilePresent = artifactExists + } + return execStatus } func (c *controller) isCsvArtifactPresent(ctx context.Context, execID int64, digest string) bool { + logger := log.GetLogger(ctx) repositoryName := fmt.Sprintf("scandata_export_%v", execID) exists, err := c.sysArtifactMgr.Exists(ctx, strings.ToLower(export.Vendor), repositoryName, digest) if err != nil { diff --git a/src/controller/scandataexport/execution_test.go b/src/controller/scandataexport/execution_test.go index 6f536393f..18e10ed50 100644 --- a/src/controller/scandataexport/execution_test.go +++ b/src/controller/scandataexport/execution_test.go @@ -97,6 +97,7 @@ func (suite *ScanDataExportExecutionTestSuite) TestGetExecution() { attrs := make(map[string]interface{}) attrs[export.JobNameAttribute] = "test-job" attrs[export.UserNameAttribute] = "test-user" + attrs[export.DigestKey] = "sha256:d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa" attrs["status_message"] = "test-message" { exec := task.Execution{ diff --git a/src/jobservice/job/impl/scandataexport/scan_data_export.go b/src/jobservice/job/impl/scandataexport/scan_data_export.go index b54abcacd..9487fcef8 100644 --- a/src/jobservice/job/impl/scandataexport/scan_data_export.go +++ b/src/jobservice/job/impl/scandataexport/scan_data_export.go @@ -184,14 +184,7 @@ func (sde *ScanDataExport) writeCsvFile(ctx job.Context, params job.Parameters, return err } - // check if any projects are specified. If not then fetch all the projects - // of which the current user is a project admin. - projectIds, err := sde.filterProcessor.ProcessProjectFilter(systemContext, filterCriteria.UserName, filterCriteria.Projects) - - if err != nil { - return err - } - + projectIds := filterCriteria.Projects if len(projectIds) == 0 { return nil } diff --git a/src/jobservice/job/impl/scandataexport/scan_data_export_test.go b/src/jobservice/job/impl/scandataexport/scan_data_export_test.go index 36261fb69..6f3bb2d51 100644 --- a/src/jobservice/job/impl/scandataexport/scan_data_export_test.go +++ b/src/jobservice/job/impl/scandataexport/scan_data_export_test.go @@ -73,7 +73,6 @@ func (suite *ScanDataExportJobTestSuite) TestRun() { data := suite.createDataRecords(3) mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil) - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessLabelFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() @@ -86,7 +85,9 @@ func (suite *ScanDataExportJobTestSuite) TestRun() { params := job.Parameters{} params[export.JobModeKey] = export.JobModeExport params["JobId"] = JobId - params["Request"] = map[string]interface{}{} + params["Request"] = map[string]interface{}{ + "projects": []int64{1}, + } ctx := &mockjobservice.MockJobContext{} err := suite.job.Run(ctx, params) @@ -138,7 +139,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() { data := suite.createDataRecords(3) mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessLabelFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() @@ -152,7 +152,9 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() { params := job.Parameters{} params[export.JobModeKey] = export.JobModeExport params["JobId"] = JobId - params["Request"] = map[string]interface{}{} + params["Request"] = map[string]interface{}{ + "projects": []int{1}, + } ctx := &mockjobservice.MockJobContext{} err := suite.job.Run(ctx, params) @@ -209,7 +211,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() { repoCandidates := []int64{1} artCandidates := []*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1, Digest: "digest1"}}} - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil) mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(artCandidates, nil) mock.OnAnything(suite.filterProcessor, "ProcessLabelFilter").Return(artCandidates, nil) @@ -274,7 +275,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() { repoCandidate1 := &selector.Candidate{NamespaceID: 1} repoCandidates := []*selector.Candidate{repoCandidate1} - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil) mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil) @@ -321,111 +321,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() { } } -func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForProjectIdFilter() { - { - data := suite.createDataRecords(3) - - mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() - mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), nil).Once() - mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil) - mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() - mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), nil).Once() - mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil) - execAttrs := make(map[string]interface{}) - execAttrs[export.JobNameAttribute] = "test-job" - execAttrs[export.UserNameAttribute] = "test-user" - mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return(nil, errors.New("test error")).Once() - mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(nil, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, nil).Once() - - criteria := export.Request{ - CVEIds: "CVE-123", - Labels: []int64{1}, - Projects: []int64{1}, - Repositories: "test-repo", - Tags: "test-tag", - } - criteriaMap := make(map[string]interface{}) - bytes, _ := json.Marshal(criteria) - json.Unmarshal(bytes, &criteriaMap) - params := job.Parameters{} - params[export.JobModeKey] = export.JobModeExport - params["JobId"] = JobId - params["Request"] = criteriaMap - - ctx := &mockjobservice.MockJobContext{} - ctx.On("SystemContext").Return(context.TODO()).Once() - - err := suite.job.Run(ctx, params) - suite.Error(err) - sysArtifactRecordMatcher := testifymock.MatchedBy(func(sa *model.SystemArtifact) bool { - return sa.Repository == "scandata_export_100" && sa.Vendor == strings.ToLower(export.Vendor) && sa.Digest == MockDigest - }) - suite.sysArtifactMgr.AssertNotCalled(suite.T(), "Create", mock.Anything, sysArtifactRecordMatcher, mock.Anything) - suite.execMgr.AssertNotCalled(suite.T(), "UpdateExtraAttrs", mock.Anything, int64(JobId), mock.Anything) - _, err = os.Stat("/tmp/scandata_export_100.csv") - - suite.exportMgr.AssertNotCalled(suite.T(), "Fetch", mock.Anything, mock.Anything) - - suite.Truef(os.IsNotExist(err), "Expected CSV file to be deleted") - } - - // empty list of projects - { - data := suite.createDataRecords(3) - - mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() - mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), nil).Once() - mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil) - mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once() - mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), nil).Once() - mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil) - execAttrs := make(map[string]interface{}) - execAttrs[export.JobNameAttribute] = "test-job" - execAttrs[export.UserNameAttribute] = "test-user" - mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() - - criteria := export.Request{ - CVEIds: "CVE-123", - Labels: []int64{1}, - Projects: []int64{1}, - Repositories: "test-repo", - Tags: "test-tag", - } - criteriaMap := make(map[string]interface{}) - bytes, _ := json.Marshal(criteria) - json.Unmarshal(bytes, &criteriaMap) - params := job.Parameters{} - params[export.JobModeKey] = export.JobModeExport - params["JobId"] = JobId - params["Request"] = criteriaMap - - ctx := &mockjobservice.MockJobContext{} - ctx.On("SystemContext").Return(context.TODO()).Once() - - err := suite.job.Run(ctx, params) - suite.NoError(err) - sysArtifactRecordMatcher := testifymock.MatchedBy(func(sa *model.SystemArtifact) bool { - return sa.Repository == "scandata_export_100" && sa.Vendor == strings.ToLower(export.Vendor) && sa.Digest == MockDigest - }) - suite.sysArtifactMgr.AssertNotCalled(suite.T(), "Create", mock.Anything, sysArtifactRecordMatcher, mock.Anything) - - suite.execMgr.AssertCalled(suite.T(), "UpdateExtraAttrs", mock.Anything, int64(JobId), mock.Anything) - _, err = os.Stat("/tmp/scandata_export_100.csv") - - suite.exportMgr.AssertNotCalled(suite.T(), "Fetch", mock.Anything, mock.Anything) - - suite.Truef(os.IsNotExist(err), "Expected CSV file to be deleted") - } - -} - func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilter() { { data := suite.createDataRecords(3) @@ -441,7 +336,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte execAttrs[export.UserNameAttribute] = "test-user" mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, errors.New("test error")).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, errors.New("test error")).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once() @@ -492,7 +386,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte execAttrs[export.UserNameAttribute] = "test-user" mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{}, nil).Once() @@ -545,7 +438,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT execAttrs[export.UserNameAttribute] = "test-user" mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, errors.New("test error")).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, errors.New("test error")).Once() @@ -596,7 +488,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT execAttrs[export.UserNameAttribute] = "test-user" mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once() - mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once() mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, nil).Once() diff --git a/src/pkg/scan/export/filter_processor.go b/src/pkg/scan/export/filter_processor.go index 0e60e6adf..26a99f2e7 100644 --- a/src/pkg/scan/export/filter_processor.go +++ b/src/pkg/scan/export/filter_processor.go @@ -3,24 +3,19 @@ package export import ( "context" - commonmodels "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/common/security/local" "github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/controller/artifact" - "github.com/goharbor/harbor/src/jobservice/logger" "github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/lib/selector" "github.com/goharbor/harbor/src/lib/selector/selectors/doublestar" "github.com/goharbor/harbor/src/pkg" artpkg "github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/project" - "github.com/goharbor/harbor/src/pkg/project/models" "github.com/goharbor/harbor/src/pkg/repository" "github.com/goharbor/harbor/src/pkg/user" ) type FilterProcessor interface { - ProcessProjectFilter(ctx context.Context, userName string, projectsToFilter []int64) ([]int64, error) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) ProcessTagFilter(ctx context.Context, filter string, repositoryIds []int64) ([]*artifact.Artifact, error) ProcessLabelFilter(ctx context.Context, labelIDs []int64, arts []*artifact.Artifact) ([]*artifact.Artifact, error) @@ -43,50 +38,6 @@ func NewFilterProcessor() FilterProcessor { } } -func (dfp *DefaultFilterProcessor) ProcessProjectFilter(ctx context.Context, userName string, projectIdsToFilter []int64) ([]int64, error) { - // get the user id of the current user - - usr, err := dfp.usrMgr.GetByName(ctx, userName) - if err != nil { - return nil, err - } - logger.Infof("Retrieved user id :%d for user name : %s", usr.UserID, userName) - - if err != nil { - return nil, err - } - - query := dfp.getProjectQueryFilter(usr) - projects, err := dfp.projectMgr.List(ctx, query) - - if err != nil { - return nil, err - } - logger.Infof("Selected %d projects administered by user %s ", len(projects), userName) - projectIds := make([]int64, 0) - for _, proj := range projects { - projectIds = append(projectIds, proj.ProjectID) - } - - // check if the project ids specified in the filter are present in the list - // of projects of which the current user is a project admin - if len(projectIdsToFilter) == 0 { - return projectIds, nil - } - m := make(map[int64]bool) - for _, projectID := range projectIds { - m[projectID] = true - } - filtered := make([]int64, 0) - - for _, filteredProjID := range projectIdsToFilter { - if m[filteredProjID] { - filtered = append(filtered, filteredProjID) - } - } - return filtered, nil -} - func (dfp *DefaultFilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) { sel := doublestar.New(doublestar.RepoMatches, filter, "") candidates := make([]*selector.Candidate, 0) @@ -223,14 +174,3 @@ func (dfp *DefaultFilterProcessor) ProcessLabelFilter(ctx context.Context, label return filteredArts, nil } - -func (dfp *DefaultFilterProcessor) getProjectQueryFilter(user *commonmodels.User) *q.Query { - secContext := local.NewSecurityContext(user) - if secContext.IsSysAdmin() { - logger.Infof("User %v is sys admin. Selecting all projects for export.", user.Username) - return q.New(q.KeyWords{}) - } - - logger.Infof("User %v is not sys admin. Selecting projects with admin roles for export.", user.Username) - return q.New(q.KeyWords{"member": &models.MemberQuery{UserID: user.UserID, GroupIDs: user.GroupIDs}}) -} diff --git a/src/pkg/scan/export/filter_processor_test.go b/src/pkg/scan/export/filter_processor_test.go index fc6ae9d4e..b32760eb0 100644 --- a/src/pkg/scan/export/filter_processor_test.go +++ b/src/pkg/scan/export/filter_processor_test.go @@ -3,21 +3,15 @@ package export import ( "context" "errors" - "reflect" "testing" "time" - testifymock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - commonmodels "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/controller/artifact" - project3 "github.com/goharbor/harbor/src/controller/project" "github.com/goharbor/harbor/src/controller/tag" - "github.com/goharbor/harbor/src/lib/q" artpkg "github.com/goharbor/harbor/src/pkg/artifact" labelmodel "github.com/goharbor/harbor/src/pkg/label/model" - "github.com/goharbor/harbor/src/pkg/project/models" "github.com/goharbor/harbor/src/pkg/repository/model" tagmodel "github.com/goharbor/harbor/src/pkg/tag/model/tag" artifactctl "github.com/goharbor/harbor/src/testing/controller/artifact" @@ -53,70 +47,6 @@ func (suite *FilterProcessorTestSuite) SetupTest() { } } -func (suite *FilterProcessorTestSuite) TestProcessProjectFilter() { - project1 := &models.Project{ProjectID: 1} - - project2 := &models.Project{ProjectID: 2} - - // no filtered projects returns all projects - { - suite.usrMgr.On("GetByName", mock.Anything, "test-user").Return(&commonmodels.User{UserID: 1}, nil).Once() - suite.projectMgr.On("List", mock.Anything, mock.Anything).Return([]*models.Project{project1, project2}, nil).Once() - projectIds, err := suite.filterProcessor.ProcessProjectFilter(context.TODO(), "test-user", []int64{}) - suite.Equal(2, len(projectIds)) - suite.NoError(err) - } - - // filtered project - { - suite.usrMgr.On("GetByName", mock.Anything, "test-user").Return(&commonmodels.User{UserID: 1}, nil).Once() - suite.projectMgr.On("List", mock.Anything, mock.Anything).Return([]*models.Project{project1, project2}, nil).Once() - projectIds, err := suite.filterProcessor.ProcessProjectFilter(context.TODO(), "test-user", []int64{1}) - suite.Equal(1, len(projectIds)) - suite.Equal(int64(1), projectIds[0]) - suite.NoError(err) - } - - // filtered project with group ids - { - groupIDs := []int{4, 5} - suite.usrMgr.On("GetByName", mock.Anything, "test-user").Return(&commonmodels.User{UserID: 1, GroupIDs: groupIDs}, nil).Once() - suite.projectMgr.On("List", mock.Anything, mock.Anything).Return([]*models.Project{project1, project2}, nil).Once() - projectIds, err := suite.filterProcessor.ProcessProjectFilter(context.TODO(), "test-user", []int64{1}) - suite.Equal(1, len(projectIds)) - suite.Equal(int64(1), projectIds[0]) - suite.NoError(err) - memberQueryMatcher := testifymock.MatchedBy(func(query *q.Query) bool { - memberQuery := query.Keywords["member"].(*project3.MemberQuery) - return len(memberQuery.GroupIDs) == 2 && reflect.DeepEqual(memberQuery.GroupIDs, groupIDs) && memberQuery.Role == 0 - }) - suite.projectMgr.AssertCalled(suite.T(), "List", mock.Anything, memberQueryMatcher) - } - - // project listing for admin user - { - suite.usrMgr.On("GetByName", mock.Anything, "test-user").Return(&commonmodels.User{UserID: 1, SysAdminFlag: true}, nil).Once() - suite.projectMgr.On("List", mock.Anything, mock.Anything).Return([]*models.Project{project1, project2}, nil).Once() - _, err := suite.filterProcessor.ProcessProjectFilter(context.TODO(), "test-user", []int64{1}) - suite.NoError(err) - queryArgumentMatcher := testifymock.MatchedBy(func(query *q.Query) bool { - return len(query.Keywords) == 0 - }) - suite.projectMgr.AssertCalled(suite.T(), "List", mock.Anything, queryArgumentMatcher) - } - - // project listing returns an error - // filtered project - { - suite.usrMgr.On("GetByName", mock.Anything, "test-user").Return(&commonmodels.User{UserID: 1}, nil).Once() - suite.projectMgr.On("List", mock.Anything, mock.Anything).Return(nil, errors.New("test-error")).Once() - projectIds, err := suite.filterProcessor.ProcessProjectFilter(context.TODO(), "test-user", []int64{1}) - suite.Error(err) - suite.Nil(projectIds) - } - -} - func (suite *FilterProcessorTestSuite) TestProcessRepositoryFilter() { repoRecord1 := model.RepoRecord{ diff --git a/src/server/v2.0/handler/scanexport.go b/src/server/v2.0/handler/scanexport.go index ac9ec3fb8..3c07a994d 100644 --- a/src/server/v2.0/handler/scanexport.go +++ b/src/server/v2.0/handler/scanexport.go @@ -54,8 +54,10 @@ func (se *scanDataExportAPI) ExportScanData(ctx context.Context, params operatio return se.SendError(ctx, err) } - if err := se.RequireProjectAccess(ctx, params.Criteria.Projects[0], rbac.ActionCreate, rbac.ResourceExportCVE); err != nil { - return se.SendError(ctx, err) + for _, pid := range params.Criteria.Projects { + if err := se.RequireProjectAccess(ctx, pid, rbac.ActionCreate, rbac.ResourceExportCVE); err != nil { + return se.SendError(ctx, err) + } } scanDataExportJob := new(models.ScanDataExportJob) diff --git a/src/testing/pkg/scan/export/filter_processor.go b/src/testing/pkg/scan/export/filter_processor.go index be9af0f80..eead8ceec 100644 --- a/src/testing/pkg/scan/export/filter_processor.go +++ b/src/testing/pkg/scan/export/filter_processor.go @@ -38,29 +38,6 @@ func (_m *FilterProcessor) ProcessLabelFilter(ctx context.Context, labelIDs []in return r0, r1 } -// ProcessProjectFilter provides a mock function with given fields: ctx, userName, projectsToFilter -func (_m *FilterProcessor) ProcessProjectFilter(ctx context.Context, userName string, projectsToFilter []int64) ([]int64, error) { - ret := _m.Called(ctx, userName, projectsToFilter) - - var r0 []int64 - if rf, ok := ret.Get(0).(func(context.Context, string, []int64) []int64); ok { - r0 = rf(ctx, userName, projectsToFilter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int64) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, []int64) error); ok { - r1 = rf(ctx, userName, projectsToFilter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ProcessRepositoryFilter provides a mock function with given fields: ctx, filter, projectIds func (_m *FilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) { ret := _m.Called(ctx, filter, projectIds)