Fix bug in ping registry API

Fix bug in ping registry API: accept both ID and other properties

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2019-04-09 17:41:51 +08:00
parent 68342c68bc
commit a2fcb41b31
4 changed files with 107 additions and 41 deletions

View File

@ -3840,6 +3840,30 @@ definitions:
update_time:
type: string
description: The update time of the policy.
PingRegistry:
type: object
properties:
id:
type: integer
description: The ID of the registry
type:
type: string
description: Type of the registry, e.g. 'harbor'.
url:
type: string
description: The registry address URL string.
credential_type:
type: string
description: Credential type of the registry, e.g. 'basic'.
access_key:
type: string
description: The registry access key.
access_secret:
type: string
description: The registry access secret.
insecure:
type: boolean
description: Whether or not the certificate will be verified when Harbor tries to access the server.
PutRegistry:
type: object
properties:

View File

@ -1176,7 +1176,17 @@ func (a testapi) RegistryCreate(authInfo usrInfo, registry *model.Registry) (int
return code, err
}
func (a testapi) RegistryPing(authInfo usrInfo, registry *model.Registry) (int, error) {
type pingReq struct {
ID *int64 `json:"id"`
Type *string `json:"type"`
URL *string `json:"url"`
CredentialType *string `json:"credential_type"`
AccessKey *string `json:"access_key"`
AccessSecret *string `json:"access_secret"`
Insecure *bool `json:"insecure"`
}
func (a testapi) RegistryPing(authInfo usrInfo, registry *pingReq) (int, error) {
_sling := sling.New().Base(a.basePath).Post("/api/registries/ping").BodyJSON(registry)
code, _, err := request(_sling, jsonAcceptHeader, authInfo)
return code, err

View File

@ -5,12 +5,13 @@ import (
"net/http"
"strconv"
"github.com/goharbor/harbor/src/replication/ng/event"
common_http "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/api/models"
"github.com/goharbor/harbor/src/replication/ng"
"github.com/goharbor/harbor/src/replication/ng/adapter"
"github.com/goharbor/harbor/src/replication/ng/event"
"github.com/goharbor/harbor/src/replication/ng/model"
"github.com/goharbor/harbor/src/replication/ng/policy"
"github.com/goharbor/harbor/src/replication/ng/registry"
@ -42,38 +43,84 @@ func (t *RegistryAPI) Prepare() {
// Ping checks health status of a registry
func (t *RegistryAPI) Ping() {
r := &model.Registry{}
t.DecodeJSONReqAndValidate(r)
req := struct {
ID *int64 `json:"id"`
Type *string `json:"type"`
URL *string `json:"url"`
CredentialType *string `json:"credential_type"`
AccessKey *string `json:"access_key"`
AccessSecret *string `json:"access_secret"`
Insecure *bool `json:"insecure"`
}{}
t.DecodeJSONReq(&req)
reg := &model.Registry{}
var err error
id := r.ID
if id != 0 {
r, err = t.manager.Get(id)
if req.ID != nil {
reg, err = t.manager.Get(*req.ID)
if err != nil {
log.Errorf("failed to get registry %s: %v", r.Name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
t.HandleInternalServerError(fmt.Sprintf("failed to get registry %d: %v", *req.ID, err))
return
}
if r == nil {
t.CustomAbort(http.StatusNotFound, fmt.Sprintf("Registry %d not found", id))
if reg == nil {
t.HandleNotFound(fmt.Sprintf("registry %d not found", *req.ID))
return
}
}
if req.Type != nil {
reg.Type = model.RegistryType(*req.Type)
}
if req.URL != nil {
url, err := utils.ParseEndpoint(*req.URL)
if err != nil {
t.HandleBadRequest(err.Error())
return
}
if len(r.URL) == 0 {
t.CustomAbort(http.StatusBadRequest, "URL can't be emptry")
// Prevent SSRF security issue #3755
reg.URL = url.Scheme + "://" + url.Host + url.Path
}
if req.CredentialType != nil {
if reg.Credential == nil {
reg.Credential = &model.Credential{}
}
reg.Credential.Type = model.CredentialType(*req.CredentialType)
}
if req.AccessKey != nil {
if reg.Credential == nil {
reg.Credential = &model.Credential{}
}
reg.Credential.AccessKey = *req.AccessKey
}
if req.AccessSecret != nil {
if reg.Credential == nil {
reg.Credential = &model.Credential{}
}
reg.Credential.AccessSecret = *req.AccessSecret
}
if req.Insecure != nil {
reg.Insecure = *req.Insecure
}
if len(reg.Type) == 0 || len(reg.URL) == 0 {
t.HandleBadRequest("type or url cannot be empty")
return
}
status, err := registry.CheckHealthStatus(r)
status, err := registry.CheckHealthStatus(reg)
if err != nil {
t.CustomAbort(http.StatusInternalServerError, fmt.Sprintf("Ping registry %s error: %v", r.URL, err))
e, ok := err.(*common_http.Error)
if ok && e.Code == http.StatusUnauthorized {
t.HandleBadRequest("invalid credential")
return
}
t.HandleInternalServerError(fmt.Sprintf("failed to check health of registry %s: %v", reg.URL, err))
return
}
if status != model.Healthy {
t.CustomAbort(http.StatusBadRequest, fmt.Sprintf("Ping registry %d failed", r.ID))
t.HandleBadRequest("")
return
}
return
}

View File

@ -16,7 +16,7 @@ import (
var (
testRegistry = &model.Registry{
Name: "test1",
URL: "https://test.harbor.io",
URL: "https://127.0.0.1",
Type: "harbor",
Credential: &model.Credential{
Type: model.CredentialTypeBasic,
@ -120,35 +120,20 @@ func (suite *RegistrySuite) TestPost() {
func (suite *RegistrySuite) TestPing() {
assert := assert.New(suite.T())
code, err := suite.testAPI.RegistryPing(*admin, &model.Registry{
ID: suite.defaultRegistry.ID,
})
assert.Nil(err)
assert.Equal(http.StatusInternalServerError, code)
code, err = suite.testAPI.RegistryPing(*admin, &model.Registry{
ID: -1,
})
var id int64 = -1
code, err := suite.testAPI.RegistryPing(*admin,
&pingReq{
ID: &id,
})
assert.Nil(err)
assert.Equal(http.StatusNotFound, code)
code, err = suite.testAPI.RegistryPing(*admin, &model.Registry{})
code, err = suite.testAPI.RegistryPing(*admin, nil)
assert.Nil(err)
assert.Equal(http.StatusBadRequest, code)
code, err = suite.testAPI.RegistryPing(*admin, &model.Registry{
URL: "http://foo.bar",
Credential: &model.Credential{
Type: model.CredentialTypeBasic,
AccessKey: "admin",
AccessSecret: "Harbor12345",
},
})
assert.Nil(err)
assert.NotEqual(http.StatusBadRequest, code)
code, err = suite.testAPI.RegistryPing(*testUser, &model.Registry{
ID: suite.defaultRegistry.ID,
code, err = suite.testAPI.RegistryPing(*testUser, &pingReq{
ID: &suite.defaultRegistry.ID,
})
assert.Nil(err)
assert.Equal(http.StatusForbidden, code)