mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 08:38:03 +01:00
479 lines
14 KiB
Go
479 lines
14 KiB
Go
//These APIs provide services for manipulating Harbor project.
|
|
|
|
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"runtime"
|
|
|
|
"github.com/vmware/harbor/dao"
|
|
"github.com/vmware/harbor/models"
|
|
"github.com/vmware/harbor/tests/apitests/apilib"
|
|
|
|
"github.com/astaxie/beego"
|
|
"github.com/dghubble/sling"
|
|
|
|
//for test env prepare
|
|
_ "github.com/vmware/harbor/auth/db"
|
|
_ "github.com/vmware/harbor/auth/ldap"
|
|
)
|
|
|
|
const (
|
|
jsonAcceptHeader = "application/json"
|
|
testAcceptHeader = "text/plain"
|
|
adminName = "admin"
|
|
adminPwd = "Harbor12345"
|
|
)
|
|
|
|
var admin, unknownUsr *usrInfo
|
|
|
|
type api struct {
|
|
basePath string
|
|
}
|
|
|
|
func newHarborAPI() *api {
|
|
return &api{
|
|
basePath: "",
|
|
}
|
|
}
|
|
|
|
func newHarborAPIWithBasePath(basePath string) *api {
|
|
return &api{
|
|
basePath: basePath,
|
|
}
|
|
}
|
|
|
|
type usrInfo struct {
|
|
Name string
|
|
Passwd string
|
|
}
|
|
|
|
func init() {
|
|
dao.InitDB()
|
|
_, file, _, _ := runtime.Caller(1)
|
|
apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator))))
|
|
beego.BConfig.WebConfig.Session.SessionOn = true
|
|
beego.TestBeegoInit(apppath)
|
|
|
|
beego.Router("/api/search/", &SearchAPI{})
|
|
beego.Router("/api/projects/", &ProjectAPI{}, "get:List;post:Post;head:Head")
|
|
beego.Router("/api/projects/:id", &ProjectAPI{}, "delete:Delete;get:Get")
|
|
beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword")
|
|
beego.Router("/api/projects/:id/publicity", &ProjectAPI{}, "put:ToggleProjectPublic")
|
|
beego.Router("/api/projects/:id([0-9]+)/logs/filter", &ProjectAPI{}, "post:FilterAccessLog")
|
|
beego.Router("/api/projects/:pid([0-9]+)/members/?:mid", &ProjectMemberAPI{}, "get:Get")
|
|
beego.Router("/api/statistics", &StatisticAPI{})
|
|
beego.Router("/api/logs", &LogAPI{})
|
|
|
|
_ = updateInitPassword(1, "Harbor12345")
|
|
|
|
//Init user Info
|
|
admin = &usrInfo{adminName, adminPwd}
|
|
unknownUsr = &usrInfo{"unknown", "unknown"}
|
|
|
|
}
|
|
|
|
func request(_sling *sling.Sling, acceptHeader string, authInfo ...usrInfo) (int, []byte, error) {
|
|
_sling = _sling.Set("Accept", acceptHeader)
|
|
req, err := _sling.Request()
|
|
if err != nil {
|
|
return 400, nil, err
|
|
}
|
|
if len(authInfo) > 0 {
|
|
req.SetBasicAuth(authInfo[0].Name, authInfo[0].Passwd)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
beego.BeeApp.Handlers.ServeHTTP(w, req)
|
|
body, err := ioutil.ReadAll(w.Body)
|
|
return w.Code, body, err
|
|
}
|
|
|
|
//Search for projects and repositories
|
|
//Implementation Notes
|
|
//The Search endpoint returns information about the projects and repositories
|
|
//offered at public status or related to the current logged in user.
|
|
//The response includes the project and repository list in a proper display order.
|
|
//@param q Search parameter for project and repository name.
|
|
//@return []Search
|
|
//func (a api) SearchGet (q string) (apilib.Search, error) {
|
|
func (a api) SearchGet(q string) (apilib.Search, error) {
|
|
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
// create path and map variables
|
|
path := "/api/search"
|
|
_sling = _sling.Path(path)
|
|
|
|
type QueryParams struct {
|
|
Query string `url:"q,omitempty"`
|
|
}
|
|
|
|
_sling = _sling.QueryStruct(&QueryParams{Query: q})
|
|
|
|
_, body, err := request(_sling, jsonAcceptHeader)
|
|
var successPayload = new(apilib.Search)
|
|
err = json.Unmarshal(body, &successPayload)
|
|
return *successPayload, err
|
|
}
|
|
|
|
//Create a new project.
|
|
//Implementation Notes
|
|
//This endpoint is for user to create a new project.
|
|
//@param project New created project.
|
|
//@return void
|
|
//func (a api) ProjectsPost (prjUsr usrInfo, project apilib.Project) (int, error) {
|
|
func (a api) ProjectsPost(prjUsr usrInfo, project apilib.ProjectReq) (int, error) {
|
|
|
|
_sling := sling.New().Post(a.basePath)
|
|
|
|
// create path and map variables
|
|
path := "/api/projects/"
|
|
|
|
_sling = _sling.Path(path)
|
|
|
|
// body params
|
|
_sling = _sling.BodyJSON(project)
|
|
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
return httpStatusCode, err
|
|
}
|
|
|
|
//Change password
|
|
//Implementation Notes
|
|
//Change the password on a user that already exists.
|
|
//@param userID user ID
|
|
//@param password user old and new password
|
|
//@return error
|
|
//func (a api) UsersUserIDPasswordPut (user usrInfo, userID int32, password apilib.Password) int {
|
|
func (a api) UsersUserIDPasswordPut(user usrInfo, userID int32, password apilib.Password) int {
|
|
|
|
_sling := sling.New().Put(a.basePath)
|
|
|
|
// create path and map variables
|
|
path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password"
|
|
fmt.Printf("change passwd path: %s\n", path)
|
|
fmt.Printf("password %+v\n", password)
|
|
_sling = _sling.Path(path)
|
|
|
|
// body params
|
|
_sling = _sling.BodyJSON(password)
|
|
|
|
httpStatusCode, _, _ := request(_sling, jsonAcceptHeader, user)
|
|
return httpStatusCode
|
|
}
|
|
|
|
func (a api) StatisticGet(user usrInfo) (apilib.StatisticMap, error) {
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
// create path and map variables
|
|
path := "/api/statistics/"
|
|
fmt.Printf("project statistic path: %s\n", path)
|
|
_sling = _sling.Path(path)
|
|
var successPayload = new(apilib.StatisticMap)
|
|
code, body, err := request(_sling, jsonAcceptHeader, user)
|
|
if 200 == code && nil == err {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
return *successPayload, err
|
|
}
|
|
|
|
func (a api) LogGet(user usrInfo, startTime, endTime, lines string) (int, []apilib.AccessLog, error) {
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
// create path and map variables
|
|
path := "/api/logs/"
|
|
fmt.Printf("logs path: %s\n", path)
|
|
_sling = _sling.Path(path)
|
|
type QueryParams struct {
|
|
StartTime string `url:"start_time,omitempty"`
|
|
EndTime string `url:"end_time,omitempty"`
|
|
Lines string `url:"lines,omitempty"`
|
|
}
|
|
|
|
_sling = _sling.QueryStruct(&QueryParams{StartTime: startTime, EndTime: endTime, Lines: lines})
|
|
var successPayload []apilib.AccessLog
|
|
code, body, err := request(_sling, jsonAcceptHeader, user)
|
|
if 200 == code && nil == err {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
return code, successPayload, err
|
|
}
|
|
|
|
////Delete a repository or a tag in a repository.
|
|
////Delete a repository or a tag in a repository.
|
|
////This endpoint let user delete repositories and tags with repo name and tag.\n
|
|
////@param repoName The name of repository which will be deleted.
|
|
////@param tag Tag of a repository.
|
|
////@return void
|
|
////func (a api) RepositoriesDelete(prjUsr UsrInfo, repoName string, tag string) (int, error) {
|
|
//func (a api) RepositoriesDelete(prjUsr UsrInfo, repoName string, tag string) (int, error) {
|
|
// _sling := sling.New().Delete(a.basePath)
|
|
|
|
// // create path and map variables
|
|
// path := "/api/repositories"
|
|
|
|
// _sling = _sling.Path(path)
|
|
|
|
// type QueryParams struct {
|
|
// RepoName string `url:"repo_name,omitempty"`
|
|
// Tag string `url:"tag,omitempty"`
|
|
// }
|
|
|
|
// _sling = _sling.QueryStruct(&QueryParams{RepoName: repoName, Tag: tag})
|
|
// // accept header
|
|
// accepts := []string{"application/json", "text/plain"}
|
|
// for key := range accepts {
|
|
// _sling = _sling.Set("Accept", accepts[key])
|
|
// break // only use the first Accept
|
|
// }
|
|
|
|
// req, err := _sling.Request()
|
|
// req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd)
|
|
// //fmt.Printf("request %+v", req)
|
|
|
|
// client := &http.Client{}
|
|
// httpResponse, err := client.Do(req)
|
|
// defer httpResponse.Body.Close()
|
|
|
|
// if err != nil {
|
|
// // handle error
|
|
// }
|
|
// return httpResponse.StatusCode, err
|
|
//}
|
|
|
|
//Delete project by projectID
|
|
func (a api) ProjectsDelete(prjUsr usrInfo, projectID string) (int, error) {
|
|
_sling := sling.New().Delete(a.basePath)
|
|
|
|
//create api path
|
|
path := "api/projects/" + projectID
|
|
_sling = _sling.Path(path)
|
|
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
return httpStatusCode, err
|
|
}
|
|
|
|
//Check if the project name user provided already exists
|
|
func (a api) ProjectsHead(prjUsr usrInfo, projectName string) (int, error) {
|
|
_sling := sling.New().Head(a.basePath)
|
|
|
|
//create api path
|
|
path := "api/projects"
|
|
_sling = _sling.Path(path)
|
|
type QueryParams struct {
|
|
ProjectName string `url:"project_name,omitempty"`
|
|
}
|
|
_sling = _sling.QueryStruct(&QueryParams{ProjectName: projectName})
|
|
|
|
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
return httpStatusCode, err
|
|
}
|
|
|
|
//Return specific project detail infomation
|
|
func (a api) ProjectsGetByPID(projectID string) (int, apilib.Project, error) {
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
//create api path
|
|
path := "api/projects/" + projectID
|
|
_sling = _sling.Path(path)
|
|
|
|
var successPayload apilib.Project
|
|
|
|
httpStatusCode, body, err := request(_sling, jsonAcceptHeader)
|
|
if err == nil && httpStatusCode == 200 {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
return httpStatusCode, successPayload, err
|
|
}
|
|
|
|
//Search projects by projectName and isPublic
|
|
func (a api) ProjectsGet(projectName string, isPublic int32) (int, []apilib.Project, error) {
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
//create api path
|
|
path := "api/projects"
|
|
_sling = _sling.Path(path)
|
|
type QueryParams struct {
|
|
ProjectName string `url:"project_name,omitempty"`
|
|
IsPubilc int32 `url:"is_public,omitempty"`
|
|
}
|
|
_sling = _sling.QueryStruct(&QueryParams{ProjectName: projectName, IsPubilc: isPublic})
|
|
|
|
var successPayload []apilib.Project
|
|
|
|
httpStatusCode, body, err := request(_sling, jsonAcceptHeader)
|
|
if err == nil && httpStatusCode == 200 {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
|
|
return httpStatusCode, successPayload, err
|
|
}
|
|
|
|
//Update properties for a selected project.
|
|
func (a api) ToggleProjectPublicity(prjUsr usrInfo, projectID string, ispublic int32) (int, error) {
|
|
// create path and map variables
|
|
path := "/api/projects/" + projectID + "/publicity/"
|
|
_sling := sling.New().Put(a.basePath)
|
|
|
|
_sling = _sling.Path(path)
|
|
|
|
type QueryParams struct {
|
|
Public int32 `json:"public,omitempty"`
|
|
}
|
|
|
|
_sling = _sling.BodyJSON(&QueryParams{Public: ispublic})
|
|
|
|
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
return httpStatusCode, err
|
|
|
|
}
|
|
|
|
//Get access logs accompany with a relevant project.
|
|
func (a api) ProjectLogsFilter(prjUsr usrInfo, projectID string, accessLog apilib.AccessLogFilter) (int, []byte, error) {
|
|
//func (a api) ProjectLogsFilter(prjUsr usrInfo, projectID string, accessLog apilib.AccessLog) (int, apilib.AccessLog, error) {
|
|
_sling := sling.New().Post(a.basePath)
|
|
|
|
path := "/api/projects/" + projectID + "/logs/filter"
|
|
|
|
_sling = _sling.Path(path)
|
|
|
|
// body params
|
|
_sling = _sling.BodyJSON(accessLog)
|
|
|
|
//var successPayload []apilib.AccessLog
|
|
|
|
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
/*
|
|
if err == nil && httpStatusCode == 200 {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
*/
|
|
return httpStatusCode, body, err
|
|
// return httpStatusCode, successPayload, err
|
|
}
|
|
|
|
//-------------------------Member Test---------------------------------------//
|
|
|
|
//Return relevant role members of projectID
|
|
func (a api) GetProjectMembersByProID(prjUsr usrInfo, projectID string) (int, []apilib.User, error) {
|
|
_sling := sling.New().Get(a.basePath)
|
|
|
|
path := "/api/projects/" + projectID + "/members/"
|
|
|
|
_sling = _sling.Path(path)
|
|
|
|
var successPayload []apilib.User
|
|
|
|
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
if err == nil && httpStatusCode == 200 {
|
|
err = json.Unmarshal(body, &successPayload)
|
|
}
|
|
return httpStatusCode, successPayload, err
|
|
}
|
|
|
|
//Add project role member accompany with projectID
|
|
func (a api) AddProjectMember(prjUsr usrInfo, projectID string, roles apilib.RoleParam) (int, []byte, error) {
|
|
_sling := sling.New().Post(a.basePath)
|
|
|
|
path := "/api/projects/" + projectID + "/members/"
|
|
|
|
_sling = _sling.Path(path)
|
|
|
|
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, prjUsr)
|
|
return httpStatusCode, body, err
|
|
|
|
}
|
|
|
|
//Return projects created by Harbor
|
|
//func (a HarborApi) ProjectsGet (projectName string, isPublic int32) ([]Project, error) {
|
|
// }
|
|
|
|
//Check if the project name user provided already exists.
|
|
//func (a HarborApi) ProjectsHead (projectName string) (error) {
|
|
//}
|
|
|
|
//Get access logs accompany with a relevant project.
|
|
//func (a HarborApi) ProjectsProjectIdLogsFilterPost (projectID int32, accessLog AccessLog) ([]AccessLog, error) {
|
|
//}
|
|
|
|
//Return a project's relevant role members.
|
|
//func (a HarborApi) ProjectsProjectIdMembersGet (projectID int32) ([]Role, error) {
|
|
//}
|
|
|
|
//Add project role member accompany with relevant project and user.
|
|
//func (a HarborApi) ProjectsProjectIdMembersPost (projectID int32, roles RoleParam) (error) {
|
|
//}
|
|
|
|
//Delete project role members accompany with relevant project and user.
|
|
//func (a HarborApi) ProjectsProjectIdMembersUserIdDelete (projectID int32, userId int32) (error) {
|
|
//}
|
|
|
|
//Return role members accompany with relevant project and user.
|
|
//func (a HarborApi) ProjectsProjectIdMembersUserIdGet (projectID int32, userId int32) ([]Role, error) {
|
|
//}
|
|
|
|
//Update project role members accompany with relevant project and user.
|
|
//func (a HarborApi) ProjectsProjectIdMembersUserIdPut (projectID int32, userId int32, roles RoleParam) (error) {
|
|
//}
|
|
|
|
//Update properties for a selected project.
|
|
//func (a HarborApi) ProjectsProjectIdPut (projectID int32, project Project) (error) {
|
|
//}
|
|
|
|
//Get repositories accompany with relevant project and repo name.
|
|
//func (a HarborApi) RepositoriesGet (projectID int32, q string) ([]Repository, error) {
|
|
//}
|
|
|
|
//Get manifests of a relevant repository.
|
|
//func (a HarborApi) RepositoriesManifestGet (repoName string, tag string) (error) {
|
|
//}
|
|
|
|
//Get tags of a relevant repository.
|
|
//func (a HarborApi) RepositoriesTagsGet (repoName string) (error) {
|
|
//}
|
|
|
|
//Get registered users of Harbor.
|
|
//func (a HarborApi) UsersGet (userName string) ([]User, error) {
|
|
//}
|
|
|
|
//Creates a new user account.
|
|
//func (a HarborApi) UsersPost (user User) (error) {
|
|
//}
|
|
|
|
//Mark a registered user as be removed.
|
|
//func (a HarborApi) UsersUserIdDelete (userId int32) (error) {
|
|
//}
|
|
|
|
//Update a registered user to change to be an administrator of Harbor.
|
|
//func (a HarborApi) UsersUserIdPut (userId int32) (error) {
|
|
//}
|
|
|
|
func updateInitPassword(userID int, password string) error {
|
|
queryUser := models.User{UserID: userID}
|
|
user, err := dao.GetUser(queryUser)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to get user, userID: %d %v", userID, err)
|
|
}
|
|
if user == nil {
|
|
return fmt.Errorf("User id: %d does not exist.", userID)
|
|
}
|
|
if user.Salt == "" {
|
|
salt, err := dao.GenerateRandomString()
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to generate salt for encrypting password, %v", err)
|
|
}
|
|
|
|
user.Salt = salt
|
|
user.Password = password
|
|
err = dao.ChangeUserPassword(*user)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
|
}
|
|
|
|
} else {
|
|
}
|
|
return nil
|
|
}
|