diff --git a/src/server/v2.0/handler/notification_policy.go b/src/server/v2.0/handler/notification_policy.go index a17ba0be80..f36ba1b2c7 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 ( @@ -40,6 +54,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 { @@ -114,7 +143,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{} if err := lib.JSONCopy(policy, params.Policy); err != nil { log.Warningf("failed to call JSONCopy on notification policy when UpdateWebhookPolicyOfProject, error: %v", err) @@ -127,10 +163,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) @@ -144,7 +177,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) } @@ -153,7 +188,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 9908c8a6df..9c36ae38e8 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/purge.go b/src/server/v2.0/handler/purge.go index 5687d301b7..c332635228 100644 --- a/src/server/v2.0/handler/purge.go +++ b/src/server/v2.0/handler/purge.go @@ -185,6 +185,9 @@ func (p *purgeAPI) GetPurgeJob(ctx context.Context, params purge.GetPurgeJobPara } exec, err := p.executionCtl.Get(ctx, params.PurgeID) + if exec.VendorType != pg.VendorType { + return p.SendError(ctx, fmt.Errorf("purge job with id %d not found", params.PurgeID)) + } if err != nil { return p.SendError(ctx, err) } diff --git a/src/server/v2.0/handler/robot.go b/src/server/v2.0/handler/robot.go index dc0d2d56a8..b68106c7d6 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 ( @@ -95,7 +109,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() } @@ -177,7 +191,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) } @@ -285,11 +298,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") } @@ -333,7 +351,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 }