diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index c6ad6db13d4..6ad21267984 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -2351,160 +2351,6 @@ paths: $ref: '#/responses/404' '500': $ref: '#/responses/500' - /projects/{project_name_or_id}/robots: - get: - summary: Get all robot accounts of specified project - description: Get all robot accounts of specified project - parameters: - - $ref: '#/parameters/requestId' - - $ref: '#/parameters/isResourceName' - - $ref: '#/parameters/projectNameOrId' - - $ref: '#/parameters/page' - - $ref: '#/parameters/pageSize' - - $ref: '#/parameters/query' - - $ref: '#/parameters/sort' - tags: - - robotv1 - operationId: ListRobotV1 - responses: - '200': - description: Success - headers: - X-Total-Count: - description: The total count of robot accounts - type: integer - Link: - description: Link refers to the previous page and next page - type: string - schema: - type: array - items: - $ref: '#/definitions/Robot' - '400': - $ref: '#/responses/400' - '404': - $ref: '#/responses/404' - '500': - $ref: '#/responses/500' - post: - summary: Create a robot account - description: Create a robot account - tags: - - robotv1 - operationId: CreateRobotV1 - parameters: - - $ref: '#/parameters/requestId' - - $ref: '#/parameters/isResourceName' - - $ref: '#/parameters/projectNameOrId' - - name: robot - in: body - description: The JSON object of a robot account. - required: true - schema: - $ref: '#/definitions/RobotCreateV1' - responses: - '201': - description: Created - headers: - X-Request-Id: - description: The ID of the corresponding request for the response - type: string - Location: - description: The location of the resource - type: string - schema: - $ref: '#/definitions/RobotCreated' - '400': - $ref: '#/responses/400' - '401': - $ref: '#/responses/401' - '403': - $ref: '#/responses/403' - '404': - $ref: '#/responses/404' - '500': - $ref: '#/responses/500' - /projects/{project_name_or_id}/robots/{robot_id}: - get: - summary: Get a robot account - description: This endpoint returns specific robot account information by robot ID. - tags: - - robotv1 - operationId: GetRobotByIDV1 - parameters: - - $ref: '#/parameters/requestId' - - $ref: '#/parameters/isResourceName' - - $ref: '#/parameters/projectNameOrId' - - $ref: '#/parameters/robotId' - responses: - '200': - description: Return matched robot information. - schema: - $ref: '#/definitions/Robot' - '401': - $ref: '#/responses/401' - '403': - $ref: '#/responses/403' - '404': - $ref: '#/responses/404' - '500': - $ref: '#/responses/500' - put: - summary: Update status of robot account. - description: Used to disable/enable a specified robot account. - tags: - - robotv1 - operationId: UpdateRobotV1 - parameters: - - $ref: '#/parameters/requestId' - - $ref: '#/parameters/isResourceName' - - $ref: '#/parameters/projectNameOrId' - - $ref: '#/parameters/robotId' - - name: robot - in: body - description: The JSON object of a robot account. - required: true - schema: - $ref: '#/definitions/Robot' - responses: - '200': - $ref: '#/responses/200' - '400': - $ref: '#/responses/400' - '401': - $ref: '#/responses/401' - '403': - $ref: '#/responses/403' - '404': - $ref: '#/responses/404' - '409': - $ref: '#/responses/409' - '500': - $ref: '#/responses/500' - delete: - summary: Delete a robot account - description: This endpoint deletes specific robot account information by robot ID. - tags: - - robotv1 - operationId: DeleteRobotV1 - parameters: - - $ref: '#/parameters/requestId' - - $ref: '#/parameters/isResourceName' - - $ref: '#/parameters/projectNameOrId' - - $ref: '#/parameters/robotId' - responses: - '200': - $ref: '#/responses/200' - '400': - $ref: '#/responses/400' - '401': - $ref: '#/responses/401' - '403': - $ref: '#/responses/403' - '404': - $ref: '#/responses/404' - '500': - $ref: '#/responses/500' '/projects/{project_name_or_id}/immutabletagrules': get: summary: List all immutable tag rules of current project diff --git a/src/server/v2.0/handler/handler.go b/src/server/v2.0/handler/handler.go index 2de7f562f7d..2f770c696bb 100644 --- a/src/server/v2.0/handler/handler.go +++ b/src/server/v2.0/handler/handler.go @@ -44,7 +44,6 @@ func New() http.Handler { PreheatAPI: newPreheatAPI(), IconAPI: newIconAPI(), RobotAPI: newRobotAPI(), - Robotv1API: newRobotV1API(), ReplicationAPI: newReplicationAPI(), RegistryAPI: newRegistryAPI(), SysteminfoAPI: newSystemInfoAPI(), diff --git a/src/server/v2.0/handler/robotV1.go b/src/server/v2.0/handler/robotV1.go deleted file mode 100644 index 885e96e32ce..00000000000 --- a/src/server/v2.0/handler/robotV1.go +++ /dev/null @@ -1,292 +0,0 @@ -// 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" - "regexp" - "strings" - - "github.com/go-openapi/runtime/middleware" - "github.com/go-openapi/strfmt" - - "github.com/goharbor/harbor/src/common/rbac" - rbac_project "github.com/goharbor/harbor/src/common/rbac/project" - "github.com/goharbor/harbor/src/controller/project" - "github.com/goharbor/harbor/src/controller/robot" - "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/permission/types" - pkg_robot "github.com/goharbor/harbor/src/pkg/robot" - pkg "github.com/goharbor/harbor/src/pkg/robot/model" - handler_model "github.com/goharbor/harbor/src/server/v2.0/handler/model" - "github.com/goharbor/harbor/src/server/v2.0/models" - operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/robotv1" -) - -func newRobotV1API() *robotV1API { - return &robotV1API{ - robotCtl: robot.Ctl, - robotMgr: pkg_robot.Mgr, - projectCtr: project.Ctl, - } -} - -type robotV1API struct { - BaseAPI - robotCtl robot.Controller - robotMgr pkg_robot.Manager - projectCtr project.Controller -} - -func (rAPI *robotV1API) CreateRobotV1(ctx context.Context, params operation.CreateRobotV1Params) middleware.Responder { - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - if err := rAPI.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionCreate, rbac.ResourceRobot); err != nil { - return rAPI.SendError(ctx, err) - } - - if err := rAPI.validate(ctx, params); err != nil { - return rAPI.SendError(ctx, err) - } - - r := &robot.Robot{ - Robot: pkg.Robot{ - Name: params.Robot.Name, - Description: params.Robot.Description, - ExpiresAt: params.Robot.ExpiresAt, - Visible: true, - }, - Level: robot.LEVELPROJECT, - } - - projectName, ok := projectNameOrID.(string) - if !ok { - p, err := rAPI.projectCtr.Get(ctx, projectNameOrID, project.Metadata(false)) - if err != nil { - log.Errorf("failed to get project %s: %v", projectNameOrID, err) - return rAPI.SendError(ctx, err) - } - projectName = p.Name - } - - permission := &robot.Permission{ - Kind: "project", - Namespace: projectName, - } - - var policies []*types.Policy - for _, acc := range params.Robot.Access { - policy := &types.Policy{ - Action: types.Action(acc.Action), - Effect: types.Effect(acc.Effect), - } - res, err := getRawResource(acc.Resource) - if err != nil { - return rAPI.SendError(ctx, err) - } - policy.Resource = types.Resource(res) - policies = append(policies, policy) - } - permission.Access = policies - r.Permissions = append(r.Permissions, permission) - - rid, pwd, err := rAPI.robotCtl.Create(ctx, r) - if err != nil { - return rAPI.SendError(ctx, err) - } - - created, err := rAPI.robotCtl.Get(ctx, rid, nil) - if err != nil { - return rAPI.SendError(ctx, err) - } - - location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), created.ID) - return operation.NewCreateRobotV1Created().WithLocation(location).WithPayload(&models.RobotCreated{ - ID: created.ID, - Name: created.Name, - Secret: pwd, - CreationTime: strfmt.DateTime(created.CreationTime), - }) -} - -func (rAPI *robotV1API) DeleteRobotV1(ctx context.Context, params operation.DeleteRobotV1Params) middleware.Responder { - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - if err := rAPI.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionDelete, rbac.ResourceRobot); err != nil { - return rAPI.SendError(ctx, err) - } - - pro, err := rAPI.projectCtr.Get(ctx, projectNameOrID) - if err != nil { - return rAPI.SendError(ctx, err) - } - r, err := rAPI.robotCtl.List(ctx, q.New(q.KeyWords{"ProjectID": pro.ProjectID, "ID": params.RobotID}), &robot.Option{ - WithPermission: true, - }) - if err != nil { - return rAPI.SendError(ctx, err) - } - if len(r) == 0 { - return rAPI.SendError(ctx, errors.NotFoundError(fmt.Errorf("cannot find robot with project id: %d and id: %d", pro.ProjectID, params.RobotID))) - } - - // ignore the not permissions error. - if err := rAPI.robotCtl.Delete(ctx, params.RobotID); err != nil && !errors.IsNotFoundErr(err) { - return rAPI.SendError(ctx, err) - } - return operation.NewDeleteRobotV1OK() -} - -func (rAPI *robotV1API) ListRobotV1(ctx context.Context, params operation.ListRobotV1Params) middleware.Responder { - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - if err := rAPI.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionList, rbac.ResourceRobot); err != nil { - return rAPI.SendError(ctx, err) - } - - query, err := rAPI.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize) - if err != nil { - return rAPI.SendError(ctx, err) - } - query.Keywords["Visible"] = true - - pro, err := rAPI.projectCtr.Get(ctx, projectNameOrID) - if err != nil { - return rAPI.SendError(ctx, err) - } - - query.Keywords["ProjectID"] = pro.ProjectID - - total, err := rAPI.robotCtl.Count(ctx, query) - if err != nil { - return rAPI.SendError(ctx, err) - } - - robots, err := rAPI.robotCtl.List(ctx, query, &robot.Option{ - WithPermission: true, - }) - if err != nil { - return rAPI.SendError(ctx, err) - } - - var results []*models.Robot - for _, r := range robots { - results = append(results, handler_model.NewRobot(r).ToSwagger()) - } - - return operation.NewListRobotV1OK(). - WithXTotalCount(total). - WithLink(rAPI.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()). - WithPayload(results) -} - -func (rAPI *robotV1API) GetRobotByIDV1(ctx context.Context, params operation.GetRobotByIDV1Params) middleware.Responder { - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - if err := rAPI.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionRead, rbac.ResourceRobot); err != nil { - return rAPI.SendError(ctx, err) - } - - pro, err := rAPI.projectCtr.Get(ctx, projectNameOrID) - if err != nil { - return rAPI.SendError(ctx, err) - } - - r, err := rAPI.robotCtl.List(ctx, q.New(q.KeyWords{"ProjectID": pro.ProjectID, "ID": params.RobotID}), &robot.Option{ - WithPermission: true, - }) - if err != nil { - return rAPI.SendError(ctx, err) - } - if len(r) == 0 { - return rAPI.SendError(ctx, errors.NotFoundError(fmt.Errorf("cannot find robot with project id: %d and id: %d", pro.ProjectID, params.RobotID))) - } - - return operation.NewGetRobotByIDV1OK().WithPayload(handler_model.NewRobot(r[0]).ToSwagger()) -} - -func (rAPI *robotV1API) UpdateRobotV1(ctx context.Context, params operation.UpdateRobotV1Params) middleware.Responder { - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - if err := rAPI.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionUpdate, rbac.ResourceRobot); err != nil { - return rAPI.SendError(ctx, err) - } - - pro, err := rAPI.projectCtr.Get(ctx, projectNameOrID) - if err != nil { - return rAPI.SendError(ctx, err) - } - r, err := rAPI.robotCtl.List(ctx, q.New(q.KeyWords{"ProjectID": pro.ProjectID, "ID": params.RobotID}), &robot.Option{ - WithPermission: true, - }) - if err != nil { - return rAPI.SendError(ctx, err) - } - if len(r) == 0 { - return rAPI.SendError(ctx, errors.NotFoundError(fmt.Errorf("cannot find robot with project id: %d and id: %d", pro.ProjectID, params.RobotID))) - } - robot := r[0] - - // for v1 API, only update the disable. - robot.Disabled = params.Robot.Disable - if err := rAPI.robotCtl.Update(ctx, robot, nil); err != nil { - return rAPI.SendError(ctx, err) - } - - return operation.NewUpdateRobotV1OK() -} - -func (rAPI *robotV1API) validate(ctx context.Context, params operation.CreateRobotV1Params) error { - if params.Robot == nil { - return errors.New(nil).WithMessage("bad request no robot").WithCode(errors.BadRequestCode) - } - if len(params.Robot.Access) == 0 { - return errors.New(nil).WithMessage("bad request no access").WithCode(errors.BadRequestCode) - } - - projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName) - pro, err := rAPI.projectCtr.Get(ctx, projectNameOrID) - if err != nil { - return err - } - - policies := rbac_project.GetPoliciesOfProject(pro.ProjectID) - - mp := map[string]bool{} - for _, policy := range policies { - mp[policy.String()] = true - } - - for _, policy := range params.Robot.Access { - p := &types.Policy{} - if err := lib.JSONCopy(p, policy); err != nil { - log.Warningf("failed to call JSONCopy on robot access policy when validate, error: %v", err) - } - if !mp[p.String()] { - return errors.New(nil).WithMessagef("%s action of %s resource not exist in project %s", policy.Action, policy.Resource, projectNameOrID).WithCode(errors.BadRequestCode) - } - } - - return nil -} - -// /project/1/repository => repository -func getRawResource(resource string) (string, error) { - resourceReg := regexp.MustCompile("^/project/[0-9]+/(?P[a-z-]+)$") - matches := resourceReg.FindStringSubmatch(resource) - if len(matches) <= 1 { - return "", errors.New(nil).WithMessagef("bad resource %s", resource).WithCode(errors.BadRequestCode) - } - return matches[1], nil -}