mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-29 22:07:32 +02:00
[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:
parent
5c3465cd70
commit
953ce9b782
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"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/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"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) {
|
func (c *controller) GetTask(ctx context.Context, executionID int64) (*task.Task, error) {
|
||||||
|
logger := log.GetLogger(ctx)
|
||||||
query := q2.New(q2.KeyWords{})
|
query := q2.New(q2.KeyWords{})
|
||||||
|
|
||||||
keywords := make(map[string]interface{})
|
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) {
|
func (c *controller) GetExecution(ctx context.Context, executionID int64) (*export.Execution, error) {
|
||||||
|
logger := log.GetLogger(ctx)
|
||||||
exec, err := c.execMgr.Get(ctx, executionID)
|
exec, err := c.execMgr.Get(ctx, executionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error when fetching execution status for ExecutionId: %d error : %v", executionID, err)
|
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 {
|
func (c *controller) DeleteExecution(ctx context.Context, executionID int64) error {
|
||||||
|
logger := log.GetLogger(ctx)
|
||||||
err := c.execMgr.Delete(ctx, executionID)
|
err := c.execMgr.Delete(ctx, executionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error when deleting execution for ExecutionId: %d, error : %v", executionID, err)
|
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 {
|
if statusMessage, ok := exec.ExtraAttrs["status_message"]; ok {
|
||||||
execStatus.StatusMessage = statusMessage.(string)
|
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
|
return execStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) isCsvArtifactPresent(ctx context.Context, execID int64, digest string) bool {
|
func (c *controller) isCsvArtifactPresent(ctx context.Context, execID int64, digest string) bool {
|
||||||
|
logger := log.GetLogger(ctx)
|
||||||
repositoryName := fmt.Sprintf("scandata_export_%v", execID)
|
repositoryName := fmt.Sprintf("scandata_export_%v", execID)
|
||||||
exists, err := c.sysArtifactMgr.Exists(ctx, strings.ToLower(export.Vendor), repositoryName, digest)
|
exists, err := c.sysArtifactMgr.Exists(ctx, strings.ToLower(export.Vendor), repositoryName, digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -97,6 +97,7 @@ func (suite *ScanDataExportExecutionTestSuite) TestGetExecution() {
|
|||||||
attrs := make(map[string]interface{})
|
attrs := make(map[string]interface{})
|
||||||
attrs[export.JobNameAttribute] = "test-job"
|
attrs[export.JobNameAttribute] = "test-job"
|
||||||
attrs[export.UserNameAttribute] = "test-user"
|
attrs[export.UserNameAttribute] = "test-user"
|
||||||
|
attrs[export.DigestKey] = "sha256:d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"
|
||||||
attrs["status_message"] = "test-message"
|
attrs["status_message"] = "test-message"
|
||||||
{
|
{
|
||||||
exec := task.Execution{
|
exec := task.Execution{
|
||||||
|
@ -182,14 +182,7 @@ func (sde *ScanDataExport) writeCsvFile(ctx job.Context, params job.Parameters,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if any projects are specified. If not then fetch all the projects
|
projectIds := filterCriteria.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
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(projectIds) == 0 {
|
if len(projectIds) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,9 @@ func (suite *ScanDataExportJobTestSuite) TestRun() {
|
|||||||
mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once()
|
mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once()
|
||||||
mock.OnAnything(suite.exportMgr, "Fetch").Return(make([]export.Data, 0), 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.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 := make(map[string]interface{})
|
||||||
execAttrs[export.JobNameAttribute] = "test-job"
|
execAttrs[export.JobNameAttribute] = "test-job"
|
||||||
@ -83,6 +86,9 @@ func (suite *ScanDataExportJobTestSuite) TestRun() {
|
|||||||
params := job.Parameters{}
|
params := job.Parameters{}
|
||||||
params[export.JobModeKey] = export.JobModeExport
|
params[export.JobModeKey] = export.JobModeExport
|
||||||
params["JobId"] = JobId
|
params["JobId"] = JobId
|
||||||
|
params["Request"] = map[string]interface{}{
|
||||||
|
"projects": []int64{1},
|
||||||
|
}
|
||||||
ctx := &mockjobservice.MockJobContext{}
|
ctx := &mockjobservice.MockJobContext{}
|
||||||
|
|
||||||
err := suite.job.Run(ctx, params)
|
err := suite.job.Run(ctx, params)
|
||||||
@ -144,7 +150,9 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() {
|
|||||||
|
|
||||||
data := suite.createDataRecords(3, 1)
|
data := suite.createDataRecords(3, 1)
|
||||||
mock.OnAnything(suite.exportMgr, "Fetch").Return(data, nil).Once()
|
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)
|
mock.OnAnything(suite.digestCalculator, "Calculate").Return(digest.Digest(MockDigest), nil)
|
||||||
|
|
||||||
execAttrs := make(map[string]interface{})
|
execAttrs := make(map[string]interface{})
|
||||||
@ -155,6 +163,9 @@ func (suite *ScanDataExportJobTestSuite) TestRunAttributeUpdateError() {
|
|||||||
params := job.Parameters{}
|
params := job.Parameters{}
|
||||||
params[export.JobModeKey] = export.JobModeExport
|
params[export.JobModeKey] = export.JobModeExport
|
||||||
params["JobId"] = JobId
|
params["JobId"] = JobId
|
||||||
|
params["Request"] = map[string]interface{}{
|
||||||
|
"projects": []int{1},
|
||||||
|
}
|
||||||
ctx := &mockjobservice.MockJobContext{}
|
ctx := &mockjobservice.MockJobContext{}
|
||||||
|
|
||||||
err := suite.job.Run(ctx, params)
|
err := suite.job.Run(ctx, params)
|
||||||
@ -211,7 +222,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() {
|
|||||||
|
|
||||||
repoCandidates := []int64{1}
|
repoCandidates := []int64{1}
|
||||||
artCandidates := []*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1, Digest: "digest1"}}}
|
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, "ProcessRepositoryFilter").Return(repoCandidates, nil)
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(artCandidates, nil)
|
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(artCandidates, nil)
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessLabelFilter").Return(artCandidates, nil)
|
mock.OnAnything(suite.filterProcessor, "ProcessLabelFilter").Return(artCandidates, nil)
|
||||||
@ -276,7 +286,6 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteria() {
|
|||||||
|
|
||||||
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
|
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
|
||||||
repoCandidates := []*selector.Candidate{repoCandidate1}
|
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, "ProcessRepositoryFilter").Return(repoCandidates, nil)
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").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() {
|
func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilter() {
|
||||||
{
|
{
|
||||||
data := suite.createDataRecords(3, 1)
|
data := suite.createDataRecords(3, 1)
|
||||||
@ -447,11 +347,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte
|
|||||||
execAttrs[export.UserNameAttribute] = "test-user"
|
execAttrs[export.UserNameAttribute] = "test-user"
|
||||||
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
||||||
|
|
||||||
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
|
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, errors.New("test error")).Once()
|
||||||
repoCandidates := []*selector.Candidate{repoCandidate1}
|
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{{Artifact: artpkg.Artifact{ID: 1}}}, nil).Once()
|
||||||
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)
|
|
||||||
|
|
||||||
criteria := export.Request{
|
criteria := export.Request{
|
||||||
CVEIds: "CVE-123",
|
CVEIds: "CVE-123",
|
||||||
@ -500,10 +397,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdFilte
|
|||||||
execAttrs[export.UserNameAttribute] = "test-user"
|
execAttrs[export.UserNameAttribute] = "test-user"
|
||||||
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
||||||
|
|
||||||
repoCandidates := make([]*selector.Candidate, 0)
|
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once()
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once()
|
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return([]*artifact.Artifact{}, nil).Once()
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
|
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(repoCandidates, nil)
|
|
||||||
|
|
||||||
criteria := export.Request{
|
criteria := export.Request{
|
||||||
CVEIds: "CVE-123",
|
CVEIds: "CVE-123",
|
||||||
@ -554,11 +449,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT
|
|||||||
execAttrs[export.UserNameAttribute] = "test-user"
|
execAttrs[export.UserNameAttribute] = "test-user"
|
||||||
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
||||||
|
|
||||||
repoCandidate1 := &selector.Candidate{NamespaceID: 1}
|
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{1}, nil).Once()
|
||||||
repoCandidates := []*selector.Candidate{repoCandidate1}
|
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, errors.New("test error")).Once()
|
||||||
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"))
|
|
||||||
|
|
||||||
criteria := export.Request{
|
criteria := export.Request{
|
||||||
CVEIds: "CVE-123",
|
CVEIds: "CVE-123",
|
||||||
@ -607,10 +499,8 @@ func (suite *ScanDataExportJobTestSuite) TestRunWithCriteriaForRepositoryIdWithT
|
|||||||
execAttrs[export.UserNameAttribute] = "test-user"
|
execAttrs[export.UserNameAttribute] = "test-user"
|
||||||
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
mock.OnAnything(suite.execMgr, "Get").Return(&task.Execution{ID: int64(JobId), ExtraAttrs: execAttrs}, nil).Once()
|
||||||
|
|
||||||
repoCandidates := make([]*selector.Candidate, 0)
|
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return([]int64{}, nil).Once()
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessProjectFilter").Return([]int64{}, nil).Once()
|
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(nil, nil).Once()
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessRepositoryFilter").Return(repoCandidates, nil)
|
|
||||||
mock.OnAnything(suite.filterProcessor, "ProcessTagFilter").Return(make([]*selector.Candidate, 0), nil)
|
|
||||||
|
|
||||||
criteria := export.Request{
|
criteria := export.Request{
|
||||||
CVEIds: "CVE-123",
|
CVEIds: "CVE-123",
|
||||||
|
@ -3,24 +3,19 @@ package export
|
|||||||
import (
|
import (
|
||||||
"context"
|
"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/common/utils"
|
||||||
"github.com/goharbor/harbor/src/controller/artifact"
|
"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/q"
|
||||||
"github.com/goharbor/harbor/src/lib/selector"
|
"github.com/goharbor/harbor/src/lib/selector"
|
||||||
"github.com/goharbor/harbor/src/lib/selector/selectors/doublestar"
|
"github.com/goharbor/harbor/src/lib/selector/selectors/doublestar"
|
||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
artpkg "github.com/goharbor/harbor/src/pkg/artifact"
|
artpkg "github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"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/repository"
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FilterProcessor interface {
|
type FilterProcessor interface {
|
||||||
ProcessProjectFilter(ctx context.Context, userName string, projectsToFilter []int64) ([]int64, error)
|
|
||||||
ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error)
|
ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error)
|
||||||
ProcessTagFilter(ctx context.Context, filter string, repositoryIds []int64) ([]*artifact.Artifact, error)
|
ProcessTagFilter(ctx context.Context, filter string, repositoryIds []int64) ([]*artifact.Artifact, error)
|
||||||
ProcessLabelFilter(ctx context.Context, labelIDs []int64, arts []*artifact.Artifact) ([]*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) {
|
func (dfp *DefaultFilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) {
|
||||||
sel := doublestar.New(doublestar.RepoMatches, filter, "")
|
sel := doublestar.New(doublestar.RepoMatches, filter, "")
|
||||||
candidates := make([]*selector.Candidate, 0)
|
candidates := make([]*selector.Candidate, 0)
|
||||||
@ -223,14 +174,3 @@ func (dfp *DefaultFilterProcessor) ProcessLabelFilter(ctx context.Context, label
|
|||||||
|
|
||||||
return filteredArts, nil
|
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}})
|
|
||||||
}
|
|
||||||
|
@ -3,21 +3,15 @@ package export
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
testifymock "github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
commonmodels "github.com/goharbor/harbor/src/common/models"
|
|
||||||
"github.com/goharbor/harbor/src/controller/artifact"
|
"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/controller/tag"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
|
||||||
artpkg "github.com/goharbor/harbor/src/pkg/artifact"
|
artpkg "github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
labelmodel "github.com/goharbor/harbor/src/pkg/label/model"
|
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"
|
"github.com/goharbor/harbor/src/pkg/repository/model"
|
||||||
tagmodel "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
tagmodel "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||||
artifactctl "github.com/goharbor/harbor/src/testing/controller/artifact"
|
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() {
|
func (suite *FilterProcessorTestSuite) TestProcessRepositoryFilter() {
|
||||||
|
|
||||||
repoRecord1 := model.RepoRecord{
|
repoRecord1 := model.RepoRecord{
|
||||||
|
@ -54,8 +54,10 @@ func (se *scanDataExportAPI) ExportScanData(ctx context.Context, params operatio
|
|||||||
return se.SendError(ctx, err)
|
return se.SendError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := se.RequireProjectAccess(ctx, params.Criteria.Projects[0], rbac.ActionCreate, rbac.ResourceExportCVE); err != nil {
|
for _, pid := range params.Criteria.Projects {
|
||||||
return se.SendError(ctx, err)
|
if err := se.RequireProjectAccess(ctx, pid, rbac.ActionCreate, rbac.ResourceExportCVE); err != nil {
|
||||||
|
return se.SendError(ctx, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scanDataExportJob := new(models.ScanDataExportJob)
|
scanDataExportJob := new(models.ScanDataExportJob)
|
||||||
|
@ -38,29 +38,6 @@ func (_m *FilterProcessor) ProcessLabelFilter(ctx context.Context, labelIDs []in
|
|||||||
return r0, r1
|
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
|
// ProcessRepositoryFilter provides a mock function with given fields: ctx, filter, projectIds
|
||||||
func (_m *FilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) {
|
func (_m *FilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) {
|
||||||
ret := _m.Called(ctx, filter, projectIds)
|
ret := _m.Called(ctx, filter, projectIds)
|
||||||
|
Loading…
Reference in New Issue
Block a user