From 87f58abba3a5f048abc4a5ab5fc89d7a3f65bed8 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 22 Jun 2022 19:41:04 +0800 Subject: [PATCH] (cherry-pick) Unify the process of jobservice execution/task retrieve and update (#17057) Unify the process of jobservice execution/task retrieve and update Change regular expression in robot account Signed-off-by: stonezdj --- .../v2.0/handler/notification_policy.go | 56 ++++++++++-- src/server/v2.0/handler/preheat.go | 85 ++++++++++++++++++- src/server/v2.0/handler/robot.go | 30 +++++-- 3 files changed, 157 insertions(+), 14 deletions(-) diff --git a/src/server/v2.0/handler/notification_policy.go b/src/server/v2.0/handler/notification_policy.go index 2ff46b4e1..ccd680f68 100644 --- a/src/server/v2.0/handler/notification_policy.go +++ b/src/server/v2.0/handler/notification_policy.go @@ -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 ( @@ -39,6 +53,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 { @@ -111,7 +140,14 @@ 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) @@ -122,10 +158,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 +172,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 +183,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) } diff --git a/src/server/v2.0/handler/preheat.go b/src/server/v2.0/handler/preheat.go index 9908c8a6d..9c36ae38e 100644 --- a/src/server/v2.0/handler/preheat.go +++ b/src/server/v2.0/handler/preheat.go @@ -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 ( @@ -578,6 +592,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 +664,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 { @@ -683,7 +705,6 @@ func (api *preheatAPI) ListTasks(ctx context.Context, params operation.ListTasks if err := api.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionList, rbac.ResourcePreatPolicy); 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 +743,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.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 +755,64 @@ 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, 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 + } + plc, err := api.preheatCtl.GetPolicyByName(ctx, projectID, policyName) + if err != nil { + return err + } + execs, err := api.executionCtl.List(ctx, q.New(q.KeyWords{"VendorType": job.P2PPreheat, "VendorID": plc.ID})) + if err != nil { + return err + } + if len(execs) == 0 { + return errors.NotFoundError(notFoundErr) + } + var execIds []interface{} + for _, item := range execs { + execIds = append(execIds, item.ID) + } + tasks, err := api.taskCtl.List(ctx, q.New(q.KeyWords{"ExecutionID": q.NewOrList(execIds)})) + if err != nil { + return err + } + if len(tasks) == 0 { + return errors.NotFoundError(notFoundErr) + } + for _, t := range tasks { + if t.ID == taskID { + 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 + } + execs, err := api.executionCtl.List(ctx, q.New(q.KeyWords{"VendorType": job.P2PPreheat, "VendorID": plc.ID})) + if err != nil { + return err + } + for _, e := range execs { + if e.ID == executionID { + 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 { diff --git a/src/server/v2.0/handler/robot.go b/src/server/v2.0/handler/robot.go index bbd3ac851..a2f33a846 100644 --- a/src/server/v2.0/handler/robot.go +++ b/src/server/v2.0/handler/robot.go @@ -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 ( @@ -92,7 +106,7 @@ func (rAPI *robotAPI) DeleteRobot(ctx context.Context, params operation.DeleteRo } if err := rAPI.robotCtl.Delete(ctx, params.RobotID); err != nil { - // for the version 1 robot account, has to ignore the no permissions error. + // for the version 1 robot account, has to ignore the no permission error. if !r.Editable && errors.IsNotFoundErr(err) { return operation.NewDeleteRobotOK() } @@ -175,7 +189,6 @@ func (rAPI *robotAPI) GetRobotByID(ctx context.Context, params operation.GetRobo if err != nil { return rAPI.SendError(ctx, err) } - if err := rAPI.requireAccess(ctx, r.Level, r.ProjectID, rbac.ActionRead); err != nil { return rAPI.SendError(ctx, err) } @@ -283,11 +296,16 @@ func (rAPI *robotAPI) updateV2Robot(ctx context.Context, params operation.Update if err := rAPI.validate(params.Robot.Duration, params.Robot.Level, params.Robot.Permissions); err != nil { return err } - - if err := rAPI.requireAccess(ctx, params.Robot.Level, params.Robot.Permissions[0].Namespace, rbac.ActionUpdate); err != nil { + projectID, err := getProjectID(ctx, params.Robot.Permissions[0].Namespace) + if err != nil { + return err + } + if r.Level != robot.LEVELSYSTEM && r.ProjectID != projectID { + return errors.BadRequestError(nil).WithMessage("cannot update the project id of robot") + } + if err := rAPI.requireAccess(ctx, params.Robot.Level, projectID, rbac.ActionUpdate); err != nil { return err } - if params.Robot.Level != r.Level || params.Robot.Name != r.Name { return errors.BadRequestError(nil).WithMessage("cannot update the level or name of robot") } @@ -329,7 +347,7 @@ func isValidDuration(d int64) bool { func isValidSec(sec string) bool { hasLower := regexp.MustCompile(`[a-z]`) hasUpper := regexp.MustCompile(`[A-Z]`) - hasNumber := regexp.MustCompile(`[0-9]`) + hasNumber := regexp.MustCompile(`\d`) if len(sec) >= 8 && hasLower.MatchString(sec) && hasUpper.MatchString(sec) && hasNumber.MatchString(sec) { return true }