[cherry-pick] fix: resolve the oidc or ldap group user cannot export cve (#18228)

fix: resolve the oidc or ldap group user cannot export cve

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 <chenyuzh@vmware.com>
This commit is contained in:
Chlins Zhang 2023-02-16 10:34:50 +08:00 committed by GitHub
parent 5c3465cd70
commit 953ce9b782
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 36 additions and 296 deletions

View File

@ -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)
@ -190,12 +192,17 @@ func (c *controller) convertToExportExecStatus(ctx context.Context, exec *task.E
if statusMessage, ok := exec.ExtraAttrs["status_message"]; 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 {

View File

@ -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{

View File

@ -182,14 +182,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
}

View File

@ -74,6 +74,9 @@ func (suite *ScanDataExportJobTestSuite) TestRun() {
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.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()
execAttrs := make(map[string]interface{})
execAttrs[export.JobNameAttribute] = "test-job"
@ -83,6 +86,9 @@ func (suite *ScanDataExportJobTestSuite) TestRun() {
params := job.Parameters{}
params[export.JobModeKey] = export.JobModeExport
params["JobId"] = JobId
params["Request"] = map[string]interface{}{
"projects": []int64{1},
}
ctx := &mockjobservice.MockJobContext{}
err := suite.job.Run(ctx, params)
@ -144,7 +150,9 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() {
data := suite.createDataRecords(3, 1)
mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once()
mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), 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()
mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil)
execAttrs := make(map[string]interface{})
@ -155,6 +163,9 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() {
params := job.Parameters{}
params[export.JobModeKey] = export.JobModeExport
params["JobId"] = JobId
params["Request"] = map[string]interface{}{
"projects": []int{1},
}
ctx := &mockjobservice.MockJobContext{}
err := suite.job.Run(ctx, params)
@ -211,7 +222,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)
@ -276,7 +286,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)
@ -323,115 +332,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() {
}
}
func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForProjectIdFilter() {
{
data := suite.createDataRecords(3, 1)
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()
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
repoCandidates := []*selector.Candidate{repoCandidate1}
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return(nil, errors.New("test error")).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil)
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, 1)
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()
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
repoCandidates := []*selector.Candidate{repoCandidate1}
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil)
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.AssertCalled(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, 1)
@ -447,11 +347,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte
execAttrs[export.UserNameAttribute] = "test-user"
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
repoCandidates := []*selector.Candidate{repoCandidate1}
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, errors.New("test error")).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(nil, errors.New("test error"))
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil)
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()
criteria := export.Request{
CVEIds: "CVE-123",
@ -500,10 +397,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte
execAttrs[export.UserNameAttribute] = "test-user"
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
repoCandidates := make([]*selector.Candidate, 0)
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{}, nil).Once()
criteria := export.Request{
CVEIds: "CVE-123",
@ -554,11 +449,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT
execAttrs[export.UserNameAttribute] = "test-user"
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
repoCandidates := []*selector.Candidate{repoCandidate1}
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{1}, errors.New("test error")).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, errors.New("test error"))
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, errors.New("test error")).Once()
criteria := export.Request{
CVEIds: "CVE-123",
@ -607,10 +499,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT
execAttrs[export.UserNameAttribute] = "test-user"
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
repoCandidates := make([]*selector.Candidate, 0)
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(make([]*selector.Candidate, 0), nil)
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once()
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, nil).Once()
criteria := export.Request{
CVEIds: "CVE-123",

View File

@ -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}})
}

View File

@ -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{

View File

@ -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)

View File

@ -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)