mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-09 00:12:03 +01:00
add robot account 2 api handler
Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
9571af84bd
commit
8e61a3ea31
@ -1410,6 +1410,154 @@ paths:
|
|||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
|
/projects/{project_id_or_name}/robots:
|
||||||
|
get:
|
||||||
|
summary: Get all robot accounts of specified project
|
||||||
|
description: Get all robot accounts of specified project
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/projectIDOrName'
|
||||||
|
- $ref: '#/parameters/page'
|
||||||
|
- $ref: '#/parameters/pageSize'
|
||||||
|
- $ref: '#/parameters/query'
|
||||||
|
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/projectIDOrName'
|
||||||
|
- 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_id_or_name}/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/projectIDOrName'
|
||||||
|
- $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/projectIDOrName'
|
||||||
|
- $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/projectIDOrName'
|
||||||
|
- $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'
|
||||||
/icons/{digest}:
|
/icons/{digest}:
|
||||||
get:
|
get:
|
||||||
summary: Get artifact icon
|
summary: Get artifact icon
|
||||||
@ -1431,6 +1579,149 @@ paths:
|
|||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
|
/robots:
|
||||||
|
get:
|
||||||
|
summary: Get robot account
|
||||||
|
description: List the robot accounts with the specified level and project.
|
||||||
|
tags:
|
||||||
|
- robot
|
||||||
|
operationId: ListRobot
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/query'
|
||||||
|
- $ref: '#/parameters/page'
|
||||||
|
- $ref: '#/parameters/pageSize'
|
||||||
|
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:
|
||||||
|
- robot
|
||||||
|
operationId: CreateRobot
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- name: robot
|
||||||
|
in: body
|
||||||
|
description: The JSON object of a robot account.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RobotCreate'
|
||||||
|
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'
|
||||||
|
/robots/{robot_id}:
|
||||||
|
get:
|
||||||
|
summary: Get a robot account
|
||||||
|
description: This endpoint returns specific robot account information by robot ID.
|
||||||
|
tags:
|
||||||
|
- robot
|
||||||
|
operationId: GetRobotByID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $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 a robot account
|
||||||
|
description: This endpoint updates specific robot account information by robot ID.
|
||||||
|
tags:
|
||||||
|
- robot
|
||||||
|
operationId: UpdateRobot
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/robotId'
|
||||||
|
- name: robot
|
||||||
|
in: body
|
||||||
|
description: The JSON object of a robot account.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RobotCreate'
|
||||||
|
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:
|
||||||
|
- robot
|
||||||
|
operationId: DeleteRobot
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $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'
|
||||||
/replication/executions:
|
/replication/executions:
|
||||||
get:
|
get:
|
||||||
summary: List replication executions
|
summary: List replication executions
|
||||||
@ -1661,6 +1952,12 @@ parameters:
|
|||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
projectIDOrName:
|
||||||
|
name: project_id_or_name
|
||||||
|
in: path
|
||||||
|
description: The id or name of the project
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
repositoryName:
|
repositoryName:
|
||||||
name: repository_name
|
name: repository_name
|
||||||
in: path
|
in: path
|
||||||
@ -1726,6 +2023,12 @@ parameters:
|
|||||||
description: Task ID
|
description: Task ID
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
|
robotId:
|
||||||
|
name: robot_id
|
||||||
|
in: path
|
||||||
|
description: Robot ID
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Success
|
description: Success
|
||||||
@ -2693,3 +2996,129 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: The end time of the task
|
description: The end time of the task
|
||||||
|
Robot:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: The ID of the robot
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The name of the tag
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: The description of the robot
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
description: The secret of the robot
|
||||||
|
level:
|
||||||
|
type: string
|
||||||
|
description: The level of the robot, project or system
|
||||||
|
disable:
|
||||||
|
type: boolean
|
||||||
|
x-omitempty: false
|
||||||
|
description: The disable status of the robot
|
||||||
|
expires_at:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: The expiration data of the robot
|
||||||
|
permissions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Permission'
|
||||||
|
creation_time:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: The creation time of the robot.
|
||||||
|
update_time:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: The update time of the robot.
|
||||||
|
RobotCreate:
|
||||||
|
type: object
|
||||||
|
description: The request for robot account creation.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The name of the tag
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: The description of the robot
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
description: The secret of the robot
|
||||||
|
level:
|
||||||
|
type: string
|
||||||
|
description: The level of the robot, project or system
|
||||||
|
disable:
|
||||||
|
type: boolean
|
||||||
|
description: The disable status of the robot
|
||||||
|
expires_at:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: The expiration data of the robot
|
||||||
|
permissions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Permission'
|
||||||
|
RobotCreated:
|
||||||
|
type: object
|
||||||
|
description: The response for robot account creation.
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: The ID of the robot
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The name of the tag
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
description: The secret of the robot
|
||||||
|
creation_time:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: The creation time of the robot.
|
||||||
|
Permission:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
description: The kind of the permission
|
||||||
|
namespace:
|
||||||
|
type: string
|
||||||
|
description: The namespace of the permission
|
||||||
|
access:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Access'
|
||||||
|
Access:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
resource:
|
||||||
|
type: string
|
||||||
|
description: The resource of the access
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
description: The action of the access
|
||||||
|
effect:
|
||||||
|
type: string
|
||||||
|
description: The effect of the access
|
||||||
|
RobotCreateV1:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The name of robot account
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: The description of robot account
|
||||||
|
expires_at:
|
||||||
|
type: integer
|
||||||
|
description: The expiration time on or after which the JWT MUST NOT be accepted for processing.
|
||||||
|
access:
|
||||||
|
type: array
|
||||||
|
description: The permission of robot account
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Access'
|
||||||
|
@ -37,9 +37,9 @@ func (eff Effect) String() string {
|
|||||||
|
|
||||||
// Policy the type of policy
|
// Policy the type of policy
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
Resource
|
Resource `json:"resource,omitempty"`
|
||||||
Action
|
Action `json:"action,omitempty"`
|
||||||
Effect
|
Effect `json:"effect,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEffect returns effect of resource, default is allow
|
// GetEffect returns effect of resource, default is allow
|
||||||
|
@ -36,6 +36,8 @@ func New() http.Handler {
|
|||||||
ProjectAPI: newProjectAPI(),
|
ProjectAPI: newProjectAPI(),
|
||||||
PreheatAPI: newPreheatAPI(),
|
PreheatAPI: newPreheatAPI(),
|
||||||
IconAPI: newIconAPI(),
|
IconAPI: newIconAPI(),
|
||||||
|
RobotAPI: newRobotAPI(),
|
||||||
|
Robotv1API: newRobotV1API(),
|
||||||
ReplicationAPI: newReplicationAPI(),
|
ReplicationAPI: newReplicationAPI(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
41
src/server/v2.0/handler/model/robot.go
Normal file
41
src/server/v2.0/handler/model/robot.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/goharbor/harbor/src/controller/robot"
|
||||||
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Robot struct {
|
||||||
|
*robot.Robot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Robot) ToSwagger() *models.Robot {
|
||||||
|
perms := []*models.Permission{}
|
||||||
|
for _, p := range r.Permissions {
|
||||||
|
temp := &models.Permission{}
|
||||||
|
lib.JSONCopy(temp, p)
|
||||||
|
perms = append(perms, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Robot{
|
||||||
|
ID: r.ID,
|
||||||
|
Name: r.Name,
|
||||||
|
Description: r.Description,
|
||||||
|
Secret: r.Secret,
|
||||||
|
ExpiresAt: r.ExpiresAt,
|
||||||
|
Level: r.Level,
|
||||||
|
Disable: r.Disabled,
|
||||||
|
CreationTime: strfmt.DateTime(r.CreationTime),
|
||||||
|
UpdateTime: strfmt.DateTime(r.UpdateTime),
|
||||||
|
Permissions: perms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRobot ...
|
||||||
|
func NewRobot(r *robot.Robot) *Robot {
|
||||||
|
return &Robot{
|
||||||
|
Robot: r,
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
||||||
"github.com/goharbor/harbor/src/pkg/quota/types"
|
"github.com/goharbor/harbor/src/pkg/quota/types"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
"github.com/goharbor/harbor/src/pkg/robot"
|
"github.com/goharbor/harbor/src/pkg/robot2"
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
"github.com/goharbor/harbor/src/replication"
|
"github.com/goharbor/harbor/src/replication"
|
||||||
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||||
@ -48,7 +48,7 @@ func newProjectAPI() *projectAPI {
|
|||||||
repositoryCtl: repository.Ctl,
|
repositoryCtl: repository.Ctl,
|
||||||
projectCtl: project.Ctl,
|
projectCtl: project.Ctl,
|
||||||
quotaCtl: quota.Ctl,
|
quotaCtl: quota.Ctl,
|
||||||
robotMgr: robot.Mgr,
|
robotMgr: robot2.Mgr,
|
||||||
preheatCtl: preheat.Ctl,
|
preheatCtl: preheat.Ctl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ type projectAPI struct {
|
|||||||
repositoryCtl repository.Controller
|
repositoryCtl repository.Controller
|
||||||
projectCtl project.Controller
|
projectCtl project.Controller
|
||||||
quotaCtl quota.Controller
|
quotaCtl quota.Controller
|
||||||
robotMgr robot.Manager
|
robotMgr robot2.Manager
|
||||||
preheatCtl preheat.Controller
|
preheatCtl preheat.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
259
src/server/v2.0/handler/robot.go
Normal file
259
src/server/v2.0/handler/robot.go
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"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/controller/robot"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
pkg "github.com/goharbor/harbor/src/pkg/robot2/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/robot"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRobotAPI() *robotAPI {
|
||||||
|
return &robotAPI{
|
||||||
|
robotCtl: robot.Ctl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type robotAPI struct {
|
||||||
|
BaseAPI
|
||||||
|
robotCtl robot.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) CreateRobot(ctx context.Context, params operation.CreateRobotParams) middleware.Responder {
|
||||||
|
if err := rAPI.validate(params.Robot); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.requireAccess(ctx, params.Robot.Level, params.Robot.Permissions[0].Namespace, rbac.ActionCreate); 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,
|
||||||
|
},
|
||||||
|
Level: params.Robot.Level,
|
||||||
|
}
|
||||||
|
lib.JSONCopy(&r.Permissions, params.Robot.Permissions)
|
||||||
|
|
||||||
|
rid, 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.NewCreateRobotCreated().WithLocation(location).WithPayload(&models.RobotCreated{
|
||||||
|
ID: created.ID,
|
||||||
|
Name: created.Name,
|
||||||
|
Secret: created.Secret,
|
||||||
|
CreationTime: strfmt.DateTime(created.CreationTime),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) DeleteRobot(ctx context.Context, params operation.DeleteRobotParams) middleware.Responder {
|
||||||
|
if err := rAPI.RequireAuthenticated(ctx); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rAPI.robotCtl.Get(ctx, params.RobotID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.requireAccess(ctx, r.Level, r.ProjectID, rbac.ActionDelete); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.robotCtl.Delete(ctx, params.RobotID); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewDeleteRobotOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) ListRobot(ctx context.Context, params operation.ListRobotParams) middleware.Responder {
|
||||||
|
if err := rAPI.RequireAuthenticated(ctx); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := rAPI.BuildQuery(ctx, params.Q, params.Page, params.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectID int64
|
||||||
|
var level string
|
||||||
|
// GET /api/v2.0/robots or GET /api/v2.0/robots?level=system to get all of system level robots.
|
||||||
|
// GET /api/v2.0/robots?level=project&project_id=1
|
||||||
|
if _, ok := query.Keywords["Level"]; ok {
|
||||||
|
if !isValidLevel(query.Keywords["Level"].(string)) {
|
||||||
|
return rAPI.SendError(ctx, errors.New(nil).WithMessage("bad request error level input").WithCode(errors.BadRequestCode))
|
||||||
|
}
|
||||||
|
level = query.Keywords["Level"].(string)
|
||||||
|
if level == robot.LEVELPROJECT {
|
||||||
|
if _, ok := query.Keywords["ProjectID"]; !ok {
|
||||||
|
return rAPI.SendError(ctx, errors.BadRequestError(nil).WithMessage("must with project ID when to query project robots"))
|
||||||
|
}
|
||||||
|
pid, err := strconv.ParseInt(query.Keywords["ProjectID"].(string), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, errors.BadRequestError(nil).WithMessage("Project ID must be int type."))
|
||||||
|
}
|
||||||
|
projectID = pid
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
level = robot.LEVELSYSTEM
|
||||||
|
projectID = 0
|
||||||
|
query.Keywords["ProjectID"] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.requireAccess(ctx, level, projectID, rbac.ActionList); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, model.NewRobot(r).ToSwagger())
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation.NewListRobotOK().
|
||||||
|
WithXTotalCount(total).
|
||||||
|
WithLink(rAPI.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
|
WithPayload(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) GetRobotByID(ctx context.Context, params operation.GetRobotByIDParams) middleware.Responder {
|
||||||
|
if err := rAPI.RequireAuthenticated(ctx); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rAPI.robotCtl.Get(ctx, params.RobotID, &robot.Option{
|
||||||
|
WithPermission: true,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation.NewGetRobotByIDOK().WithPayload(model.NewRobot(r).ToSwagger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) UpdateRobot(ctx context.Context, params operation.UpdateRobotParams) middleware.Responder {
|
||||||
|
if err := rAPI.validate(params.Robot); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.requireAccess(ctx, params.Robot.Level, params.Robot.Permissions[0].Namespace, rbac.ActionUpdate); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rAPI.robotCtl.Get(ctx, params.RobotID, &robot.Option{
|
||||||
|
WithPermission: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Robot.Level != r.Level || params.Robot.Name != r.Name {
|
||||||
|
return rAPI.SendError(ctx, errors.BadRequestError(nil).WithMessage("cannot update the level or name of robot"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh secret only
|
||||||
|
if params.Robot.Secret != r.Secret && params.Robot.Secret != "" {
|
||||||
|
key, err := config.SecretKey()
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
secret, err := utils.ReversibleEncrypt(params.Robot.Secret, key)
|
||||||
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
r.Secret = secret
|
||||||
|
if err := rAPI.robotCtl.Update(ctx, r); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Description = params.Robot.Description
|
||||||
|
r.ExpiresAt = params.Robot.ExpiresAt
|
||||||
|
r.Disabled = params.Robot.Disable
|
||||||
|
if len(params.Robot.Permissions) != 0 {
|
||||||
|
lib.JSONCopy(&r.Permissions, params.Robot.Permissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rAPI.robotCtl.Update(ctx, r); err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation.NewUpdateRobotOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rAPI *robotAPI) requireAccess(ctx context.Context, level string, projectIDOrName interface{}, action rbac.Action) error {
|
||||||
|
if level == robot.LEVELSYSTEM {
|
||||||
|
return rAPI.RequireSysAdmin(ctx)
|
||||||
|
} else if level == robot.LEVELPROJECT {
|
||||||
|
return rAPI.RequireProjectAccess(ctx, projectIDOrName, action, rbac.ResourceRobot)
|
||||||
|
}
|
||||||
|
return errors.ForbiddenError(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// more validation
|
||||||
|
func (rAPI *robotAPI) validate(r *models.RobotCreate) error {
|
||||||
|
if !isValidLevel(r.Level) {
|
||||||
|
return errors.New(nil).WithMessage("bad request error level input").WithCode(errors.BadRequestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.Permissions) == 0 {
|
||||||
|
return errors.New(nil).WithMessage("bad request empty permission").WithCode(errors.BadRequestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Level == robot.LEVELPROJECT {
|
||||||
|
// to create a project robot, the permission must be only one project scope.
|
||||||
|
if len(r.Permissions) > 1 {
|
||||||
|
return errors.New(nil).WithMessage("bad request permission").WithCode(errors.BadRequestCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidLevel(l string) bool {
|
||||||
|
switch l {
|
||||||
|
case
|
||||||
|
robot.LEVELSYSTEM,
|
||||||
|
robot.LEVELPROJECT:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user