fix: update process of webhook and preheat policy (#17243)

1. Update the preheat policy API hander and DAO Get method.
2. Update the webhook policy and webhook job API handler.

Signed-off-by: chlins <chenyuzh@vmware.com>
This commit is contained in:
Chenyu Zhang 2022-07-27 01:12:02 +08:00 committed by GitHub
parent 9cbdb8cca1
commit 3897471486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 230 additions and 37 deletions

View File

@ -63,10 +63,13 @@ func (d *dao) Get(ctx context.Context, id int64) (*provider.Instance, error) {
}
di := provider.Instance{ID: id}
err = o.Read(&di, "ID")
if err == beego_orm.ErrNoRows {
return nil, nil
if err = o.Read(&di, "ID"); err != nil {
if e := orm.AsNotFoundError(err, "instance %d not found", id); e != nil {
err = e
}
return nil, err
}
return &di, err
}

View File

@ -5,13 +5,14 @@ import (
"testing"
beego_orm "github.com/astaxie/beego/orm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
common_dao "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
models "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
var (
@ -62,6 +63,7 @@ func (is *instanceSuite) TestGet() {
// not exist
i, err = is.dao.Get(is.ctx, 0)
assert.Nil(t, i)
assert.True(t, errors.IsNotFoundErr(err))
}
// TestCreate tests create instance.

View File

@ -2,10 +2,15 @@ package handler
import (
"context"
"github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/pkg/notification/job"
"github.com/goharbor/harbor/src/pkg/notification/policy"
policyModel "github.com/goharbor/harbor/src/pkg/notification/policy/model"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/webhookjob"
@ -16,6 +21,7 @@ func newNotificationJobAPI() *notificationJobAPI {
return &notificationJobAPI{
webhookjobMgr: job.Mgr,
webhookPolicyMgr: policy.Mgr,
projectMgr: project.Mgr,
}
}
@ -23,6 +29,7 @@ type notificationJobAPI struct {
BaseAPI
webhookjobMgr job.Manager
webhookPolicyMgr policy.Manager
projectMgr project.Manager
}
func (n *notificationJobAPI) ListWebhookJobs(ctx context.Context, params webhookjob.ListWebhookJobsParams) middleware.Responder {
@ -36,6 +43,10 @@ func (n *notificationJobAPI) ListWebhookJobs(ctx context.Context, params webhook
return n.SendError(ctx, err)
}
if err := n.requirePolicyAccess(ctx, projectNameOrID, policy); err != nil {
return n.SendError(ctx, err)
}
query, err := n.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
if err != nil {
return n.SendError(ctx, err)
@ -65,3 +76,17 @@ func (n *notificationJobAPI) ListWebhookJobs(ctx context.Context, params webhook
WithLink(n.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
WithPayload(results)
}
// requirePolicyAccess checks whether the project has the permission to the policy.
func (n *notificationJobAPI) requirePolicyAccess(ctx context.Context, projectNameIrID interface{}, policy *policyModel.Policy) error {
p, err := n.projectMgr.Get(ctx, projectNameIrID)
if err != nil {
return err
}
// check the projectID whether match with the projectID in policy
if p.ProjectID != policy.ProjectID {
return errors.NotFoundError(errors.Errorf("project id %d does not match", p.ProjectID))
}
return nil
}

View File

@ -1,14 +1,33 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package handler
import (
"context"
"fmt"
"strings"
"time"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notification/job"
@ -18,8 +37,6 @@ import (
"github.com/goharbor/harbor/src/server/v2.0/models"
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/webhook"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/webhook"
"strings"
"time"
)
func newNotificationPolicyAPI() *notificationPolicyAPI {
@ -39,6 +56,21 @@ func (n *notificationPolicyAPI) Prepare(ctx context.Context, operation string, p
return nil
}
func (n *notificationPolicyAPI) requirePolicyInProject(ctx context.Context, projectIDOrName interface{}, policyID int64) error {
projectID, err := getProjectID(ctx, projectIDOrName)
if err != nil {
return err
}
l, err := n.webhookPolicyMgr.Get(ctx, policyID)
if err != nil {
return err
}
if projectID != l.ProjectID {
return errors.NotFoundError(fmt.Errorf("project id:%d, webhook policy id: %d not found", projectID, policyID))
}
return nil
}
func (n *notificationPolicyAPI) ListWebhookPoliciesOfProject(ctx context.Context, params webhook.ListWebhookPoliciesOfProjectParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := n.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionList, rbac.ResourceNotificationPolicy); err != nil {
@ -83,7 +115,9 @@ func (n *notificationPolicyAPI) CreateWebhookPolicyOfProject(ctx context.Context
}
policy := &policy_model.Policy{}
lib.JSONCopy(policy, params.Policy)
if err := lib.JSONCopy(policy, params.Policy); err != nil {
log.Warningf("failed to call JSONCopy on notification policy when CreateWebhookPolicyOfProject, error: %v", err)
}
if ok, err := n.validateEventTypes(policy); !ok {
return n.SendError(ctx, err)
@ -111,9 +145,18 @@ func (n *notificationPolicyAPI) UpdateWebhookPolicyOfProject(ctx context.Context
if err := n.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionUpdate, rbac.ResourceNotificationPolicy); err != nil {
return n.SendError(ctx, err)
}
projectID, err := getProjectID(ctx, projectNameOrID)
if err != nil {
return n.SendError(ctx, err)
}
policyID := params.WebhookPolicyID
if err := n.requirePolicyInProject(ctx, projectID, policyID); err != nil {
return n.SendError(ctx, err)
}
policy := &policy_model.Policy{}
lib.JSONCopy(policy, params.Policy)
if err := lib.JSONCopy(policy, params.Policy); err != nil {
log.Warningf("failed to call JSONCopy on notification policy when UpdateWebhookPolicyOfProject, error: %v", err)
}
if ok, err := n.validateEventTypes(policy); !ok {
return n.SendError(ctx, err)
@ -122,10 +165,7 @@ func (n *notificationPolicyAPI) UpdateWebhookPolicyOfProject(ctx context.Context
return n.SendError(ctx, err)
}
projectID, err := getProjectID(ctx, projectNameOrID)
if err != nil {
return n.SendError(ctx, err)
}
policy.ID = policyID
policy.ProjectID = projectID
if err := n.webhookPolicyMgr.Update(ctx, policy); err != nil {
return n.SendError(ctx, err)
@ -139,7 +179,9 @@ func (n *notificationPolicyAPI) DeleteWebhookPolicyOfProject(ctx context.Context
if err := n.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionDelete, rbac.ResourceNotificationPolicy); err != nil {
return n.SendError(ctx, err)
}
if err := n.requirePolicyInProject(ctx, projectNameOrID, params.WebhookPolicyID); err != nil {
return n.SendError(ctx, err)
}
if err := n.webhookPolicyMgr.Delete(ctx, params.WebhookPolicyID); err != nil {
return n.SendError(ctx, err)
}
@ -148,7 +190,14 @@ func (n *notificationPolicyAPI) DeleteWebhookPolicyOfProject(ctx context.Context
func (n *notificationPolicyAPI) GetWebhookPolicyOfProject(ctx context.Context, params webhook.GetWebhookPolicyOfProjectParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := n.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionRead, rbac.ResourceNotificationPolicy); err != nil {
projectID, err := getProjectID(ctx, projectNameOrID)
if err != nil {
return n.SendError(ctx, err)
}
if err := n.RequireProjectAccess(ctx, projectID, rbac.ActionRead, rbac.ResourceNotificationPolicy); err != nil {
return n.SendError(ctx, err)
}
if err := n.requirePolicyInProject(ctx, projectID, params.WebhookPolicyID); err != nil {
return n.SendError(ctx, err)
}
@ -257,28 +306,26 @@ func (n *notificationPolicyAPI) validateEventTypes(policy *policy_model.Policy)
// including event type, enabled, creation time, last trigger time
func (n *notificationPolicyAPI) constructPolicyWithTriggerTime(ctx context.Context, policies []*policy_model.Policy) ([]*models.WebhookLastTrigger, error) {
res := []*models.WebhookLastTrigger{}
if policies != nil {
for _, policy := range policies {
for _, t := range policy.EventTypes {
ply := &models.WebhookLastTrigger{
PolicyName: policy.Name,
EventType: t,
Enabled: policy.Enabled,
CreationTime: strfmt.DateTime(policy.CreationTime),
}
if !policy.CreationTime.IsZero() {
ply.CreationTime = strfmt.DateTime(policy.CreationTime)
}
ltTime, err := n.getLastTriggerTimeGroupByEventType(ctx, t, policy.ID)
if err != nil {
return nil, err
}
if !ltTime.IsZero() {
ply.LastTriggerTime = strfmt.DateTime(ltTime)
}
res = append(res, ply)
for _, policy := range policies {
for _, t := range policy.EventTypes {
ply := &models.WebhookLastTrigger{
PolicyName: policy.Name,
EventType: t,
Enabled: policy.Enabled,
CreationTime: strfmt.DateTime(policy.CreationTime),
}
if !policy.CreationTime.IsZero() {
ply.CreationTime = strfmt.DateTime(policy.CreationTime)
}
ltTime, err := n.getLastTriggerTimeGroupByEventType(ctx, t, policy.ID)
if err != nil {
return nil, err
}
if !ltTime.IsZero() {
ply.LastTriggerTime = strfmt.DateTime(ltTime)
}
res = append(res, ply)
}
}
return res, nil

View File

@ -1,3 +1,17 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package handler
import (
@ -10,6 +24,7 @@ import (
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/goharbor/harbor/src/common/rbac"
preheatCtl "github.com/goharbor/harbor/src/controller/p2p/preheat"
projectCtl "github.com/goharbor/harbor/src/controller/project"
@ -244,6 +259,12 @@ func (api *preheatAPI) CreatePolicy(ctx context.Context, params operation.Create
// override project ID
policy.ProjectID = project.ProjectID
// validate provider whether exist
_, err = api.preheatCtl.GetInstance(ctx, policy.ProviderID)
if err != nil {
return api.SendError(ctx, err)
}
_, err = api.preheatCtl.CreatePolicy(ctx, policy)
if err != nil {
return api.SendError(ctx, err)
@ -264,6 +285,23 @@ func (api *preheatAPI) UpdatePolicy(ctx context.Context, params operation.Update
return api.SendError(ctx, err)
}
project, err := api.projectCtl.GetByName(ctx, params.ProjectName)
if err != nil {
return api.SendError(ctx, err)
}
// override project ID
policy.ProjectID = project.ProjectID
if err := api.requirePolicyAccess(ctx, policy); err != nil {
return api.SendError(ctx, err)
}
// validate provider whether exist
_, err = api.preheatCtl.GetInstance(ctx, policy.ProviderID)
if err != nil {
return api.SendError(ctx, err)
}
err = api.preheatCtl.UpdatePolicy(ctx, policy)
if err != nil {
return api.SendError(ctx, err)
@ -578,6 +616,10 @@ func (api *preheatAPI) GetExecution(ctx context.Context, params operation.GetExe
return api.SendError(ctx, err)
}
if err := api.requireExecutionInProject(ctx, params.ProjectName, params.PreheatPolicyName, params.ExecutionID); err != nil {
return api.SendError(ctx, err)
}
execution, err := api.executionCtl.Get(ctx, params.ExecutionID)
if err != nil {
return api.SendError(ctx, err)
@ -646,6 +688,10 @@ func (api *preheatAPI) StopExecution(ctx context.Context, params operation.StopE
return api.SendError(ctx, err)
}
if err := api.requireExecutionInProject(ctx, params.ProjectName, params.PreheatPolicyName, params.ExecutionID); err != nil {
return api.SendError(ctx, err)
}
if params.Execution.Status == "Stopped" {
err := api.executionCtl.Stop(ctx, params.ExecutionID)
if err != nil {
@ -684,6 +730,10 @@ func (api *preheatAPI) ListTasks(ctx context.Context, params operation.ListTasks
return api.SendError(ctx, err)
}
if err := api.requireExecutionInProject(ctx, params.ProjectName, params.PreheatPolicyName, params.ExecutionID); err != nil {
return api.SendError(ctx, err)
}
query, err := api.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
if err != nil {
return api.SendError(ctx, err)
@ -722,6 +772,10 @@ func (api *preheatAPI) GetPreheatLog(ctx context.Context, params operation.GetPr
return api.SendError(ctx, err)
}
if err := api.requireTaskInProject(ctx, params.ProjectName, params.PreheatPolicyName, params.ExecutionID, params.TaskID); err != nil {
return api.SendError(ctx, err)
}
l, err := api.taskCtl.GetLog(ctx, params.TaskID)
if err != nil {
return api.SendError(ctx, err)
@ -730,6 +784,52 @@ func (api *preheatAPI) GetPreheatLog(ctx context.Context, params operation.GetPr
return operation.NewGetPreheatLogOK().WithPayload(string(l))
}
func (api *preheatAPI) requireTaskInProject(ctx context.Context, projectNameOrID interface{}, policyName string, executionID, taskID int64) error {
projectID, err := getProjectID(ctx, projectNameOrID)
notFoundErr := fmt.Errorf("project id %d, task id %d not found", projectID, taskID)
if err != nil {
return err
}
// require execution before require task
if err := api.requireExecutionInProject(ctx, projectID, policyName, executionID); err != nil {
return err
}
task, err := api.taskCtl.Get(ctx, taskID)
if err != nil {
return err
}
if task != nil && task.ExecutionID == executionID {
return nil
}
return errors.NotFoundError(notFoundErr)
}
func (api *preheatAPI) requireExecutionInProject(ctx context.Context, projectNameOrID interface{}, policyName string, executionID int64) error {
projectID, err := getProjectID(ctx, projectNameOrID)
notFoundErr := fmt.Errorf("project id %d, execution id %d not found", projectID, executionID)
if err != nil {
return err
}
plc, err := api.preheatCtl.GetPolicyByName(ctx, projectID, policyName)
if err != nil {
return err
}
exec, err := api.executionCtl.Get(ctx, executionID)
if err != nil {
return err
}
if exec != nil && exec.VendorType == job.P2PPreheat && exec.VendorID == plc.ID {
return nil
}
return errors.NotFoundError(notFoundErr)
}
// ListProvidersUnderProject is Get all providers at project level
func (api *preheatAPI) ListProvidersUnderProject(ctx context.Context, params operation.ListProvidersUnderProjectParams) middleware.Responder {
if err := api.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionList, rbac.ResourcePreatPolicy); err != nil {
@ -753,3 +853,19 @@ func (api *preheatAPI) ListProvidersUnderProject(ctx context.Context, params ope
return operation.NewListProvidersUnderProjectOK().WithPayload(providers)
}
// requirePolicyAccess checks the project whether has the permission to the policy.
func (api *preheatAPI) requirePolicyAccess(ctx context.Context, policy *policy.Schema) error {
if policy != nil && policy.ID != 0 {
db, err := api.preheatCtl.GetPolicy(ctx, policy.ID)
if err != nil {
return err
}
// return err if project id does not match
if db.ProjectID != policy.ProjectID {
return errors.NotFoundError(errors.Errorf("project id %d does not match", policy.ProjectID))
}
}
return nil
}