mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-22 23:51:27 +01:00
add validation to create, update policies and targets
This commit is contained in:
parent
93a4158d21
commit
f4e6d048b2
26
api/base.go
26
api/base.go
@ -17,8 +17,10 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/vmware/harbor/auth"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
@ -51,6 +53,30 @@ func (b *BaseAPI) DecodeJSONReq(v interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates v if it implements interface validation.ValidFormer
|
||||
func (b *BaseAPI) Validate(v interface{}) {
|
||||
validator := validation.Validation{}
|
||||
isValid, err := validator.Valid(v)
|
||||
if err != nil {
|
||||
log.Errorf("failed to validate: %v", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
message := ""
|
||||
for _, e := range validator.Errors {
|
||||
message += fmt.Sprintf("%s %s \n", e.Field, e.Message)
|
||||
}
|
||||
b.CustomAbort(http.StatusBadRequest, message)
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeJsonReqAndValidate does both decoding and validation
|
||||
func (b *BaseAPI) DecodeJsonReqAndValidate(v interface{}) {
|
||||
b.DecodeJSONReq(v)
|
||||
b.Validate(v)
|
||||
}
|
||||
|
||||
// ValidateUser checks if the request triggered by a valid user
|
||||
func (b *BaseAPI) ValidateUser() int {
|
||||
|
||||
|
@ -69,9 +69,40 @@ func (pa *RepPolicyAPI) Get() {
|
||||
|
||||
// Post creates a policy, and if it is enbled, the replication will be triggered right now.
|
||||
func (pa *RepPolicyAPI) Post() {
|
||||
policy := models.RepPolicy{}
|
||||
pa.DecodeJSONReq(&policy)
|
||||
pid, err := dao.AddRepPolicy(policy)
|
||||
policy := &models.RepPolicy{}
|
||||
pa.DecodeJsonReqAndValidate(policy)
|
||||
|
||||
po, err := dao.GetRepPolicyByName(policy.Name)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get policy %s: %v", policy.Name, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if po != nil {
|
||||
pa.CustomAbort(http.StatusConflict, "name is already used")
|
||||
}
|
||||
|
||||
project, err := dao.GetProjectByID(policy.ProjectID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get project %d: %v", policy.ProjectID, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("project %d does not exist", policy.ProjectID))
|
||||
}
|
||||
|
||||
target, err := dao.GetRepTarget(policy.TargetID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get target %d: %v", policy.TargetID, err)
|
||||
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID))
|
||||
}
|
||||
|
||||
pid, err := dao.AddRepPolicy(*policy)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add policy to DB, error: %v", err)
|
||||
pa.RenderError(http.StatusInternalServerError, "Internal Error")
|
||||
|
@ -164,10 +164,16 @@ func (t *TargetAPI) Get() {
|
||||
// Post ...
|
||||
func (t *TargetAPI) Post() {
|
||||
target := &models.RepTarget{}
|
||||
t.DecodeJSONReq(target)
|
||||
t.DecodeJsonReqAndValidate(target)
|
||||
|
||||
if len(target.Name) == 0 || len(target.URL) == 0 {
|
||||
t.CustomAbort(http.StatusBadRequest, "name or URL is nil")
|
||||
ta, err := dao.GetRepTargetByName(target.Name)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get target %s: %v", target.Name, err)
|
||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if ta != nil {
|
||||
t.CustomAbort(http.StatusConflict, "name is already used")
|
||||
}
|
||||
|
||||
if len(target.Password) != 0 {
|
||||
@ -187,16 +193,32 @@ func (t *TargetAPI) Post() {
|
||||
func (t *TargetAPI) Put() {
|
||||
id := t.getIDFromURL()
|
||||
if id == 0 {
|
||||
t.CustomAbort(http.StatusBadRequest, http.StatusText(http.StatusBadRequest))
|
||||
t.CustomAbort(http.StatusBadRequest, "id can not be empty or 0")
|
||||
}
|
||||
|
||||
target := &models.RepTarget{}
|
||||
t.DecodeJSONReq(target)
|
||||
t.DecodeJsonReqAndValidate(target)
|
||||
|
||||
if target.ID == 0 {
|
||||
target.ID = id
|
||||
originTarget, err := dao.GetRepTarget(id)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get target %d: %v", id, err)
|
||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if target.Name != originTarget.Name {
|
||||
ta, err := dao.GetRepTargetByName(target.Name)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get target %s: %v", target.Name, err)
|
||||
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if ta != nil {
|
||||
t.CustomAbort(http.StatusConflict, "name is already used")
|
||||
}
|
||||
}
|
||||
|
||||
target.ID = id
|
||||
|
||||
if len(target.Password) != 0 {
|
||||
target.Password = utils.ReversibleEncrypt(target.Password)
|
||||
}
|
||||
|
@ -26,6 +26,17 @@ func GetRepTarget(id int64) (*models.RepTarget, error) {
|
||||
return &t, err
|
||||
}
|
||||
|
||||
// GetRepTargetByName ...
|
||||
func GetRepTargetByName(name string) (*models.RepTarget, error) {
|
||||
o := orm.NewOrm()
|
||||
t := models.RepTarget{Name: name}
|
||||
err := o.Read(&t, "Name")
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &t, err
|
||||
}
|
||||
|
||||
// DeleteRepTarget ...
|
||||
func DeleteRepTarget(id int64) error {
|
||||
o := orm.NewOrm()
|
||||
@ -87,6 +98,17 @@ func GetRepPolicy(id int64) (*models.RepPolicy, error) {
|
||||
return &p, err
|
||||
}
|
||||
|
||||
// GetRepPolicyByName ...
|
||||
func GetRepPolicyByName(name string) (*models.RepPolicy, error) {
|
||||
o := orm.NewOrm()
|
||||
p := models.RepPolicy{Name: name}
|
||||
err := o.Read(&p, "Name")
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &p, err
|
||||
}
|
||||
|
||||
// GetRepPolicyByProject ...
|
||||
func GetRepPolicyByProject(projectID int64) ([]*models.RepPolicy, error) {
|
||||
var res []*models.RepPolicy
|
||||
|
@ -2,6 +2,8 @@ package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/validation"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -42,6 +44,33 @@ type RepPolicy struct {
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
}
|
||||
|
||||
// Validate ...
|
||||
func (r *RepPolicy) Valid(v *validation.Validation) {
|
||||
if len(r.Name) == 0 {
|
||||
v.SetError("name", "can not be empty")
|
||||
}
|
||||
|
||||
if len(r.Name) > 256 {
|
||||
v.SetError("name", "max length is 256")
|
||||
}
|
||||
|
||||
if r.ProjectID <= 0 {
|
||||
v.SetError("project_id", "invalid")
|
||||
}
|
||||
|
||||
if r.TargetID <= 0 {
|
||||
v.SetError("target_id", "invalid")
|
||||
}
|
||||
|
||||
if r.Enabled != 0 && r.Enabled != 1 {
|
||||
v.SetError("enabled", "must be 0 or 1")
|
||||
}
|
||||
|
||||
if len(r.CronStr) > 256 {
|
||||
v.SetError("cron_str", "max length is 256")
|
||||
}
|
||||
}
|
||||
|
||||
// RepJob is the model for a replication job, which is the execution unit on job service, currently it is used to transfer/remove
|
||||
// a repository to/from a remote registry instance.
|
||||
type RepJob struct {
|
||||
@ -68,6 +97,31 @@ type RepTarget struct {
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
}
|
||||
|
||||
// Valid ...
|
||||
func (r *RepTarget) Valid(v *validation.Validation) {
|
||||
if len(r.Name) == 0 {
|
||||
v.SetError("name", "can not be empty")
|
||||
}
|
||||
|
||||
if len(r.Name) > 64 {
|
||||
v.SetError("name", "max length is 64")
|
||||
}
|
||||
|
||||
if len(r.URL) == 0 {
|
||||
v.SetError("endpoint", "can not be empty")
|
||||
}
|
||||
|
||||
if len(r.URL) > 64 {
|
||||
v.SetError("endpoint", "max length is 64")
|
||||
}
|
||||
|
||||
// password is encoded using base64, the length of this field
|
||||
// in DB is 64, so the max length in request is 48
|
||||
if len(r.Password) > 48 {
|
||||
v.SetError("password", "max length is 48")
|
||||
}
|
||||
}
|
||||
|
||||
//TableName is required by by beego orm to map RepTarget to table replication_target
|
||||
func (rt *RepTarget) TableName() string {
|
||||
return "replication_target"
|
||||
|
Loading…
Reference in New Issue
Block a user