Support well-formatted error returned from the REST APIs. (#6957)

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
Wang Yan 2019-04-17 16:43:06 +08:00 committed by GitHub
parent 8a7d04ef47
commit a6af9e9972
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 952 additions and 739 deletions

View File

@ -24,6 +24,7 @@ import (
commonhttp "github.com/goharbor/harbor/src/common/http" commonhttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"errors"
"github.com/astaxie/beego" "github.com/astaxie/beego"
) )
@ -48,68 +49,6 @@ func (b *BaseAPI) GetInt64FromPath(key string) (int64, error) {
return strconv.ParseInt(value, 10, 64) return strconv.ParseInt(value, 10, 64)
} }
// HandleNotFound ...
func (b *BaseAPI) HandleNotFound(text string) {
log.Info(text)
b.RenderError(http.StatusNotFound, text)
}
// HandleUnauthorized ...
func (b *BaseAPI) HandleUnauthorized() {
log.Info("unauthorized")
b.RenderError(http.StatusUnauthorized, "")
}
// HandleForbidden ...
func (b *BaseAPI) HandleForbidden(text string) {
log.Infof("forbidden: %s", text)
b.RenderError(http.StatusForbidden, text)
}
// HandleBadRequest ...
func (b *BaseAPI) HandleBadRequest(text string) {
log.Info(text)
b.RenderError(http.StatusBadRequest, text)
}
// HandleStatusPreconditionFailed ...
func (b *BaseAPI) HandleStatusPreconditionFailed(text string) {
log.Info(text)
b.RenderError(http.StatusPreconditionFailed, text)
}
// HandleConflict ...
func (b *BaseAPI) HandleConflict(text ...string) {
msg := ""
if len(text) > 0 {
msg = text[0]
}
log.Infof("conflict: %s", msg)
b.RenderError(http.StatusConflict, msg)
}
// HandleInternalServerError ...
func (b *BaseAPI) HandleInternalServerError(text string) {
log.Error(text)
b.RenderError(http.StatusInternalServerError, "")
}
// ParseAndHandleError : if the err is an instance of utils/error.Error,
// return the status code and the detail message contained in err, otherwise
// return 500
func (b *BaseAPI) ParseAndHandleError(text string, err error) {
if err == nil {
return
}
log.Errorf("%s: %v", text, err)
if e, ok := err.(*commonhttp.Error); ok {
b.RenderError(e.Code, e.Message)
return
}
b.RenderError(http.StatusInternalServerError, "")
}
// Render returns nil as it won't render template // Render returns nil as it won't render template
func (b *BaseAPI) Render() error { func (b *BaseAPI) Render() error {
return nil return nil
@ -120,23 +59,35 @@ func (b *BaseAPI) RenderError(code int, text string) {
http.Error(b.Ctx.ResponseWriter, text, code) http.Error(b.Ctx.ResponseWriter, text, code)
} }
// RenderFormattedError renders errors with well formatted style
func (b *BaseAPI) RenderFormattedError(errorCode int, errorMsg string) {
error := commonhttp.Error{
Code: errorCode,
Message: errorMsg,
}
formattedErrMsg := error.String()
log.Errorf("%s %s failed with error: %s", b.Ctx.Request.Method, b.Ctx.Request.URL.String(), formattedErrMsg)
b.RenderError(error.Code, formattedErrMsg)
}
// DecodeJSONReq decodes a json request // DecodeJSONReq decodes a json request
func (b *BaseAPI) DecodeJSONReq(v interface{}) { func (b *BaseAPI) DecodeJSONReq(v interface{}) error {
err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<32), v) err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<32), v)
if err != nil { if err != nil {
log.Errorf("Error while decoding the json request, error: %v, %v", log.Errorf("Error while decoding the json request, error: %v, %v",
err, string(b.Ctx.Input.CopyBody(1 << 32)[:])) err, string(b.Ctx.Input.CopyBody(1 << 32)[:]))
b.CustomAbort(http.StatusBadRequest, "Invalid json request") return errors.New("Invalid json request")
} }
return nil
} }
// Validate validates v if it implements interface validation.ValidFormer // Validate validates v if it implements interface validation.ValidFormer
func (b *BaseAPI) Validate(v interface{}) { func (b *BaseAPI) Validate(v interface{}) (bool, error) {
validator := validation.Validation{} validator := validation.Validation{}
isValid, err := validator.Valid(v) isValid, err := validator.Valid(v)
if err != nil { if err != nil {
log.Errorf("failed to validate: %v", err) log.Errorf("failed to validate: %v", err)
b.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) return false, err
} }
if !isValid { if !isValid {
@ -144,14 +95,17 @@ func (b *BaseAPI) Validate(v interface{}) {
for _, e := range validator.Errors { for _, e := range validator.Errors {
message += fmt.Sprintf("%s %s \n", e.Field, e.Message) message += fmt.Sprintf("%s %s \n", e.Field, e.Message)
} }
b.CustomAbort(http.StatusBadRequest, message) return false, errors.New(message)
} }
return true, nil
} }
// DecodeJSONReqAndValidate does both decoding and validation // DecodeJSONReqAndValidate does both decoding and validation
func (b *BaseAPI) DecodeJSONReqAndValidate(v interface{}) { func (b *BaseAPI) DecodeJSONReqAndValidate(v interface{}) (bool, error) {
b.DecodeJSONReq(v) if err := b.DecodeJSONReq(v); err != nil {
b.Validate(v) return false, err
}
return b.Validate(v)
} }
// Redirect does redirection to resource URI with http header status code. // Redirect does redirection to resource URI with http header status code.
@ -163,18 +117,18 @@ func (b *BaseAPI) Redirect(statusCode int, resouceID string) {
} }
// GetIDFromURL checks the ID in request URL // GetIDFromURL checks the ID in request URL
func (b *BaseAPI) GetIDFromURL() int64 { func (b *BaseAPI) GetIDFromURL() (int64, error) {
idStr := b.Ctx.Input.Param(":id") idStr := b.Ctx.Input.Param(":id")
if len(idStr) == 0 { if len(idStr) == 0 {
b.CustomAbort(http.StatusBadRequest, "invalid ID in URL") return 0, errors.New("invalid ID in URL")
} }
id, err := strconv.ParseInt(idStr, 10, 64) id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 { if err != nil || id <= 0 {
b.CustomAbort(http.StatusBadRequest, "invalid ID in URL") return 0, errors.New("invalid ID in URL")
} }
return id return id, nil
} }
// SetPaginationHeader set"Link" and "X-Total-Count" header for pagination request // SetPaginationHeader set"Link" and "X-Total-Count" header for pagination request
@ -213,15 +167,15 @@ func (b *BaseAPI) SetPaginationHeader(total, page, pageSize int64) {
} }
// GetPaginationParams ... // GetPaginationParams ...
func (b *BaseAPI) GetPaginationParams() (page, pageSize int64) { func (b *BaseAPI) GetPaginationParams() (page, pageSize int64, err error) {
page, err := b.GetInt64("page", 1) page, err = b.GetInt64("page", 1)
if err != nil || page <= 0 { if err != nil || page <= 0 {
b.CustomAbort(http.StatusBadRequest, "invalid page") return 0, 0, errors.New("invalid page")
} }
pageSize, err = b.GetInt64("page_size", defaultPageSize) pageSize, err = b.GetInt64("page_size", defaultPageSize)
if err != nil || pageSize <= 0 { if err != nil || pageSize <= 0 {
b.CustomAbort(http.StatusBadRequest, "invalid page_size") return 0, 0, errors.New("invalid page_size")
} }
if pageSize > maxPageSize { if pageSize > maxPageSize {
@ -229,5 +183,60 @@ func (b *BaseAPI) GetPaginationParams() (page, pageSize int64) {
log.Debugf("the parameter page_size %d exceeds the max %d, set it to max", pageSize, maxPageSize) log.Debugf("the parameter page_size %d exceeds the max %d, set it to max", pageSize, maxPageSize)
} }
return page, pageSize return page, pageSize, nil
}
// ParseAndHandleError : if the err is an instance of utils/error.Error,
// return the status code and the detail message contained in err, otherwise
// return 500
func (b *BaseAPI) ParseAndHandleError(text string, err error) {
if err == nil {
return
}
log.Errorf("%s: %v", text, err)
if e, ok := err.(*commonhttp.Error); ok {
b.RenderFormattedError(e.Code, e.Message)
return
}
b.SendInternalServerError(errors.New(""))
}
// SendUnAuthorizedError sends unauthorized error to the client.
func (b *BaseAPI) SendUnAuthorizedError(err error) {
b.RenderFormattedError(http.StatusUnauthorized, err.Error())
}
// SendConflictError sends conflict error to the client.
func (b *BaseAPI) SendConflictError(err error) {
b.RenderFormattedError(http.StatusConflict, err.Error())
}
// SendNotFoundError sends not found error to the client.
func (b *BaseAPI) SendNotFoundError(err error) {
b.RenderFormattedError(http.StatusNotFound, err.Error())
}
// SendBadRequestError sends bad request error to the client.
func (b *BaseAPI) SendBadRequestError(err error) {
b.RenderFormattedError(http.StatusBadRequest, err.Error())
}
// SendInternalServerError sends internal server error to the client.
func (b *BaseAPI) SendInternalServerError(err error) {
b.RenderFormattedError(http.StatusInternalServerError, err.Error())
}
// SendForbiddenError sends forbidden error to the client.
func (b *BaseAPI) SendForbiddenError(err error) {
b.RenderFormattedError(http.StatusForbidden, err.Error())
}
// SendPreconditionFailedError sends forbidden error to the client.
func (b *BaseAPI) SendPreconditionFailedError(err error) {
b.RenderFormattedError(http.StatusPreconditionFailed, err.Error())
}
// SendStatusServiceUnavailableError sends forbidden error to the client.
func (b *BaseAPI) SendStatusServiceUnavailableError(err error) {
b.RenderFormattedError(http.StatusServiceUnavailable, err.Error())
} }

View File

@ -15,16 +15,26 @@
package http package http
import ( import (
"encoding/json"
"fmt" "fmt"
) )
// Error wrap HTTP status code and message as an error // Error wrap HTTP status code and message as an error
type Error struct { type Error struct {
Code int Code int `json:"code"`
Message string Message string `json:"message"`
} }
// Error ... // Error ...
func (e *Error) Error() string { func (e *Error) Error() string {
return fmt.Sprintf("http error: code %d, message %s", e.Code, e.Message) return fmt.Sprintf("http error: code %d, message %s", e.Code, e.Message)
} }
// String wraps the error msg to the well formatted error message
func (e *Error) String() string {
data, err := json.Marshal(&e)
if err != nil {
return e.Message
}
return string(data)
}

View File

@ -0,0 +1,17 @@
package http
import (
"github.com/stretchr/testify/assert"
"testing"
)
// Test case for error wrapping function.
func TestWrapError(t *testing.T) {
err := Error{
Code: 1,
Message: "test",
}
assert.Equal(t, err.String(), "{\"code\":1,\"message\":\"test\"}")
}

View File

@ -1,9 +1,9 @@
package token package token
import ( import (
"errors"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
"github.com/pkg/errors"
) )
// RobotClaims implements the interface of jwt.Claims // RobotClaims implements the interface of jwt.Claims

View File

@ -27,6 +27,7 @@ import (
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/api/models" "github.com/goharbor/harbor/src/core/api/models"
utils_core "github.com/goharbor/harbor/src/core/utils" utils_core "github.com/goharbor/harbor/src/core/utils"
"github.com/pkg/errors"
) )
// AJAPI manages the CRUD of admin job and its schedule, any API wants to handle manual and cron job like ScanAll and GC cloud reuse it. // AJAPI manages the CRUD of admin job and its schedule, any API wants to handle manual and cron job like ScanAll and GC cloud reuse it.
@ -42,7 +43,7 @@ func (aj *AJAPI) Prepare() {
// updateSchedule update a schedule of admin job. // updateSchedule update a schedule of admin job.
func (aj *AJAPI) updateSchedule(ajr models.AdminJobReq) { func (aj *AJAPI) updateSchedule(ajr models.AdminJobReq) {
if ajr.Schedule.Type == models.ScheduleManual { if ajr.Schedule.Type == models.ScheduleManual {
aj.HandleInternalServerError(fmt.Sprintf("Fail to update admin job schedule as wrong schedule type: %s.", ajr.Schedule.Type)) aj.SendInternalServerError((fmt.Errorf("fail to update admin job schedule as wrong schedule type: %s", ajr.Schedule.Type)))
return return
} }
@ -52,24 +53,24 @@ func (aj *AJAPI) updateSchedule(ajr models.AdminJobReq) {
} }
jobs, err := dao.GetAdminJobs(query) jobs, err := dao.GetAdminJobs(query)
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
if len(jobs) != 1 { if len(jobs) != 1 {
aj.HandleInternalServerError("Fail to update admin job schedule as we found more than one schedule in system, please ensure that only one schedule left for your job .") aj.SendInternalServerError(errors.New("fail to update admin job schedule as we found more than one schedule in system, please ensure that only one schedule left for your job"))
return return
} }
// stop the scheduled job and remove it. // stop the scheduled job and remove it.
if err = utils_core.GetJobServiceClient().PostAction(jobs[0].UUID, common_job.JobActionStop); err != nil { if err = utils_core.GetJobServiceClient().PostAction(jobs[0].UUID, common_job.JobActionStop); err != nil {
if e, ok := err.(*common_http.Error); !ok || e.Code != http.StatusNotFound { if e, ok := err.(*common_http.Error); !ok || e.Code != http.StatusNotFound {
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
} }
if err = dao.DeleteAdminJob(jobs[0].ID); err != nil { if err = dao.DeleteAdminJob(jobs[0].ID); err != nil {
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
@ -85,17 +86,17 @@ func (aj *AJAPI) get(id int64) {
ID: id, ID: id,
}) })
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to get admin jobs: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err))
return return
} }
if len(jobs) == 0 { if len(jobs) == 0 {
aj.HandleNotFound("No admin job found.") aj.SendNotFoundError(errors.New("no admin job found"))
return return
} }
adminJobRep, err := convertToAdminJobRep(jobs[0]) adminJobRep, err := convertToAdminJobRep(jobs[0])
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to convert admin job response: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err))
return return
} }
@ -107,7 +108,7 @@ func (aj *AJAPI) get(id int64) {
func (aj *AJAPI) list(name string) { func (aj *AJAPI) list(name string) {
jobs, err := dao.GetTop10AdminJobsOfName(name) jobs, err := dao.GetTop10AdminJobsOfName(name)
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to get admin jobs: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err))
return return
} }
@ -115,7 +116,7 @@ func (aj *AJAPI) list(name string) {
for _, job := range jobs { for _, job := range jobs {
AdminJobRep, err := convertToAdminJobRep(job) AdminJobRep, err := convertToAdminJobRep(job)
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to convert admin job response: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err))
return return
} }
AdminJobReps = append(AdminJobReps, &AdminJobRep) AdminJobReps = append(AdminJobReps, &AdminJobRep)
@ -134,18 +135,18 @@ func (aj *AJAPI) getSchedule(name string) {
Kind: common_job.JobKindPeriodic, Kind: common_job.JobKindPeriodic,
}) })
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to get admin jobs: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err))
return return
} }
if len(jobs) > 1 { if len(jobs) > 1 {
aj.HandleInternalServerError("Get more than one scheduled admin job, make sure there has only one.") aj.SendInternalServerError(errors.New("get more than one scheduled admin job, make sure there has only one"))
return return
} }
if len(jobs) != 0 { if len(jobs) != 0 {
adminJobRep, err := convertToAdminJobRep(jobs[0]) adminJobRep, err := convertToAdminJobRep(jobs[0])
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to convert admin job response: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err))
return return
} }
adminJobSchedule.Schedule = adminJobRep.Schedule adminJobSchedule.Schedule = adminJobRep.Schedule
@ -160,11 +161,13 @@ func (aj *AJAPI) getLog(id int64) {
job, err := dao.GetAdminJob(id) job, err := dao.GetAdminJob(id)
if err != nil { if err != nil {
log.Errorf("Failed to load job data for job: %d, error: %v", id, err) log.Errorf("Failed to load job data for job: %d, error: %v", id, err)
aj.CustomAbort(http.StatusInternalServerError, "Failed to get Job data") aj.SendInternalServerError(errors.New("Failed to get Job data"))
return
} }
if job == nil { if job == nil {
log.Errorf("Failed to get admin job: %d", id) log.Errorf("Failed to get admin job: %d", id)
aj.CustomAbort(http.StatusNotFound, "Failed to get Job") aj.SendNotFoundError(errors.New("Failed to get Job"))
return
} }
logBytes, err := utils_core.GetJobServiceClient().GetJobLog(job.UUID) logBytes, err := utils_core.GetJobServiceClient().GetJobLog(job.UUID)
@ -175,14 +178,14 @@ func (aj *AJAPI) getLog(id int64) {
id, httpErr.Code, httpErr.Message)) id, httpErr.Code, httpErr.Message))
return return
} }
aj.HandleInternalServerError(fmt.Sprintf("Failed to get job logs, uuid: %s, error: %v", job.UUID, err)) aj.SendInternalServerError(fmt.Errorf("Failed to get job logs, uuid: %s, error: %v", job.UUID, err))
return return
} }
aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes))) aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes)))
aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain") aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain")
_, err = aj.Ctx.ResponseWriter.Write(logBytes) _, err = aj.Ctx.ResponseWriter.Write(logBytes)
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("Failed to write job logs, uuid: %s, error: %v", job.UUID, err)) aj.SendInternalServerError(fmt.Errorf("Failed to write job logs, uuid: %s, error: %v", job.UUID, err))
} }
} }
@ -195,11 +198,11 @@ func (aj *AJAPI) submit(ajr *models.AdminJobReq) {
Kind: common_job.JobKindPeriodic, Kind: common_job.JobKindPeriodic,
}) })
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("failed to get admin jobs: %v", err)) aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err))
return return
} }
if len(jobs) != 0 { if len(jobs) != 0 {
aj.HandleStatusPreconditionFailed("Fail to set schedule for admin job as always had one, please delete it firstly then to re-schedule.") aj.SendPreconditionFailedError(errors.New("fail to set schedule for admin job as always had one, please delete it firstly then to re-schedule"))
return return
} }
} }
@ -210,7 +213,7 @@ func (aj *AJAPI) submit(ajr *models.AdminJobReq) {
Cron: ajr.CronString(), Cron: ajr.CronString(),
}) })
if err != nil { if err != nil {
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
ajr.ID = id ajr.ID = id
@ -224,14 +227,14 @@ func (aj *AJAPI) submit(ajr *models.AdminJobReq) {
log.Debugf("Failed to delete admin job, err: %v", err) log.Debugf("Failed to delete admin job, err: %v", err)
} }
if httpErr, ok := err.(*common_http.Error); ok && httpErr.Code == http.StatusConflict { if httpErr, ok := err.(*common_http.Error); ok && httpErr.Code == http.StatusConflict {
aj.HandleConflict(fmt.Sprintf("Conflict when triggering %s, please try again later.", ajr.Name)) aj.SendConflictError(fmt.Errorf("conflict when triggering %s, please try again later", ajr.Name))
return return
} }
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
if err := dao.SetAdminJobUUID(id, uuid); err != nil { if err := dao.SetAdminJobUUID(id, uuid); err != nil {
aj.HandleInternalServerError(fmt.Sprintf("%v", err)) aj.SendInternalServerError(err)
return return
} }
} }

View File

@ -17,14 +17,14 @@ package api
import ( import (
"net/http" "net/http"
yaml "github.com/ghodss/yaml" "errors"
"github.com/ghodss/yaml"
"github.com/goharbor/harbor/src/common/api" "github.com/goharbor/harbor/src/common/api"
"github.com/goharbor/harbor/src/common/security" "github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/filter" "github.com/goharbor/harbor/src/core/filter"
"github.com/goharbor/harbor/src/core/promgr" "github.com/goharbor/harbor/src/core/promgr"
"github.com/goharbor/harbor/src/core/utils"
) )
const ( const (
@ -54,55 +54,20 @@ func (b *BaseController) Prepare() {
ctx, err := filter.GetSecurityContext(b.Ctx.Request) ctx, err := filter.GetSecurityContext(b.Ctx.Request)
if err != nil { if err != nil {
log.Errorf("failed to get security context: %v", err) log.Errorf("failed to get security context: %v", err)
b.CustomAbort(http.StatusInternalServerError, "") b.SendInternalServerError(errors.New(""))
return
} }
b.SecurityCtx = ctx b.SecurityCtx = ctx
pm, err := filter.GetProjectManager(b.Ctx.Request) pm, err := filter.GetProjectManager(b.Ctx.Request)
if err != nil { if err != nil {
log.Errorf("failed to get project manager: %v", err) log.Errorf("failed to get project manager: %v", err)
b.CustomAbort(http.StatusInternalServerError, "") b.SendInternalServerError(errors.New(""))
return
} }
b.ProjectMgr = pm b.ProjectMgr = pm
} }
// RenderFormatedError renders errors with well formted style `{"error": "This is an error"}`
func (b *BaseController) RenderFormatedError(code int, err error) {
formatedErr := utils.WrapError(err)
log.Errorf("%s %s failed with error: %s", b.Ctx.Request.Method, b.Ctx.Request.URL.String(), formatedErr.Error())
b.RenderError(code, formatedErr.Error())
}
// SendUnAuthorizedError sends unauthorized error to the client.
func (b *BaseController) SendUnAuthorizedError(err error) {
b.RenderFormatedError(http.StatusUnauthorized, err)
}
// SendConflictError sends conflict error to the client.
func (b *BaseController) SendConflictError(err error) {
b.RenderFormatedError(http.StatusConflict, err)
}
// SendNotFoundError sends not found error to the client.
func (b *BaseController) SendNotFoundError(err error) {
b.RenderFormatedError(http.StatusNotFound, err)
}
// SendBadRequestError sends bad request error to the client.
func (b *BaseController) SendBadRequestError(err error) {
b.RenderFormatedError(http.StatusBadRequest, err)
}
// SendInternalServerError sends internal server error to the client.
func (b *BaseController) SendInternalServerError(err error) {
b.RenderFormatedError(http.StatusInternalServerError, err)
}
// SendForbiddenError sends forbidden error to the client.
func (b *BaseController) SendForbiddenError(err error) {
b.RenderFormatedError(http.StatusForbidden, err)
}
// WriteJSONData writes the JSON data to the client. // WriteJSONData writes the JSON data to the client.
func (b *BaseController) WriteJSONData(object interface{}) { func (b *BaseController) WriteJSONData(object interface{}) {
b.Data["json"] = object b.Data["json"] = object

View File

@ -61,7 +61,7 @@ func (cla *ChartLabelAPI) requireAccess(action rbac.Action) bool {
resource := rbac.NewProjectNamespace(cla.project.ProjectID).Resource(rbac.ResourceHelmChartVersionLabel) resource := rbac.NewProjectNamespace(cla.project.ProjectID).Resource(rbac.ResourceHelmChartVersionLabel)
if !cla.SecurityCtx.Can(action, resource) { if !cla.SecurityCtx.Can(action, resource) {
cla.HandleForbidden(cla.SecurityCtx.GetUsername()) cla.SendForbiddenError(errors.New(cla.SecurityCtx.GetUsername()))
return false return false
} }
@ -75,7 +75,10 @@ func (cla *ChartLabelAPI) MarkLabel() {
} }
l := &models.Label{} l := &models.Label{}
cla.DecodeJSONReq(l) if err := cla.DecodeJSONReq(l); err != nil {
cla.SendBadRequestError(err)
return
}
label, ok := cla.validate(l.ID, cla.project.ProjectID) label, ok := cla.validate(l.ID, cla.project.ProjectID)
if !ok { if !ok {

View File

@ -95,7 +95,7 @@ func (cra *ChartRepositoryAPI) requireAccess(action rbac.Action, subresource ...
if !cra.SecurityCtx.IsAuthenticated() { if !cra.SecurityCtx.IsAuthenticated() {
cra.SendUnAuthorizedError(errors.New("Unauthorized")) cra.SendUnAuthorizedError(errors.New("Unauthorized"))
} else { } else {
cra.HandleForbidden(cra.SecurityCtx.GetUsername()) cra.SendForbiddenError(errors.New(cra.SecurityCtx.GetUsername()))
} }
return false return false
@ -113,7 +113,7 @@ func (cra *ChartRepositoryAPI) GetHealthStatus() {
} }
if !cra.SecurityCtx.IsSysAdmin() { if !cra.SecurityCtx.IsSysAdmin() {
cra.HandleForbidden(cra.SecurityCtx.GetUsername()) cra.SendForbiddenError(errors.New(cra.SecurityCtx.GetUsername()))
return return
} }
@ -141,7 +141,7 @@ func (cra *ChartRepositoryAPI) GetIndex() {
} }
if !cra.SecurityCtx.IsSysAdmin() { if !cra.SecurityCtx.IsSysAdmin() {
cra.HandleForbidden(cra.SecurityCtx.GetUsername()) cra.SendForbiddenError(errors.New(cra.SecurityCtx.GetUsername()))
return return
} }

View File

@ -16,9 +16,9 @@ package api
import ( import (
"fmt" "fmt"
"net/http"
"strings" "strings"
"errors"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config" "github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/config/metadata" "github.com/goharbor/harbor/src/common/config/metadata"
@ -41,20 +41,20 @@ func (c *ConfigAPI) Prepare() {
c.BaseController.Prepare() c.BaseController.Prepare()
c.cfgManager = corecfg.GetCfgManager() c.cfgManager = corecfg.GetCfgManager()
if !c.SecurityCtx.IsAuthenticated() { if !c.SecurityCtx.IsAuthenticated() {
c.HandleUnauthorized() c.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
// Only internal container can access /api/internal/configurations // Only internal container can access /api/internal/configurations
if strings.EqualFold(c.Ctx.Request.RequestURI, "/api/internal/configurations") { if strings.EqualFold(c.Ctx.Request.RequestURI, "/api/internal/configurations") {
if _, ok := c.Ctx.Request.Context().Value(filter.SecurCtxKey).(*secret.SecurityContext); !ok { if _, ok := c.Ctx.Request.Context().Value(filter.SecurCtxKey).(*secret.SecurityContext); !ok {
c.HandleUnauthorized() c.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
} }
if !c.SecurityCtx.IsSysAdmin() && !c.SecurityCtx.IsSolutionUser() { if !c.SecurityCtx.IsSysAdmin() && !c.SecurityCtx.IsSolutionUser() {
c.HandleForbidden(c.SecurityCtx.GetUsername()) c.SendForbiddenError(errors.New(c.SecurityCtx.GetUsername()))
return return
} }
@ -71,7 +71,8 @@ func (c *ConfigAPI) Get() {
m, err := convertForGet(configs) m, err := convertForGet(configs)
if err != nil { if err != nil {
log.Errorf("failed to convert configurations: %v", err) log.Errorf("failed to convert configurations: %v", err)
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.SendInternalServerError(errors.New(""))
return
} }
c.Data["json"] = m c.Data["json"] = m
@ -89,25 +90,33 @@ func (c *ConfigAPI) GetInternalConfig() {
// Put updates configurations // Put updates configurations
func (c *ConfigAPI) Put() { func (c *ConfigAPI) Put() {
m := map[string]interface{}{} m := map[string]interface{}{}
c.DecodeJSONReq(&m) if err := c.DecodeJSONReq(&m); err != nil {
c.SendBadRequestError(err)
return
}
err := c.cfgManager.Load() err := c.cfgManager.Load()
if err != nil { if err != nil {
log.Errorf("failed to get configurations: %v", err) log.Errorf("failed to get configurations: %v", err)
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.SendInternalServerError(errors.New(""))
return
} }
isSysErr, err := c.validateCfg(m) isSysErr, err := c.validateCfg(m)
if err != nil { if err != nil {
if isSysErr { if isSysErr {
log.Errorf("failed to validate configurations: %v", err) log.Errorf("failed to validate configurations: %v", err)
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.SendInternalServerError(errors.New(""))
return
} }
c.CustomAbort(http.StatusBadRequest, err.Error()) c.SendBadRequestError(err)
return
} }
if err := c.cfgManager.UpdateConfig(m); err != nil { if err := c.cfgManager.UpdateConfig(m); err != nil {
log.Errorf("failed to upload configurations: %v", err) log.Errorf("failed to upload configurations: %v", err)
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) c.SendInternalServerError(errors.New(""))
return
} }
} }

View File

@ -15,8 +15,8 @@
package api package api
import ( import (
"errors"
"net" "net"
"net/http"
"strconv" "strconv"
"github.com/goharbor/harbor/src/common/utils/email" "github.com/goharbor/harbor/src/common/utils/email"
@ -37,12 +37,12 @@ type EmailAPI struct {
func (e *EmailAPI) Prepare() { func (e *EmailAPI) Prepare() {
e.BaseController.Prepare() e.BaseController.Prepare()
if !e.SecurityCtx.IsAuthenticated() { if !e.SecurityCtx.IsAuthenticated() {
e.HandleUnauthorized() e.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if !e.SecurityCtx.IsSysAdmin() { if !e.SecurityCtx.IsSysAdmin() {
e.HandleForbidden(e.SecurityCtx.GetUsername()) e.SendForbiddenError(errors.New(e.SecurityCtx.GetUsername()))
return return
} }
} }
@ -57,8 +57,8 @@ func (e *EmailAPI) Ping() {
cfg, err := config.Email() cfg, err := config.Email()
if err != nil { if err != nil {
log.Errorf("failed to get email configurations: %v", err) log.Errorf("failed to get email configurations: %v", err)
e.CustomAbort(http.StatusInternalServerError, e.SendInternalServerError(err)
http.StatusText(http.StatusInternalServerError)) return
} }
host = cfg.Host host = cfg.Host
port = cfg.Port port = cfg.Port
@ -77,18 +77,22 @@ func (e *EmailAPI) Ping() {
Identity string `json:"email_identity"` Identity string `json:"email_identity"`
Insecure bool `json:"email_insecure"` Insecure bool `json:"email_insecure"`
}{} }{}
e.DecodeJSONReq(&settings) if err := e.DecodeJSONReq(&settings); err != nil {
e.SendBadRequestError(err)
return
}
if len(settings.Host) == 0 || settings.Port == nil { if len(settings.Host) == 0 || settings.Port == nil {
e.CustomAbort(http.StatusBadRequest, "empty host or port") e.SendBadRequestError(errors.New("empty host or port"))
return
} }
if settings.Password == nil { if settings.Password == nil {
cfg, err := config.Email() cfg, err := config.Email()
if err != nil { if err != nil {
log.Errorf("failed to get email configurations: %v", err) log.Errorf("failed to get email configurations: %v", err)
e.CustomAbort(http.StatusInternalServerError, e.SendInternalServerError(err)
http.StatusText(http.StatusInternalServerError)) return
} }
settings.Password = &cfg.Password settings.Password = &cfg.Password
@ -108,7 +112,7 @@ func (e *EmailAPI) Ping() {
password, pingEmailTimeout, ssl, insecure); err != nil { password, pingEmailTimeout, ssl, insecure); err != nil {
log.Errorf("failed to ping email server: %v", err) log.Errorf("failed to ping email server: %v", err)
// do not return any detail information of the error, or may cause SSRF security issue #3755 // do not return any detail information of the error, or may cause SSRF security issue #3755
e.RenderError(http.StatusBadRequest, "failed to ping email server") e.SendBadRequestError(errors.New("failed to ping email server"))
return return
} }
} }

View File

@ -15,7 +15,7 @@
package api package api
import ( import (
"net/http" "errors"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
@ -32,11 +32,11 @@ type InternalAPI struct {
func (ia *InternalAPI) Prepare() { func (ia *InternalAPI) Prepare() {
ia.BaseController.Prepare() ia.BaseController.Prepare()
if !ia.SecurityCtx.IsAuthenticated() { if !ia.SecurityCtx.IsAuthenticated() {
ia.HandleUnauthorized() ia.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if !ia.SecurityCtx.IsSysAdmin() { if !ia.SecurityCtx.IsSysAdmin() {
ia.HandleForbidden(ia.SecurityCtx.GetUsername()) ia.SendForbiddenError(errors.New(ia.SecurityCtx.GetUsername()))
return return
} }
} }
@ -45,7 +45,7 @@ func (ia *InternalAPI) Prepare() {
func (ia *InternalAPI) SyncRegistry() { func (ia *InternalAPI) SyncRegistry() {
err := SyncRegistry(ia.ProjectMgr) err := SyncRegistry(ia.ProjectMgr)
if err != nil { if err != nil {
ia.HandleInternalServerError(err.Error()) ia.SendInternalServerError(err)
return return
} }
} }
@ -54,7 +54,8 @@ func (ia *InternalAPI) SyncRegistry() {
func (ia *InternalAPI) RenameAdmin() { func (ia *InternalAPI) RenameAdmin() {
if !dao.IsSuperUser(ia.SecurityCtx.GetUsername()) { if !dao.IsSuperUser(ia.SecurityCtx.GetUsername()) {
log.Errorf("User %s is not super user, not allow to rename admin.", ia.SecurityCtx.GetUsername()) log.Errorf("User %s is not super user, not allow to rename admin.", ia.SecurityCtx.GetUsername())
ia.CustomAbort(http.StatusForbidden, "") ia.SendForbiddenError(errors.New(ia.SecurityCtx.GetUsername()))
return
} }
newName := common.NewHarborAdminName newName := common.NewHarborAdminName
if err := dao.ChangeUserProfile(models.User{ if err := dao.ChangeUserProfile(models.User{
@ -62,7 +63,8 @@ func (ia *InternalAPI) RenameAdmin() {
Username: newName, Username: newName,
}, "username"); err != nil { }, "username"); err != nil {
log.Errorf("Failed to change admin's username, error: %v", err) log.Errorf("Failed to change admin's username, error: %v", err)
ia.CustomAbort(http.StatusInternalServerError, "Failed to rename admin user.") ia.SendInternalServerError(errors.New("failed to rename admin user"))
return
} }
log.Debugf("The super user has been renamed to: %s", newName) log.Debugf("The super user has been renamed to: %s", newName)
ia.DestroySession() ia.DestroySession()

View File

@ -15,6 +15,7 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -44,25 +45,25 @@ func (l *LabelAPI) Prepare() {
// POST, PUT, DELETE need login first // POST, PUT, DELETE need login first
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if method == http.MethodPut || method == http.MethodDelete { if method == http.MethodPut || method == http.MethodDelete {
id, err := l.GetInt64FromPath(":id") id, err := l.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
l.HandleBadRequest("invalid label ID") l.SendBadRequestError(errors.New("invalid lable ID"))
return return
} }
label, err := dao.GetLabel(id) label, err := dao.GetLabel(id)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to get label %d: %v", id, err)) l.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", id, err))
return return
} }
if label == nil || label.Deleted { if label == nil || label.Deleted {
l.HandleNotFound(fmt.Sprintf("label %d not found", id)) l.SendNotFoundError(fmt.Errorf("label %d not found", id))
return return
} }
@ -86,9 +87,9 @@ func (l *LabelAPI) requireAccess(label *models.Label, action rbac.Action, subres
if !hasPermission { if !hasPermission {
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.SendUnAuthorizedError(errors.New("UnAuthorized"))
} else { } else {
l.HandleForbidden(l.SecurityCtx.GetUsername()) l.SendForbiddenError(errors.New(l.SecurityCtx.GetUsername()))
} }
return false return false
} }
@ -99,7 +100,12 @@ func (l *LabelAPI) requireAccess(label *models.Label, action rbac.Action, subres
// Post creates a label // Post creates a label
func (l *LabelAPI) Post() { func (l *LabelAPI) Post() {
label := &models.Label{} label := &models.Label{}
l.DecodeJSONReqAndValidate(label) isValid, err := l.DecodeJSONReqAndValidate(label)
if !isValid {
l.SendBadRequestError(err)
return
}
label.Level = common.LabelLevelUser label.Level = common.LabelLevelUser
switch label.Scope { switch label.Scope {
@ -108,12 +114,12 @@ func (l *LabelAPI) Post() {
case common.LabelScopeProject: case common.LabelScopeProject:
exist, err := l.ProjectMgr.Exists(label.ProjectID) exist, err := l.ProjectMgr.Exists(label.ProjectID)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %d: %v", l.SendInternalServerError(fmt.Errorf("failed to check the existence of project %d: %v",
label.ProjectID, err)) label.ProjectID, err))
return return
} }
if !exist { if !exist {
l.HandleNotFound(fmt.Sprintf("project %d not found", label.ProjectID)) l.SendNotFoundError(fmt.Errorf("project %d not found", label.ProjectID))
return return
} }
} }
@ -129,17 +135,17 @@ func (l *LabelAPI) Post() {
ProjectID: label.ProjectID, ProjectID: label.ProjectID,
}) })
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to list labels: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
return return
} }
if len(labels) > 0 { if len(labels) > 0 {
l.HandleConflict() l.SendConflictError(errors.New("conflict label"))
return return
} }
id, err := dao.AddLabel(label) id, err := dao.AddLabel(label)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to create label: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to create label: %v", err))
return return
} }
@ -150,18 +156,18 @@ func (l *LabelAPI) Post() {
func (l *LabelAPI) Get() { func (l *LabelAPI) Get() {
id, err := l.GetInt64FromPath(":id") id, err := l.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
l.HandleBadRequest(fmt.Sprintf("invalid label ID: %s", l.GetStringFromPath(":id"))) l.SendBadRequestError(fmt.Errorf("invalid label ID: %s", l.GetStringFromPath(":id")))
return return
} }
label, err := dao.GetLabel(id) label, err := dao.GetLabel(id)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to get label %d: %v", id, err)) l.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", id, err))
return return
} }
if label == nil || label.Deleted { if label == nil || label.Deleted {
l.HandleNotFound(fmt.Sprintf("label %d not found", id)) l.SendNotFoundError(fmt.Errorf("label %d not found", id))
return return
} }
@ -183,7 +189,7 @@ func (l *LabelAPI) List() {
scope := l.GetString("scope") scope := l.GetString("scope")
if scope != common.LabelScopeGlobal && scope != common.LabelScopeProject { if scope != common.LabelScopeGlobal && scope != common.LabelScopeProject {
l.HandleBadRequest(fmt.Sprintf("invalid scope: %s", scope)) l.SendBadRequestError(fmt.Errorf("invalid scope: %s", scope))
return return
} }
query.Scope = scope query.Scope = scope
@ -191,22 +197,22 @@ func (l *LabelAPI) List() {
if scope == common.LabelScopeProject { if scope == common.LabelScopeProject {
projectIDStr := l.GetString("project_id") projectIDStr := l.GetString("project_id")
if len(projectIDStr) == 0 { if len(projectIDStr) == 0 {
l.HandleBadRequest("project_id is required") l.SendBadRequestError(errors.New("project_id is required"))
return return
} }
projectID, err := strconv.ParseInt(projectIDStr, 10, 64) projectID, err := strconv.ParseInt(projectIDStr, 10, 64)
if err != nil || projectID <= 0 { if err != nil || projectID <= 0 {
l.HandleBadRequest(fmt.Sprintf("invalid project_id: %s", projectIDStr)) l.SendBadRequestError(fmt.Errorf("invalid project_id: %s", projectIDStr))
return return
} }
resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceLabel) resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceLabel)
if !l.SecurityCtx.Can(rbac.ActionList, resource) { if !l.SecurityCtx.Can(rbac.ActionList, resource) {
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
l.HandleForbidden(l.SecurityCtx.GetUsername()) l.SendForbiddenError(errors.New(l.SecurityCtx.GetUsername()))
return return
} }
query.ProjectID = projectID query.ProjectID = projectID
@ -214,15 +220,19 @@ func (l *LabelAPI) List() {
total, err := dao.GetTotalOfLabels(query) total, err := dao.GetTotalOfLabels(query)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to get total count of labels: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to get total count of labels: %v", err))
return return
} }
query.Page, query.Size = l.GetPaginationParams() query.Page, query.Size, err = l.GetPaginationParams()
if err != nil {
l.SendBadRequestError(err)
return
}
labels, err := dao.ListLabels(query) labels, err := dao.ListLabels(query)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to list labels: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
return return
} }
@ -238,7 +248,10 @@ func (l *LabelAPI) Put() {
} }
label := &models.Label{} label := &models.Label{}
l.DecodeJSONReq(label) if err := l.DecodeJSONReq(label); err != nil {
l.SendBadRequestError(err)
return
}
oldName := l.label.Name oldName := l.label.Name
@ -247,7 +260,13 @@ func (l *LabelAPI) Put() {
l.label.Description = label.Description l.label.Description = label.Description
l.label.Color = label.Color l.label.Color = label.Color
l.Validate(l.label) isValidate, err := l.Validate(l.label)
if !isValidate {
if err != nil {
l.SendBadRequestError(err)
return
}
}
if l.label.Name != oldName { if l.label.Name != oldName {
labels, err := dao.ListLabels(&models.LabelQuery{ labels, err := dao.ListLabels(&models.LabelQuery{
@ -257,17 +276,17 @@ func (l *LabelAPI) Put() {
ProjectID: l.label.ProjectID, ProjectID: l.label.ProjectID,
}) })
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to list labels: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
return return
} }
if len(labels) > 0 { if len(labels) > 0 {
l.HandleConflict() l.SendConflictError(errors.New("conflict label"))
return return
} }
} }
if err := dao.UpdateLabel(l.label); err != nil { if err := dao.UpdateLabel(l.label); err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to update label %d: %v", l.label.ID, err)) l.SendInternalServerError(fmt.Errorf("failed to update label %d: %v", l.label.ID, err))
return return
} }
@ -281,11 +300,11 @@ func (l *LabelAPI) Delete() {
id := l.label.ID id := l.label.ID
if err := dao.DeleteResourceLabelByLabel(id); err != nil { if err := dao.DeleteResourceLabelByLabel(id); err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to delete resource label mappings of label %d: %v", id, err)) l.SendInternalServerError(fmt.Errorf("failed to delete resource label mappings of label %d: %v", id, err))
return return
} }
if err := dao.DeleteLabel(id); err != nil { if err := dao.DeleteLabel(id); err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to delete label %d: %v", id, err)) l.SendInternalServerError(fmt.Errorf("failed to delete label %d: %v", id, err))
return return
} }
} }
@ -294,18 +313,18 @@ func (l *LabelAPI) Delete() {
func (l *LabelAPI) ListResources() { func (l *LabelAPI) ListResources() {
id, err := l.GetInt64FromPath(":id") id, err := l.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
l.HandleBadRequest("invalid label ID") l.SendBadRequestError(errors.New("invalid label ID"))
return return
} }
label, err := dao.GetLabel(id) label, err := dao.GetLabel(id)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to get label %d: %v", id, err)) l.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", id, err))
return return
} }
if label == nil || label.Deleted { if label == nil || label.Deleted {
l.HandleNotFound(fmt.Sprintf("label %d not found", id)) l.SendNotFoundError(fmt.Errorf("label %d not found", id))
return return
} }
@ -315,7 +334,7 @@ func (l *LabelAPI) ListResources() {
result, err := core.GlobalController.GetPolicies(rep_models.QueryParameter{}) result, err := core.GlobalController.GetPolicies(rep_models.QueryParameter{})
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("failed to get policies: %v", err)) l.SendInternalServerError(fmt.Errorf("failed to get policies: %v", err))
return return
} }
policies := []*rep_models.ReplicationPolicy{} policies := []*rep_models.ReplicationPolicy{}

View File

@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/auth" "github.com/goharbor/harbor/src/core/auth"
"errors"
goldap "gopkg.in/ldap.v2" goldap "gopkg.in/ldap.v2"
) )
@ -43,17 +44,17 @@ const (
func (l *LdapAPI) Prepare() { func (l *LdapAPI) Prepare() {
l.BaseController.Prepare() l.BaseController.Prepare()
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
if !l.SecurityCtx.IsSysAdmin() { if !l.SecurityCtx.IsSysAdmin() {
l.HandleForbidden(l.SecurityCtx.GetUsername()) l.SendForbiddenError(errors.New(l.SecurityCtx.GetUsername()))
return return
} }
ldapCfg, err := ldapUtils.LoadSystemLdapConfig() ldapCfg, err := ldapUtils.LoadSystemLdapConfig()
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("Can't load system configuration, error: %v", err)) l.SendInternalServerError(fmt.Errorf("Can't load system configuration, error: %v", err))
return return
} }
l.ldapConfig = ldapCfg l.ldapConfig = ldapCfg
@ -73,12 +74,16 @@ func (l *LdapAPI) Ping() {
ldapSession := *l.ldapConfig ldapSession := *l.ldapConfig
err = ldapSession.ConnectionTest() err = ldapSession.ConnectionTest()
} else { } else {
l.DecodeJSONReqAndValidate(&ldapConfs) isValid, err := l.DecodeJSONReqAndValidate(&ldapConfs)
if !isValid {
l.SendBadRequestError(err)
return
}
err = ldapUtils.ConnectionTestWithConfig(ldapConfs) err = ldapUtils.ConnectionTestWithConfig(ldapConfs)
} }
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP connect fail, error: %v", err)) l.SendInternalServerError(fmt.Errorf("LDAP connect fail, error: %v", err))
return return
} }
} }
@ -89,7 +94,7 @@ func (l *LdapAPI) Search() {
var ldapUsers []models.LdapUser var ldapUsers []models.LdapUser
ldapSession := *l.ldapConfig ldapSession := *l.ldapConfig
if err = ldapSession.Open(); err != nil { if err = ldapSession.Open(); err != nil {
l.HandleInternalServerError(fmt.Sprintf("Can't Open LDAP session, error: %v", err)) l.SendInternalServerError(fmt.Errorf("can't Open LDAP session, error: %v", err))
return return
} }
defer ldapSession.Close() defer ldapSession.Close()
@ -99,7 +104,7 @@ func (l *LdapAPI) Search() {
ldapUsers, err = ldapSession.SearchUser(searchName) ldapUsers, err = ldapSession.SearchUser(searchName)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP search fail, error: %v", err)) l.SendInternalServerError(fmt.Errorf("LDAP search fail, error: %v", err))
return return
} }
@ -113,18 +118,22 @@ func (l *LdapAPI) ImportUser() {
var ldapImportUsers models.LdapImportUser var ldapImportUsers models.LdapImportUser
var ldapFailedImportUsers []models.LdapFailedImportUser var ldapFailedImportUsers []models.LdapFailedImportUser
l.DecodeJSONReqAndValidate(&ldapImportUsers) isValid, err := l.DecodeJSONReqAndValidate(&ldapImportUsers)
if !isValid {
l.SendBadRequestError(err)
return
}
ldapFailedImportUsers, err := importUsers(ldapImportUsers.LdapUIDList, l.ldapConfig) ldapFailedImportUsers, err = importUsers(ldapImportUsers.LdapUIDList, l.ldapConfig)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP import user fail, error: %v", err)) l.SendInternalServerError(fmt.Errorf("LDAP import user fail, error: %v", err))
return return
} }
if len(ldapFailedImportUsers) > 0 { if len(ldapFailedImportUsers) > 0 {
// Some user require json format response. // Some user require json format response.
l.HandleNotFound("") l.SendNotFoundError(errors.New("ldap user is not found"))
l.Data["json"] = ldapFailedImportUsers l.Data["json"] = ldapFailedImportUsers
l.ServeJSON() l.ServeJSON()
return return
@ -206,23 +215,23 @@ func (l *LdapAPI) SearchGroup() {
if len(searchName) > 0 { if len(searchName) > 0 {
ldapGroups, err = ldapSession.SearchGroupByName(searchName) ldapGroups, err = ldapSession.SearchGroupByName(searchName)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf("Can't search LDAP group by name, error: %v", err)) l.SendInternalServerError(fmt.Errorf("can't search LDAP group by name, error: %v", err))
return return
} }
} else if len(groupDN) > 0 { } else if len(groupDN) > 0 {
if _, err := goldap.ParseDN(groupDN); err != nil { if _, err := goldap.ParseDN(groupDN); err != nil {
l.HandleBadRequest(fmt.Sprintf("Invalid DN: %v", err)) l.SendBadRequestError(fmt.Errorf("invalid DN: %v", err))
return return
} }
ldapGroups, err = ldapSession.SearchGroupByDN(groupDN) ldapGroups, err = ldapSession.SearchGroupByDN(groupDN)
if err != nil { if err != nil {
// OpenLDAP usually return an error if DN is not found // OpenLDAP usually return an error if DN is not found
l.HandleNotFound(fmt.Sprintf("Search LDAP group fail, error: %v", err)) l.SendNotFoundError(fmt.Errorf("search LDAP group fail, error: %v", err))
return return
} }
} }
if len(ldapGroups) == 0 { if len(ldapGroups) == 0 {
l.HandleNotFound("No ldap group found") l.SendNotFoundError(errors.New("No ldap group found"))
return return
} }
l.Data["json"] = ldapGroups l.Data["json"] = ldapGroups

View File

@ -17,6 +17,7 @@ package api
import ( import (
"fmt" "fmt"
"errors"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
@ -33,7 +34,7 @@ type LogAPI struct {
func (l *LogAPI) Prepare() { func (l *LogAPI) Prepare() {
l.BaseController.Prepare() l.BaseController.Prepare()
if !l.SecurityCtx.IsAuthenticated() { if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized() l.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
l.username = l.SecurityCtx.GetUsername() l.username = l.SecurityCtx.GetUsername()
@ -42,7 +43,11 @@ func (l *LogAPI) Prepare() {
// Get returns the recent logs according to parameters // Get returns the recent logs according to parameters
func (l *LogAPI) Get() { func (l *LogAPI) Get() {
page, size := l.GetPaginationParams() page, size, err := l.GetPaginationParams()
if err != nil {
l.SendBadRequestError(err)
return
}
query := &models.LogQueryParam{ query := &models.LogQueryParam{
Username: l.GetString("username"), Username: l.GetString("username"),
Repository: l.GetString("repository"), Repository: l.GetString("repository"),
@ -58,7 +63,7 @@ func (l *LogAPI) Get() {
if len(timestamp) > 0 { if len(timestamp) > 0 {
t, err := utils.ParseTimeStamp(timestamp) t, err := utils.ParseTimeStamp(timestamp)
if err != nil { if err != nil {
l.HandleBadRequest(fmt.Sprintf("invalid begin_timestamp: %s", timestamp)) l.SendBadRequestError(fmt.Errorf("invalid begin_timestamp: %s", timestamp))
return return
} }
query.BeginTime = t query.BeginTime = t
@ -68,7 +73,7 @@ func (l *LogAPI) Get() {
if len(timestamp) > 0 { if len(timestamp) > 0 {
t, err := utils.ParseTimeStamp(timestamp) t, err := utils.ParseTimeStamp(timestamp)
if err != nil { if err != nil {
l.HandleBadRequest(fmt.Sprintf("invalid end_timestamp: %s", timestamp)) l.SendBadRequestError(fmt.Errorf("invalid end_timestamp: %s", timestamp))
return return
} }
query.EndTime = t query.EndTime = t
@ -77,7 +82,7 @@ func (l *LogAPI) Get() {
if !l.isSysAdmin { if !l.isSysAdmin {
projects, err := l.SecurityCtx.GetMyProjects() projects, err := l.SecurityCtx.GetMyProjects()
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf( l.SendInternalServerError(fmt.Errorf(
"failed to get projects of user %s: %v", l.username, err)) "failed to get projects of user %s: %v", l.username, err))
return return
} }
@ -98,14 +103,14 @@ func (l *LogAPI) Get() {
total, err := dao.GetTotalOfAccessLogs(query) total, err := dao.GetTotalOfAccessLogs(query)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf( l.SendInternalServerError(fmt.Errorf(
"failed to get total of access logs: %v", err)) "failed to get total of access logs: %v", err))
return return
} }
logs, err := dao.GetAccessLogs(query) logs, err := dao.GetAccessLogs(query)
if err != nil { if err != nil {
l.HandleInternalServerError(fmt.Sprintf( l.SendInternalServerError(fmt.Errorf(
"failed to get access logs: %v", err)) "failed to get access logs: %v", err))
return return
} }

View File

@ -21,6 +21,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"errors"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
@ -56,7 +57,7 @@ func (m *MetadataAPI) Prepare() {
} else { } else {
text += fmt.Sprintf("%d", id) text += fmt.Sprintf("%d", id)
} }
m.HandleBadRequest(text) m.SendBadRequestError(errors.New(text))
return return
} }
@ -67,7 +68,7 @@ func (m *MetadataAPI) Prepare() {
} }
if project == nil { if project == nil {
m.HandleNotFound(fmt.Sprintf("project %d not found", id)) m.SendNotFoundError(fmt.Errorf("project %d not found", id))
return return
} }
@ -78,11 +79,11 @@ func (m *MetadataAPI) Prepare() {
m.name = name m.name = name
metas, err := m.metaMgr.Get(project.ProjectID, name) metas, err := m.metaMgr.Get(project.ProjectID, name)
if err != nil { if err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to get metadata of project %d: %v", project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to get metadata of project %d: %v", project.ProjectID, err))
return return
} }
if len(metas) == 0 { if len(metas) == 0 {
m.HandleNotFound(fmt.Sprintf("metadata %s of project %d not found", name, project.ProjectID)) m.SendNotFoundError(fmt.Errorf("metadata %s of project %d not found", name, project.ProjectID))
return return
} }
} }
@ -93,9 +94,9 @@ func (m *MetadataAPI) requireAccess(action rbac.Action) bool {
if !m.SecurityCtx.Can(action, resource) { if !m.SecurityCtx.Can(action, resource) {
if !m.SecurityCtx.IsAuthenticated() { if !m.SecurityCtx.IsAuthenticated() {
m.HandleUnauthorized() m.SendUnAuthorizedError(errors.New("Unauthorized"))
} else { } else {
m.HandleForbidden(m.SecurityCtx.GetUsername()) m.SendForbiddenError(errors.New(m.SecurityCtx.GetUsername()))
} }
return false return false
} }
@ -118,7 +119,7 @@ func (m *MetadataAPI) Get() {
} }
if err != nil { if err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to get metadata %s of project %d: %v", m.name, m.project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to get metadata %s of project %d: %v", m.name, m.project.ProjectID, err))
return return
} }
m.Data["json"] = metas m.Data["json"] = metas
@ -132,33 +133,36 @@ func (m *MetadataAPI) Post() {
} }
var metas map[string]string var metas map[string]string
m.DecodeJSONReq(&metas) if err := m.DecodeJSONReq(&metas); err != nil {
m.SendBadRequestError(err)
return
}
ms, err := validateProjectMetadata(metas) ms, err := validateProjectMetadata(metas)
if err != nil { if err != nil {
m.HandleBadRequest(err.Error()) m.SendBadRequestError(err)
return return
} }
if len(ms) != 1 { if len(ms) != 1 {
m.HandleBadRequest("invalid request: has no valid key/value pairs or has more than one valid key/value pairs") m.SendBadRequestError(errors.New("invalid request: has no valid key/value pairs or has more than one valid key/value pairs"))
return return
} }
keys := reflect.ValueOf(ms).MapKeys() keys := reflect.ValueOf(ms).MapKeys()
mts, err := m.metaMgr.Get(m.project.ProjectID, keys[0].String()) mts, err := m.metaMgr.Get(m.project.ProjectID, keys[0].String())
if err != nil { if err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to get metadata for project %d: %v", m.project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to get metadata for project %d: %v", m.project.ProjectID, err))
return return
} }
if len(mts) != 0 { if len(mts) != 0 {
m.HandleConflict() m.SendConflictError(errors.New("conflict metadata"))
return return
} }
if err := m.metaMgr.Add(m.project.ProjectID, ms); err != nil { if err := m.metaMgr.Add(m.project.ProjectID, ms); err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to create metadata for project %d: %v", m.project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to create metadata for project %d: %v", m.project.ProjectID, err))
return return
} }
@ -172,11 +176,14 @@ func (m *MetadataAPI) Put() {
} }
var metas map[string]string var metas map[string]string
m.DecodeJSONReq(&metas) if err := m.DecodeJSONReq(&metas); err != nil {
m.SendBadRequestError(err)
return
}
meta, exist := metas[m.name] meta, exist := metas[m.name]
if !exist { if !exist {
m.HandleBadRequest(fmt.Sprintf("must contains key %s", m.name)) m.SendBadRequestError(fmt.Errorf("must contains key %s", m.name))
return return
} }
@ -184,14 +191,14 @@ func (m *MetadataAPI) Put() {
m.name: meta, m.name: meta,
}) })
if err != nil { if err != nil {
m.HandleBadRequest(err.Error()) m.SendBadRequestError(err)
return return
} }
if err := m.metaMgr.Update(m.project.ProjectID, map[string]string{ if err := m.metaMgr.Update(m.project.ProjectID, map[string]string{
m.name: ms[m.name], m.name: ms[m.name],
}); err != nil { }); err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to update metadata %s of project %d: %v", m.name, m.project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to update metadata %s of project %d: %v", m.name, m.project.ProjectID, err))
return return
} }
} }
@ -203,7 +210,7 @@ func (m *MetadataAPI) Delete() {
} }
if err := m.metaMgr.Delete(m.project.ProjectID, m.name); err != nil { if err := m.metaMgr.Delete(m.project.ProjectID, m.name); err != nil {
m.HandleInternalServerError(fmt.Sprintf("failed to delete metadata %s of project %d: %v", m.name, m.project.ProjectID, err)) m.SendInternalServerError(fmt.Errorf("failed to delete metadata %s of project %d: %v", m.name, m.project.ProjectID, err))
return return
} }
} }

View File

@ -28,6 +28,7 @@ import (
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
"errors"
"strconv" "strconv"
"time" "time"
) )
@ -59,7 +60,7 @@ func (p *ProjectAPI) Prepare() {
} else { } else {
text += fmt.Sprintf("%d", id) text += fmt.Sprintf("%d", id)
} }
p.HandleBadRequest(text) p.SendBadRequestError(errors.New(text))
return return
} }
@ -70,7 +71,7 @@ func (p *ProjectAPI) Prepare() {
} }
if project == nil { if project == nil {
p.HandleNotFound(fmt.Sprintf("project %d not found", id)) p.SendNotFoundError(fmt.Errorf("project %d not found", id))
return return
} }
@ -86,9 +87,10 @@ func (p *ProjectAPI) requireAccess(action rbac.Action, subresource ...rbac.Resou
if !p.SecurityCtx.Can(action, resource) { if !p.SecurityCtx.Can(action, resource) {
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.SendUnAuthorizedError(errors.New("Unauthorized"))
} else { } else {
p.HandleForbidden(p.SecurityCtx.GetUsername()) p.SendForbiddenError(errors.New(p.SecurityCtx.GetUsername()))
} }
return false return false
@ -100,7 +102,7 @@ func (p *ProjectAPI) requireAccess(action rbac.Action, subresource ...rbac.Resou
// Post ... // Post ...
func (p *ProjectAPI) Post() { func (p *ProjectAPI) Post() {
if !p.SecurityCtx.IsAuthenticated() { if !p.SecurityCtx.IsAuthenticated() {
p.HandleUnauthorized() p.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
var onlyAdmin bool var onlyAdmin bool
@ -111,21 +113,25 @@ func (p *ProjectAPI) Post() {
onlyAdmin, err = config.OnlyAdminCreateProject() onlyAdmin, err = config.OnlyAdminCreateProject()
if err != nil { if err != nil {
log.Errorf("failed to determine whether only admin can create projects: %v", err) log.Errorf("failed to determine whether only admin can create projects: %v", err)
p.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) p.SendInternalServerError(fmt.Errorf("failed to determine whether only admin can create projects: %v", err))
return
} }
} }
if onlyAdmin && !p.SecurityCtx.IsSysAdmin() { if onlyAdmin && !p.SecurityCtx.IsSysAdmin() {
log.Errorf("Only sys admin can create project") log.Errorf("Only sys admin can create project")
p.RenderError(http.StatusForbidden, "Only system admin can create project") p.SendForbiddenError(errors.New("Only system admin can create project"))
return return
} }
var pro *models.ProjectRequest var pro *models.ProjectRequest
p.DecodeJSONReq(&pro) if err := p.DecodeJSONReq(&pro); err != nil {
p.SendBadRequestError(err)
return
}
err = validateProjectReq(pro) err = validateProjectReq(pro)
if err != nil { if err != nil {
log.Errorf("Invalid project request, error: %v", err) log.Errorf("Invalid project request, error: %v", err)
p.RenderError(http.StatusBadRequest, fmt.Sprintf("invalid request: %v", err)) p.SendBadRequestError(fmt.Errorf("invalid request: %v", err))
return return
} }
@ -136,7 +142,7 @@ func (p *ProjectAPI) Post() {
return return
} }
if exist { if exist {
p.RenderError(http.StatusConflict, "") p.SendConflictError(errors.New("conflict project"))
return return
} }
@ -161,7 +167,7 @@ func (p *ProjectAPI) Post() {
if err != nil { if err != nil {
if err == errutil.ErrDupProject { if err == errutil.ErrDupProject {
log.Debugf("conflict %s", pro.Name) log.Debugf("conflict %s", pro.Name)
p.RenderError(http.StatusConflict, "") p.SendConflictError(fmt.Errorf("conflict %s", pro.Name))
} else { } else {
p.ParseAndHandleError("failed to add project", err) p.ParseAndHandleError("failed to add project", err)
} }
@ -189,7 +195,7 @@ func (p *ProjectAPI) Post() {
func (p *ProjectAPI) Head() { func (p *ProjectAPI) Head() {
name := p.GetString("project_name") name := p.GetString("project_name")
if len(name) == 0 { if len(name) == 0 {
p.HandleBadRequest("project_name is needed") p.SendBadRequestError(errors.New("project_name is needed"))
return return
} }
@ -200,7 +206,7 @@ func (p *ProjectAPI) Head() {
} }
if project == nil { if project == nil {
p.HandleNotFound(fmt.Sprintf("project %s not found", name)) p.SendNotFoundError(fmt.Errorf("project %s not found", name))
return return
} }
} }
@ -225,12 +231,13 @@ func (p *ProjectAPI) Delete() {
result, err := p.deletable(p.project.ProjectID) result, err := p.deletable(p.project.ProjectID)
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf( p.SendInternalServerError(fmt.Errorf(
"failed to check the deletable of project %d: %v", p.project.ProjectID, err)) "failed to check the deletable of project %d: %v", p.project.ProjectID, err))
return return
} }
if !result.Deletable { if !result.Deletable {
p.CustomAbort(http.StatusPreconditionFailed, result.Message) p.SendPreconditionFailedError(errors.New(result.Message))
return
} }
if err = p.ProjectMgr.Delete(p.project.ProjectID); err != nil { if err = p.ProjectMgr.Delete(p.project.ProjectID); err != nil {
@ -260,7 +267,7 @@ func (p *ProjectAPI) Deletable() {
result, err := p.deletable(p.project.ProjectID) result, err := p.deletable(p.project.ProjectID)
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf( p.SendInternalServerError(fmt.Errorf(
"failed to check the deletable of project %d: %v", p.project.ProjectID, err)) "failed to check the deletable of project %d: %v", p.project.ProjectID, err))
return return
} }
@ -319,7 +326,11 @@ func (p *ProjectAPI) deletable(projectID int64) (*deletableResp, error) {
// List ... // List ...
func (p *ProjectAPI) List() { func (p *ProjectAPI) List() {
// query strings // query strings
page, size := p.GetPaginationParams() page, size, err := p.GetPaginationParams()
if err != nil {
p.SendBadRequestError(err)
return
}
query := &models.ProjectQueryParam{ query := &models.ProjectQueryParam{
Name: p.GetString("name"), Name: p.GetString("name"),
Owner: p.GetString("owner"), Owner: p.GetString("owner"),
@ -333,7 +344,7 @@ func (p *ProjectAPI) List() {
if len(public) > 0 { if len(public) > 0 {
pub, err := strconv.ParseBool(public) pub, err := strconv.ParseBool(public)
if err != nil { if err != nil {
p.HandleBadRequest(fmt.Sprintf("invalid public: %s", public)) p.SendBadRequestError(fmt.Errorf("invalid public: %s", public))
return return
} }
query.Public = &pub query.Public = &pub
@ -346,7 +357,7 @@ func (p *ProjectAPI) List() {
// not login, only get public projects // not login, only get public projects
pros, err := p.ProjectMgr.GetPublic() pros, err := p.ProjectMgr.GetPublic()
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get public projects: %v", err)) p.SendInternalServerError(fmt.Errorf("failed to get public projects: %v", err))
return return
} }
projects = []*models.Project{} projects = []*models.Project{}
@ -358,13 +369,13 @@ func (p *ProjectAPI) List() {
// projects that the user is member of // projects that the user is member of
pros, err := p.ProjectMgr.GetPublic() pros, err := p.ProjectMgr.GetPublic()
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to get public projects: %v", err)) p.SendInternalServerError(fmt.Errorf("failed to get public projects: %v", err))
return return
} }
projects = append(projects, pros...) projects = append(projects, pros...)
mps, err := p.SecurityCtx.GetMyProjects() mps, err := p.SecurityCtx.GetMyProjects()
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf("failed to list projects: %v", err)) p.SendInternalServerError(fmt.Errorf("failed to list projects: %v", err))
return return
} }
projects = append(projects, mps...) projects = append(projects, mps...)
@ -414,7 +425,8 @@ func (p *ProjectAPI) populateProperties(project *models.Project) {
}) })
if err != nil { if err != nil {
log.Errorf("failed to get total of repositories of project %d: %v", project.ProjectID, err) log.Errorf("failed to get total of repositories of project %d: %v", project.ProjectID, err)
p.CustomAbort(http.StatusInternalServerError, "") p.SendInternalServerError(errors.New(""))
return
} }
project.RepoCount = total project.RepoCount = total
@ -424,7 +436,8 @@ func (p *ProjectAPI) populateProperties(project *models.Project) {
count, err := chartController.GetCountOfCharts([]string{project.Name}) count, err := chartController.GetCountOfCharts([]string{project.Name})
if err != nil { if err != nil {
log.Errorf("Failed to get total of charts under project %s: %v", project.Name, err) log.Errorf("Failed to get total of charts under project %s: %v", project.Name, err)
p.CustomAbort(http.StatusInternalServerError, "") p.SendInternalServerError(errors.New(""))
return
} }
project.ChartCount = count project.ChartCount = count
@ -438,7 +451,10 @@ func (p *ProjectAPI) Put() {
} }
var req *models.ProjectRequest var req *models.ProjectRequest
p.DecodeJSONReq(&req) if err := p.DecodeJSONReq(&req); err != nil {
p.SendBadRequestError(err)
return
}
if err := p.ProjectMgr.Update(p.project.ProjectID, if err := p.ProjectMgr.Update(p.project.ProjectID,
&models.Project{ &models.Project{
@ -456,7 +472,11 @@ func (p *ProjectAPI) Logs() {
return return
} }
page, size := p.GetPaginationParams() page, size, err := p.GetPaginationParams()
if err != nil {
p.SendBadRequestError(err)
return
}
query := &models.LogQueryParam{ query := &models.LogQueryParam{
ProjectIDs: []int64{p.project.ProjectID}, ProjectIDs: []int64{p.project.ProjectID},
Username: p.GetString("username"), Username: p.GetString("username"),
@ -473,7 +493,7 @@ func (p *ProjectAPI) Logs() {
if len(timestamp) > 0 { if len(timestamp) > 0 {
t, err := utils.ParseTimeStamp(timestamp) t, err := utils.ParseTimeStamp(timestamp)
if err != nil { if err != nil {
p.HandleBadRequest(fmt.Sprintf("invalid begin_timestamp: %s", timestamp)) p.SendBadRequestError(fmt.Errorf("invalid begin_timestamp: %s", timestamp))
return return
} }
query.BeginTime = t query.BeginTime = t
@ -483,7 +503,7 @@ func (p *ProjectAPI) Logs() {
if len(timestamp) > 0 { if len(timestamp) > 0 {
t, err := utils.ParseTimeStamp(timestamp) t, err := utils.ParseTimeStamp(timestamp)
if err != nil { if err != nil {
p.HandleBadRequest(fmt.Sprintf("invalid end_timestamp: %s", timestamp)) p.SendBadRequestError(fmt.Errorf("invalid end_timestamp: %s", timestamp))
return return
} }
query.EndTime = t query.EndTime = t
@ -491,14 +511,14 @@ func (p *ProjectAPI) Logs() {
total, err := dao.GetTotalOfAccessLogs(query) total, err := dao.GetTotalOfAccessLogs(query)
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf( p.SendInternalServerError(fmt.Errorf(
"failed to get total of access log: %v", err)) "failed to get total of access log: %v", err))
return return
} }
logs, err := dao.GetAccessLogs(query) logs, err := dao.GetAccessLogs(query)
if err != nil { if err != nil {
p.HandleInternalServerError(fmt.Sprintf( p.SendInternalServerError(fmt.Errorf(
"failed to get access log: %v", err)) "failed to get access log: %v", err))
return return
} }

View File

@ -50,7 +50,7 @@ func (pma *ProjectMemberAPI) Prepare() {
pma.BaseController.Prepare() pma.BaseController.Prepare()
if !pma.SecurityCtx.IsAuthenticated() { if !pma.SecurityCtx.IsAuthenticated() {
pma.HandleUnauthorized() pma.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
pid, err := pma.GetInt64FromPath(":pid") pid, err := pma.GetInt64FromPath(":pid")
@ -61,7 +61,7 @@ func (pma *ProjectMemberAPI) Prepare() {
} else { } else {
text += fmt.Sprintf("%d", pid) text += fmt.Sprintf("%d", pid)
} }
pma.HandleBadRequest(text) pma.SendBadRequestError(errors.New(text))
return return
} }
project, err := pma.ProjectMgr.Get(pid) project, err := pma.ProjectMgr.Get(pid)
@ -70,7 +70,7 @@ func (pma *ProjectMemberAPI) Prepare() {
return return
} }
if project == nil { if project == nil {
pma.HandleNotFound(fmt.Sprintf("project %d not found", pid)) pma.SendNotFoundError(fmt.Errorf("project %d not found", pid))
return return
} }
pma.project = project pma.project = project
@ -80,7 +80,7 @@ func (pma *ProjectMemberAPI) Prepare() {
log.Warningf("Failed to get pmid from path, error %v", err) log.Warningf("Failed to get pmid from path, error %v", err)
} }
if pmid <= 0 && (pma.Ctx.Input.IsPut() || pma.Ctx.Input.IsDelete()) { if pmid <= 0 && (pma.Ctx.Input.IsPut() || pma.Ctx.Input.IsDelete()) {
pma.HandleBadRequest(fmt.Sprintf("The project member id is invalid, pmid:%s", pma.GetStringFromPath(":pmid"))) pma.SendBadRequestError(fmt.Errorf("The project member id is invalid, pmid:%s", pma.GetStringFromPath(":pmid")))
return return
} }
pma.id = int(pmid) pma.id = int(pmid)
@ -91,9 +91,9 @@ func (pma *ProjectMemberAPI) requireAccess(action rbac.Action) bool {
if !pma.SecurityCtx.Can(action, resource) { if !pma.SecurityCtx.Can(action, resource) {
if !pma.SecurityCtx.IsAuthenticated() { if !pma.SecurityCtx.IsAuthenticated() {
pma.HandleUnauthorized() pma.SendUnAuthorizedError(errors.New("Unauthorized"))
} else { } else {
pma.HandleForbidden(pma.SecurityCtx.GetUsername()) pma.SendForbiddenError(errors.New(pma.SecurityCtx.GetUsername()))
} }
return false return false
@ -115,7 +115,7 @@ func (pma *ProjectMemberAPI) Get() {
entityname := pma.GetString("entityname") entityname := pma.GetString("entityname")
memberList, err := project.SearchMemberByName(projectID, entityname) memberList, err := project.SearchMemberByName(projectID, entityname)
if err != nil { if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("Failed to query database for member list, error: %v", err)) pma.SendInternalServerError(fmt.Errorf("Failed to query database for member list, error: %v", err))
return return
} }
if len(memberList) > 0 { if len(memberList) > 0 {
@ -127,11 +127,11 @@ func (pma *ProjectMemberAPI) Get() {
queryMember.ID = pma.id queryMember.ID = pma.id
memberList, err := project.GetProjectMember(queryMember) memberList, err := project.GetProjectMember(queryMember)
if err != nil { if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("Failed to query database for member list, error: %v", err)) pma.SendInternalServerError(fmt.Errorf("Failed to query database for member list, error: %v", err))
return return
} }
if len(memberList) == 0 { if len(memberList) == 0 {
pma.HandleNotFound(fmt.Sprintf("The project member does not exit, pmid:%v", pma.id)) pma.SendNotFoundError(fmt.Errorf("The project member does not exit, pmid:%v", pma.id))
return return
} }
@ -150,27 +150,30 @@ func (pma *ProjectMemberAPI) Post() {
} }
projectID := pma.project.ProjectID projectID := pma.project.ProjectID
var request models.MemberReq var request models.MemberReq
pma.DecodeJSONReq(&request) if err := pma.DecodeJSONReq(&request); err != nil {
pma.SendBadRequestError(err)
return
}
request.MemberGroup.LdapGroupDN = strings.TrimSpace(request.MemberGroup.LdapGroupDN) request.MemberGroup.LdapGroupDN = strings.TrimSpace(request.MemberGroup.LdapGroupDN)
pmid, err := AddProjectMember(projectID, request) pmid, err := AddProjectMember(projectID, request)
if err == auth.ErrorGroupNotExist || err == auth.ErrorUserNotExist { if err == auth.ErrorGroupNotExist || err == auth.ErrorUserNotExist {
pma.HandleNotFound(fmt.Sprintf("Failed to add project member, error: %v", err)) pma.SendNotFoundError(fmt.Errorf("Failed to add project member, error: %v", err))
return return
} else if err == auth.ErrDuplicateLDAPGroup { } else if err == auth.ErrDuplicateLDAPGroup {
pma.HandleConflict(fmt.Sprintf("Failed to add project member, already exist LDAP group or project member, groupDN:%v", request.MemberGroup.LdapGroupDN)) pma.SendConflictError(fmt.Errorf("Failed to add project member, already exist LDAP group or project member, groupDN:%v", request.MemberGroup.LdapGroupDN))
return return
} else if err == ErrDuplicateProjectMember { } else if err == ErrDuplicateProjectMember {
pma.HandleConflict(fmt.Sprintf("Failed to add project member, already exist LDAP group or project member, groupMemberID:%v", request.MemberGroup.ID)) pma.SendConflictError(fmt.Errorf("Failed to add project member, already exist LDAP group or project member, groupMemberID:%v", request.MemberGroup.ID))
return return
} else if err == ErrInvalidRole { } else if err == ErrInvalidRole {
pma.HandleBadRequest(fmt.Sprintf("Invalid role ID, role ID %v", request.Role)) pma.SendBadRequestError(fmt.Errorf("Invalid role ID, role ID %v", request.Role))
return return
} else if err == auth.ErrInvalidLDAPGroupDN { } else if err == auth.ErrInvalidLDAPGroupDN {
pma.HandleBadRequest(fmt.Sprintf("Invalid LDAP DN: %v", request.MemberGroup.LdapGroupDN)) pma.SendBadRequestError(fmt.Errorf("Invalid LDAP DN: %v", request.MemberGroup.LdapGroupDN))
return return
} else if err != nil { } else if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("Failed to add project member, error: %v", err)) pma.SendInternalServerError(fmt.Errorf("Failed to add project member, error: %v", err))
return return
} }
pma.Redirect(http.StatusCreated, strconv.FormatInt(int64(pmid), 10)) pma.Redirect(http.StatusCreated, strconv.FormatInt(int64(pmid), 10))
@ -184,14 +187,17 @@ func (pma *ProjectMemberAPI) Put() {
pid := pma.project.ProjectID pid := pma.project.ProjectID
pmID := pma.id pmID := pma.id
var req models.Member var req models.Member
pma.DecodeJSONReq(&req) if err := pma.DecodeJSONReq(&req); err != nil {
pma.SendBadRequestError(err)
return
}
if req.Role < 1 || req.Role > 4 { if req.Role < 1 || req.Role > 4 {
pma.HandleBadRequest(fmt.Sprintf("Invalid role id %v", req.Role)) pma.SendBadRequestError(fmt.Errorf("Invalid role id %v", req.Role))
return return
} }
err := project.UpdateProjectMemberRole(pmID, req.Role) err := project.UpdateProjectMemberRole(pmID, req.Role)
if err != nil { if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("Failed to update DB to add project user role, project id: %d, pmid : %d, role id: %d", pid, pmID, req.Role)) pma.SendInternalServerError(fmt.Errorf("Failed to update DB to add project user role, project id: %d, pmid : %d, role id: %d", pid, pmID, req.Role))
return return
} }
} }
@ -204,7 +210,7 @@ func (pma *ProjectMemberAPI) Delete() {
pmid := pma.id pmid := pma.id
err := project.DeleteProjectMemberByID(pmid) err := project.DeleteProjectMemberByID(pmid)
if err != nil { if err != nil {
pma.HandleInternalServerError(fmt.Sprintf("Failed to delete project roles for user, project member id: %d, error: %v", pmid, err)) pma.SendInternalServerError(fmt.Errorf("Failed to delete project roles for user, project member id: %d, error: %v", pmid, err))
return return
} }
} }

View File

@ -15,7 +15,7 @@
package api package api
import ( import (
"fmt" "errors"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@ -33,11 +33,11 @@ type GCAPI struct {
func (gc *GCAPI) Prepare() { func (gc *GCAPI) Prepare() {
gc.BaseController.Prepare() gc.BaseController.Prepare()
if !gc.SecurityCtx.IsAuthenticated() { if !gc.SecurityCtx.IsAuthenticated() {
gc.HandleUnauthorized() gc.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if !gc.SecurityCtx.IsSysAdmin() { if !gc.SecurityCtx.IsSysAdmin() {
gc.HandleForbidden(gc.SecurityCtx.GetUsername()) gc.SendForbiddenError(errors.New(gc.SecurityCtx.GetUsername()))
return return
} }
} }
@ -58,7 +58,11 @@ func (gc *GCAPI) Prepare() {
// } // }
func (gc *GCAPI) Post() { func (gc *GCAPI) Post() {
ajr := models.AdminJobReq{} ajr := models.AdminJobReq{}
gc.DecodeJSONReqAndValidate(&ajr) isValid, err := gc.DecodeJSONReqAndValidate(&ajr)
if !isValid {
gc.SendBadRequestError(err)
return
}
ajr.Name = common_job.ImageGC ajr.Name = common_job.ImageGC
ajr.Parameters = map[string]interface{}{ ajr.Parameters = map[string]interface{}{
"redis_url_reg": os.Getenv("_REDIS_URL_REG"), "redis_url_reg": os.Getenv("_REDIS_URL_REG"),
@ -77,7 +81,11 @@ func (gc *GCAPI) Post() {
// } // }
func (gc *GCAPI) Put() { func (gc *GCAPI) Put() {
ajr := models.AdminJobReq{} ajr := models.AdminJobReq{}
gc.DecodeJSONReqAndValidate(&ajr) isValid, err := gc.DecodeJSONReqAndValidate(&ajr)
if !isValid {
gc.SendBadRequestError(err)
return
}
ajr.Name = common_job.ImageGC ajr.Name = common_job.ImageGC
gc.updateSchedule(ajr) gc.updateSchedule(ajr)
} }
@ -86,7 +94,7 @@ func (gc *GCAPI) Put() {
func (gc *GCAPI) GetGC() { func (gc *GCAPI) GetGC() {
id, err := gc.GetInt64FromPath(":id") id, err := gc.GetInt64FromPath(":id")
if err != nil { if err != nil {
gc.HandleInternalServerError(fmt.Sprintf("need to specify gc id")) gc.SendInternalServerError(errors.New("need to specify gc id"))
return return
} }
gc.get(id) gc.get(id)
@ -106,7 +114,7 @@ func (gc *GCAPI) Get() {
func (gc *GCAPI) GetLog() { func (gc *GCAPI) GetLog() {
id, err := gc.GetInt64FromPath(":id") id, err := gc.GetInt64FromPath(":id")
if err != nil { if err != nil {
gc.HandleBadRequest("invalid ID") gc.SendBadRequestError(errors.New("invalid ID"))
return return
} }
gc.getLog(id) gc.getLog(id)

View File

@ -16,7 +16,6 @@ package api
import ( import (
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
@ -28,6 +27,7 @@ import (
"github.com/goharbor/harbor/src/replication/event/notification" "github.com/goharbor/harbor/src/replication/event/notification"
"github.com/goharbor/harbor/src/replication/event/topic" "github.com/goharbor/harbor/src/replication/event/topic"
"errors"
"github.com/docker/distribution/uuid" "github.com/docker/distribution/uuid"
) )
@ -40,12 +40,12 @@ type ReplicationAPI struct {
func (r *ReplicationAPI) Prepare() { func (r *ReplicationAPI) Prepare() {
r.BaseController.Prepare() r.BaseController.Prepare()
if !r.SecurityCtx.IsAuthenticated() { if !r.SecurityCtx.IsAuthenticated() {
r.HandleUnauthorized() r.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
if !r.SecurityCtx.IsSysAdmin() && !r.SecurityCtx.IsSolutionUser() { if !r.SecurityCtx.IsSysAdmin() && !r.SecurityCtx.IsSolutionUser() {
r.HandleForbidden(r.SecurityCtx.GetUsername()) r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
return return
} }
} }
@ -53,16 +53,20 @@ func (r *ReplicationAPI) Prepare() {
// Post trigger a replication according to the specified policy // Post trigger a replication according to the specified policy
func (r *ReplicationAPI) Post() { func (r *ReplicationAPI) Post() {
replication := &api_models.Replication{} replication := &api_models.Replication{}
r.DecodeJSONReqAndValidate(replication) isValid, err := r.DecodeJSONReqAndValidate(replication)
if !isValid {
r.SendBadRequestError(err)
return
}
policy, err := core.GlobalController.GetPolicy(replication.PolicyID) policy, err := core.GlobalController.GetPolicy(replication.PolicyID)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to get replication policy %d: %v", replication.PolicyID, err)) r.SendInternalServerError(fmt.Errorf("failed to get replication policy %d: %v", replication.PolicyID, err))
return return
} }
if policy.ID == 0 { if policy.ID == 0 {
r.HandleNotFound(fmt.Sprintf("replication policy %d not found", replication.PolicyID)) r.SendNotFoundError(fmt.Errorf("replication policy %d not found", replication.PolicyID))
return return
} }
@ -72,18 +76,18 @@ func (r *ReplicationAPI) Post() {
Operations: []string{models.RepOpTransfer, models.RepOpDelete}, Operations: []string{models.RepOpTransfer, models.RepOpDelete},
}) })
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to filter jobs of policy %d: %v", r.SendInternalServerError(fmt.Errorf("failed to filter jobs of policy %d: %v",
replication.PolicyID, err)) replication.PolicyID, err))
return return
} }
if count > 0 { if count > 0 {
r.RenderError(http.StatusPreconditionFailed, "policy has running/pending jobs, new replication can not be triggered") r.SendPreconditionFailedError(errors.New("policy has running/pending jobs, new replication can not be triggered"))
return return
} }
opUUID, err := startReplication(replication.PolicyID) opUUID, err := startReplication(replication.PolicyID)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to publish replication topic for policy %d: %v", replication.PolicyID, err)) r.SendInternalServerError(fmt.Errorf("failed to publish replication topic for policy %d: %v", replication.PolicyID, err))
return return
} }
log.Infof("replication signal for policy %d sent", replication.PolicyID) log.Infof("replication signal for policy %d sent", replication.PolicyID)

View File

@ -15,13 +15,13 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
common_http "github.com/goharbor/harbor/src/common/http"
common_job "github.com/goharbor/harbor/src/common/job" common_job "github.com/goharbor/harbor/src/common/job"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
@ -41,19 +41,19 @@ type RepJobAPI struct {
func (ra *RepJobAPI) Prepare() { func (ra *RepJobAPI) Prepare() {
ra.BaseController.Prepare() ra.BaseController.Prepare()
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
if !(ra.Ctx.Request.Method == http.MethodGet || ra.SecurityCtx.IsSysAdmin()) { if !(ra.Ctx.Request.Method == http.MethodGet || ra.SecurityCtx.IsSysAdmin()) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
if len(ra.GetStringFromPath(":id")) != 0 { if len(ra.GetStringFromPath(":id")) != 0 {
id, err := ra.GetInt64FromPath(":id") id, err := ra.GetInt64FromPath(":id")
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid ID: %s", ra.GetStringFromPath(":id"))) ra.SendBadRequestError(fmt.Errorf("invalid ID: %s", ra.GetStringFromPath(":id")))
return return
} }
ra.jobID = id ra.jobID = id
@ -66,24 +66,25 @@ func (ra *RepJobAPI) List() {
policyID, err := ra.GetInt64("policy_id") policyID, err := ra.GetInt64("policy_id")
if err != nil || policyID <= 0 { if err != nil || policyID <= 0 {
ra.HandleBadRequest(fmt.Sprintf("invalid policy_id: %s", ra.GetString("policy_id"))) ra.SendBadRequestError(fmt.Errorf("invalid policy_id: %s", ra.GetString("policy_id")))
return return
} }
policy, err := core.GlobalController.GetPolicy(policyID) policy, err := core.GlobalController.GetPolicy(policyID)
if err != nil { if err != nil {
log.Errorf("failed to get policy %d: %v", policyID, err) log.Errorf("failed to get policy %d: %v", policyID, err)
ra.CustomAbort(http.StatusInternalServerError, "") ra.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", policyID, err))
return
} }
if policy.ID == 0 { if policy.ID == 0 {
ra.HandleNotFound(fmt.Sprintf("policy %d not found", policyID)) ra.SendNotFoundError(fmt.Errorf("policy %d not found", policyID))
return return
} }
resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob) resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) { if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
@ -102,7 +103,7 @@ func (ra *RepJobAPI) List() {
if len(startTimeStr) != 0 { if len(startTimeStr) != 0 {
i, err := strconv.ParseInt(startTimeStr, 10, 64) i, err := strconv.ParseInt(startTimeStr, 10, 64)
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid start_time: %s", startTimeStr)) ra.SendBadRequestError(fmt.Errorf("invalid start_time: %s", startTimeStr))
return return
} }
t := time.Unix(i, 0) t := time.Unix(i, 0)
@ -113,23 +114,27 @@ func (ra *RepJobAPI) List() {
if len(endTimeStr) != 0 { if len(endTimeStr) != 0 {
i, err := strconv.ParseInt(endTimeStr, 10, 64) i, err := strconv.ParseInt(endTimeStr, 10, 64)
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid end_time: %s", endTimeStr)) ra.SendBadRequestError(fmt.Errorf("invalid end_time: %s", endTimeStr))
return return
} }
t := time.Unix(i, 0) t := time.Unix(i, 0)
query.EndTime = &t query.EndTime = &t
} }
query.Page, query.Size = ra.GetPaginationParams() query.Page, query.Size, err = ra.GetPaginationParams()
if err != nil {
ra.SendBadRequestError(err)
return
}
total, err := dao.GetTotalCountOfRepJobs(query) total, err := dao.GetTotalCountOfRepJobs(query)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get total count of repository jobs of policy %d: %v", policyID, err)) ra.SendInternalServerError(fmt.Errorf("failed to get total count of repository jobs of policy %d: %v", policyID, err))
return return
} }
jobs, err := dao.GetRepJobs(query) jobs, err := dao.GetRepJobs(query)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get repository jobs, query: %v :%v", query, err)) ra.SendInternalServerError(fmt.Errorf("failed to get repository jobs, query: %v :%v", query, err))
return return
} }
@ -142,79 +147,75 @@ func (ra *RepJobAPI) List() {
// Delete ... // Delete ...
func (ra *RepJobAPI) Delete() { func (ra *RepJobAPI) Delete() {
if ra.jobID == 0 { if ra.jobID == 0 {
ra.HandleBadRequest("ID is nil") ra.SendBadRequestError(errors.New("ID is nil"))
return return
} }
job, err := dao.GetRepJob(ra.jobID) job, err := dao.GetRepJob(ra.jobID)
if err != nil { if err != nil {
log.Errorf("failed to get job %d: %v", ra.jobID, err) log.Errorf("failed to get job %d: %v", ra.jobID, err)
ra.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) ra.SendInternalServerError(fmt.Errorf("failed to get job %d: %v", ra.jobID, err))
return
} }
if job == nil { if job == nil {
ra.HandleNotFound(fmt.Sprintf("job %d not found", ra.jobID)) ra.SendNotFoundError(fmt.Errorf("job %d not found", ra.jobID))
return return
} }
if job.Status == models.JobPending || job.Status == models.JobRunning { if job.Status == models.JobPending || job.Status == models.JobRunning {
ra.HandleBadRequest(fmt.Sprintf("job is %s, can not be deleted", job.Status)) ra.SendBadRequestError(fmt.Errorf("job is %s, can not be deleted", job.Status))
return return
} }
if err = dao.DeleteRepJob(ra.jobID); err != nil { if err = dao.DeleteRepJob(ra.jobID); err != nil {
log.Errorf("failed to deleted job %d: %v", ra.jobID, err) log.Errorf("failed to deleted job %d: %v", ra.jobID, err)
ra.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) ra.SendInternalServerError(fmt.Errorf("failed to deleted job %d: %v", ra.jobID, err))
return
} }
} }
// GetLog ... // GetLog ...
func (ra *RepJobAPI) GetLog() { func (ra *RepJobAPI) GetLog() {
if ra.jobID == 0 { if ra.jobID == 0 {
ra.HandleBadRequest("ID is nil") ra.SendBadRequestError(errors.New("ID is nil"))
return return
} }
job, err := dao.GetRepJob(ra.jobID) job, err := dao.GetRepJob(ra.jobID)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get replication job %d: %v", ra.jobID, err)) ra.SendInternalServerError(fmt.Errorf("failed to get replication job %d: %v", ra.jobID, err))
return return
} }
if job == nil { if job == nil {
ra.HandleNotFound(fmt.Sprintf("replication job %d not found", ra.jobID)) ra.SendNotFoundError(fmt.Errorf("replication job %d not found", ra.jobID))
return return
} }
policy, err := core.GlobalController.GetPolicy(job.PolicyID) policy, err := core.GlobalController.GetPolicy(job.PolicyID)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get policy %d: %v", job.PolicyID, err)) ra.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", job.PolicyID, err))
return return
} }
resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob) resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplicationJob)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) { if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
logBytes, err := utils.GetJobServiceClient().GetJobLog(job.UUID) logBytes, err := utils.GetJobServiceClient().GetJobLog(job.UUID)
if err != nil { if err != nil {
if httpErr, ok := err.(*common_http.Error); ok { ra.ParseAndHandleError(fmt.Sprintf("failed to get log of job %s",
ra.RenderError(httpErr.Code, "") job.UUID), err)
log.Errorf(fmt.Sprintf("failed to get log of job %d: %d %s",
ra.jobID, httpErr.Code, httpErr.Message))
return
}
ra.HandleInternalServerError(fmt.Sprintf("failed to get log of job %s: %v",
job.UUID, err))
return return
} }
ra.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes))) ra.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes)))
ra.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain") ra.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain")
_, err = ra.Ctx.ResponseWriter.Write(logBytes) _, err = ra.Ctx.ResponseWriter.Write(logBytes)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to write log of job %s: %v", job.UUID, err)) ra.SendInternalServerError(fmt.Errorf("failed to write log of job %s: %v", job.UUID, err))
return return
} }
} }
@ -222,16 +223,20 @@ func (ra *RepJobAPI) GetLog() {
// StopJobs stop replication jobs for the policy // StopJobs stop replication jobs for the policy
func (ra *RepJobAPI) StopJobs() { func (ra *RepJobAPI) StopJobs() {
req := &api_models.StopJobsReq{} req := &api_models.StopJobsReq{}
ra.DecodeJSONReqAndValidate(req) isValid, err := ra.DecodeJSONReqAndValidate(req)
if !isValid {
ra.SendBadRequestError(err)
return
}
policy, err := core.GlobalController.GetPolicy(req.PolicyID) policy, err := core.GlobalController.GetPolicy(req.PolicyID)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get policy %d: %v", req.PolicyID, err)) ra.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", req.PolicyID, err))
return return
} }
if policy.ID == 0 { if policy.ID == 0 {
ra.HandleNotFound(fmt.Sprintf("policy %d not found", req.PolicyID)) ra.SendNotFoundError(fmt.Errorf("policy %d not found", req.PolicyID))
return return
} }
@ -240,7 +245,7 @@ func (ra *RepJobAPI) StopJobs() {
Operations: []string{models.RepOpTransfer, models.RepOpDelete}, Operations: []string{models.RepOpTransfer, models.RepOpDelete},
}) })
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to list jobs of policy %d: %v", policy.ID, err)) ra.SendInternalServerError(fmt.Errorf("failed to list jobs of policy %d: %v", policy.ID, err))
return return
} }
for _, job := range jobs { for _, job := range jobs {

View File

@ -17,6 +17,7 @@ package api
import ( import (
"fmt" "fmt"
"errors"
"net/http" "net/http"
"strconv" "strconv"
@ -40,33 +41,39 @@ type RepPolicyAPI struct {
func (pa *RepPolicyAPI) Prepare() { func (pa *RepPolicyAPI) Prepare() {
pa.BaseController.Prepare() pa.BaseController.Prepare()
if !pa.SecurityCtx.IsAuthenticated() { if !pa.SecurityCtx.IsAuthenticated() {
pa.HandleUnauthorized() pa.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
if !(pa.Ctx.Request.Method == http.MethodGet || pa.SecurityCtx.IsSysAdmin()) { if !(pa.Ctx.Request.Method == http.MethodGet || pa.SecurityCtx.IsSysAdmin()) {
pa.HandleForbidden(pa.SecurityCtx.GetUsername()) pa.SendForbiddenError(errors.New(pa.SecurityCtx.GetUsername()))
return return
} }
} }
// Get ... // Get ...
func (pa *RepPolicyAPI) Get() { func (pa *RepPolicyAPI) Get() {
id := pa.GetIDFromURL() id, err := pa.GetIDFromURL()
if err != nil {
pa.SendBadRequestError(err)
return
}
policy, err := core.GlobalController.GetPolicy(id) policy, err := core.GlobalController.GetPolicy(id)
if err != nil { if err != nil {
log.Errorf("failed to get policy %d: %v", id, err) log.Errorf("failed to get policy %d: %v", id, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) pa.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", id, err))
return
} }
if policy.ID == 0 { if policy.ID == 0 {
pa.HandleNotFound(fmt.Sprintf("policy %d not found", id)) pa.SendNotFoundError(fmt.Errorf("policy %d not found", id))
return return
} }
resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplication) resource := rbac.NewProjectNamespace(policy.ProjectIDs[0]).Resource(rbac.ResourceReplication)
if !pa.SecurityCtx.Can(rbac.ActionRead, resource) { if !pa.SecurityCtx.Can(rbac.ActionRead, resource) {
pa.HandleForbidden(pa.SecurityCtx.GetUsername()) pa.SendForbiddenError(errors.New(pa.SecurityCtx.GetUsername()))
return return
} }
@ -89,17 +96,23 @@ func (pa *RepPolicyAPI) List() {
if len(projectIDStr) > 0 { if len(projectIDStr) > 0 {
projectID, err := strconv.ParseInt(projectIDStr, 10, 64) projectID, err := strconv.ParseInt(projectIDStr, 10, 64)
if err != nil || projectID <= 0 { if err != nil || projectID <= 0 {
pa.HandleBadRequest(fmt.Sprintf("invalid project ID: %s", projectIDStr)) pa.SendBadRequestError(fmt.Errorf("invalid project ID: %s", projectIDStr))
return return
} }
queryParam.ProjectID = projectID queryParam.ProjectID = projectID
} }
queryParam.Page, queryParam.PageSize = pa.GetPaginationParams() var err error
queryParam.Page, queryParam.PageSize, err = pa.GetPaginationParams()
if err != nil {
pa.SendBadRequestError(err)
return
}
result, err := core.GlobalController.GetPolicies(queryParam) result, err := core.GlobalController.GetPolicies(queryParam)
if err != nil { if err != nil {
log.Errorf("failed to get policies: %v, query parameters: %v", err, queryParam) log.Errorf("failed to get policies: %v, query parameters: %v", err, queryParam)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) pa.SendInternalServerError(fmt.Errorf("failed to get policies: %v, query parameters: %v", err, queryParam))
return
} }
var total int64 var total int64
@ -129,17 +142,21 @@ func (pa *RepPolicyAPI) List() {
// Post creates a replicartion policy // Post creates a replicartion policy
func (pa *RepPolicyAPI) Post() { func (pa *RepPolicyAPI) Post() {
policy := &api_models.ReplicationPolicy{} policy := &api_models.ReplicationPolicy{}
pa.DecodeJSONReqAndValidate(policy) isValid, err := pa.DecodeJSONReqAndValidate(policy)
if !isValid {
pa.SendBadRequestError(err)
return
}
// check the name // check the name
exist, err := exist(policy.Name) exist, err := exist(policy.Name)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to check the existence of policy %s: %v", policy.Name, err)) pa.SendInternalServerError(fmt.Errorf("failed to check the existence of policy %s: %v", policy.Name, err))
return return
} }
if exist { if exist {
pa.HandleConflict(fmt.Sprintf("name %s is already used", policy.Name)) pa.SendConflictError(fmt.Errorf("name %s is already used", policy.Name))
return return
} }
@ -151,7 +168,7 @@ func (pa *RepPolicyAPI) Post() {
return return
} }
if pro == nil { if pro == nil {
pa.HandleNotFound(fmt.Sprintf("project %d not found", project.ProjectID)) pa.SendNotFoundError(fmt.Errorf("project %d not found", project.ProjectID))
return return
} }
project.Name = pro.Name project.Name = pro.Name
@ -161,12 +178,12 @@ func (pa *RepPolicyAPI) Post() {
for _, target := range policy.Targets { for _, target := range policy.Targets {
t, err := dao.GetRepTarget(target.ID) t, err := dao.GetRepTarget(target.ID)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to get target %d: %v", target.ID, err)) pa.SendInternalServerError(fmt.Errorf("failed to get target %d: %v", target.ID, err))
return return
} }
if t == nil { if t == nil {
pa.HandleNotFound(fmt.Sprintf("target %d not found", target.ID)) pa.SendNotFoundError(fmt.Errorf("target %d not found", target.ID))
return return
} }
} }
@ -177,11 +194,11 @@ func (pa *RepPolicyAPI) Post() {
labelID := filter.Value.(int64) labelID := filter.Value.(int64)
label, err := dao.GetLabel(labelID) label, err := dao.GetLabel(labelID)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to get label %d: %v", labelID, err)) pa.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", labelID, err))
return return
} }
if label == nil || label.Deleted { if label == nil || label.Deleted {
pa.HandleNotFound(fmt.Sprintf("label %d not found", labelID)) pa.SendNotFoundError(fmt.Errorf("label %d not found", labelID))
return return
} }
} }
@ -189,7 +206,7 @@ func (pa *RepPolicyAPI) Post() {
id, err := core.GlobalController.CreatePolicy(convertToRepPolicy(policy)) id, err := core.GlobalController.CreatePolicy(convertToRepPolicy(policy))
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to create policy: %v", err)) pa.SendInternalServerError(fmt.Errorf("failed to create policy: %v", err))
return return
} }
@ -224,21 +241,30 @@ func exist(name string) (bool, error) {
// Put updates the replication policy // Put updates the replication policy
func (pa *RepPolicyAPI) Put() { func (pa *RepPolicyAPI) Put() {
id := pa.GetIDFromURL() id, err := pa.GetIDFromURL()
if err != nil {
pa.SendBadRequestError(err)
return
}
originalPolicy, err := core.GlobalController.GetPolicy(id) originalPolicy, err := core.GlobalController.GetPolicy(id)
if err != nil { if err != nil {
log.Errorf("failed to get policy %d: %v", id, err) log.Errorf("failed to get policy %d: %v", id, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) pa.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", id, err))
return
} }
if originalPolicy.ID == 0 { if originalPolicy.ID == 0 {
pa.HandleNotFound(fmt.Sprintf("policy %d not found", id)) pa.SendNotFoundError(fmt.Errorf("policy %d not found", id))
return return
} }
policy := &api_models.ReplicationPolicy{} policy := &api_models.ReplicationPolicy{}
pa.DecodeJSONReqAndValidate(policy) isValid, err := pa.DecodeJSONReqAndValidate(policy)
if !isValid {
pa.SendBadRequestError(err)
return
}
policy.ID = id policy.ID = id
@ -246,12 +272,12 @@ func (pa *RepPolicyAPI) Put() {
if policy.Name != originalPolicy.Name { if policy.Name != originalPolicy.Name {
exist, err := exist(policy.Name) exist, err := exist(policy.Name)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to check the existence of policy %s: %v", policy.Name, err)) pa.SendInternalServerError(fmt.Errorf("failed to check the existence of policy %s: %v", policy.Name, err))
return return
} }
if exist { if exist {
pa.HandleConflict(fmt.Sprintf("name %s is already used", policy.Name)) pa.SendConflictError(fmt.Errorf("name %s is already used", policy.Name))
return return
} }
} }
@ -264,7 +290,7 @@ func (pa *RepPolicyAPI) Put() {
return return
} }
if pro == nil { if pro == nil {
pa.HandleNotFound(fmt.Sprintf("project %d not found", project.ProjectID)) pa.SendNotFoundError(fmt.Errorf("project %d not found", project.ProjectID))
return return
} }
project.Name = pro.Name project.Name = pro.Name
@ -274,12 +300,12 @@ func (pa *RepPolicyAPI) Put() {
for _, target := range policy.Targets { for _, target := range policy.Targets {
t, err := dao.GetRepTarget(target.ID) t, err := dao.GetRepTarget(target.ID)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to get target %d: %v", target.ID, err)) pa.SendInternalServerError(fmt.Errorf("failed to get target %d: %v", target.ID, err))
return return
} }
if t == nil { if t == nil {
pa.HandleNotFound(fmt.Sprintf("target %d not found", target.ID)) pa.SendNotFoundError(fmt.Errorf("target %d not found", target.ID))
return return
} }
} }
@ -290,18 +316,18 @@ func (pa *RepPolicyAPI) Put() {
labelID := filter.Value.(int64) labelID := filter.Value.(int64)
label, err := dao.GetLabel(labelID) label, err := dao.GetLabel(labelID)
if err != nil { if err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to get label %d: %v", labelID, err)) pa.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", labelID, err))
return return
} }
if label == nil || label.Deleted { if label == nil || label.Deleted {
pa.HandleNotFound(fmt.Sprintf("label %d not found", labelID)) pa.SendNotFoundError(fmt.Errorf("label %d not found", labelID))
return return
} }
} }
} }
if err = core.GlobalController.UpdatePolicy(convertToRepPolicy(policy)); err != nil { if err = core.GlobalController.UpdatePolicy(convertToRepPolicy(policy)); err != nil {
pa.HandleInternalServerError(fmt.Sprintf("failed to update policy %d: %v", id, err)) pa.SendInternalServerError(fmt.Errorf("failed to update policy %d: %v", id, err))
return return
} }
@ -318,16 +344,21 @@ func (pa *RepPolicyAPI) Put() {
// Delete the replication policy // Delete the replication policy
func (pa *RepPolicyAPI) Delete() { func (pa *RepPolicyAPI) Delete() {
id := pa.GetIDFromURL() id, err := pa.GetIDFromURL()
if err != nil {
pa.SendBadRequestError(err)
return
}
policy, err := core.GlobalController.GetPolicy(id) policy, err := core.GlobalController.GetPolicy(id)
if err != nil { if err != nil {
log.Errorf("failed to get policy %d: %v", id, err) log.Errorf("failed to get policy %d: %v", id, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) pa.SendInternalServerError(fmt.Errorf("failed to get policy %d: %v", id, err))
return
} }
if policy.ID == 0 { if policy.ID == 0 {
pa.HandleNotFound(fmt.Sprintf("policy %d not found", id)) pa.SendNotFoundError(fmt.Errorf("policy %d not found", id))
return return
} }
@ -339,15 +370,19 @@ func (pa *RepPolicyAPI) Delete() {
}) })
if err != nil { if err != nil {
log.Errorf("failed to filter jobs of policy %d: %v", id, err) log.Errorf("failed to filter jobs of policy %d: %v", id, err)
pa.CustomAbort(http.StatusInternalServerError, "") pa.SendInternalServerError(fmt.Errorf("failed to filter jobs of policy %d: %v", id, err))
return
} }
if count > 0 { if count > 0 {
pa.CustomAbort(http.StatusPreconditionFailed, "policy has running/retrying/pending jobs, can not be deleted") pa.SendPreconditionFailedError(errors.New("policy has running/retrying/pending jobs, can not be deleted"))
return
} }
if err = core.GlobalController.RemovePolicy(id); err != nil { if err = core.GlobalController.RemovePolicy(id); err != nil {
log.Errorf("failed to delete policy %d: %v", id, err) log.Errorf("failed to delete policy %d: %v", id, err)
pa.CustomAbort(http.StatusInternalServerError, "") pa.SendInternalServerError(fmt.Errorf("failed to delete policy %d: %v", id, err))
return
} }
} }

View File

@ -24,6 +24,7 @@ import (
"strings" "strings"
"time" "time"
"errors"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
@ -110,13 +111,13 @@ type manifestResp struct {
func (ra *RepositoryAPI) Get() { func (ra *RepositoryAPI) Get() {
projectID, err := ra.GetInt64("project_id") projectID, err := ra.GetInt64("project_id")
if err != nil || projectID <= 0 { if err != nil || projectID <= 0 {
ra.HandleBadRequest(fmt.Sprintf("invalid project_id %s", ra.GetString("project_id"))) ra.SendBadRequestError(fmt.Errorf("invalid project_id %s", ra.GetString("project_id")))
return return
} }
labelID, err := ra.GetInt64("label_id", 0) labelID, err := ra.GetInt64("label_id", 0)
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id"))) ra.SendBadRequestError(fmt.Errorf("invalid label_id: %s", ra.GetString("label_id")))
return return
} }
@ -128,17 +129,17 @@ func (ra *RepositoryAPI) Get() {
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %d not found", projectID)) ra.SendNotFoundError(fmt.Errorf("project %d not found", projectID))
return return
} }
resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceRepository) resource := rbac.NewProjectNamespace(projectID).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) { if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
@ -147,19 +148,24 @@ func (ra *RepositoryAPI) Get() {
Name: ra.GetString("q"), Name: ra.GetString("q"),
LabelID: labelID, LabelID: labelID,
} }
query.Page, query.Size = ra.GetPaginationParams() query.Page, query.Size, err = ra.GetPaginationParams()
if err != nil {
ra.SendBadRequestError(err)
return
}
query.Sort = ra.GetString("sort") query.Sort = ra.GetString("sort")
total, err := dao.GetTotalOfRepositories(query) total, err := dao.GetTotalOfRepositories(query)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get total of repositories of project %d: %v", ra.SendInternalServerError(fmt.Errorf("failed to get total of repositories of project %d: %v",
projectID, err)) projectID, err))
return return
} }
repositories, err := getRepositories(query) repositories, err := getRepositories(query)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get repository: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to get repository: %v", err))
return return
} }
@ -240,25 +246,26 @@ func (ra *RepositoryAPI) Delete() {
} }
if project == nil { if project == nil {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return return
} }
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
resource := rbac.NewProjectNamespace(project.ProjectID).Resource(rbac.ResourceRepository) resource := rbac.NewProjectNamespace(project.ProjectID).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionDelete, resource) { if !ra.SecurityCtx.Can(rbac.ActionDelete, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
rc, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName) rc, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.SendInternalServerError(errors.New("internal error"))
return
} }
tags := []string{} tags := []string{}
@ -266,18 +273,13 @@ func (ra *RepositoryAPI) Delete() {
if len(tag) == 0 { if len(tag) == 0 {
tagList, err := rc.ListTag() tagList, err := rc.ListTag()
if err != nil { if err != nil {
log.Errorf("error occurred while listing tags of %s: %v", repoName, err) ra.ParseAndHandleError(fmt.Sprintf("error occurred while listing tags of %s", repoName), err)
return
if regErr, ok := err.(*commonhttp.Error); ok {
ra.CustomAbort(regErr.Code, regErr.Message)
}
ra.CustomAbort(http.StatusInternalServerError, "internal error")
} }
// TODO remove the logic if the bug of registry is fixed // TODO remove the logic if the bug of registry is fixed
if len(tagList) == 0 { if len(tagList) == 0 {
ra.HandleNotFound(fmt.Sprintf("no tags found for repository %s", repoName)) ra.SendNotFoundError(fmt.Errorf("no tags found for repository %s", repoName))
return return
} }
@ -289,7 +291,7 @@ func (ra *RepositoryAPI) Delete() {
if config.WithNotary() { if config.WithNotary() {
signedTags, err := getSignatures(ra.SecurityCtx.GetUsername(), repoName) signedTags, err := getSignatures(ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf( ra.SendInternalServerError(fmt.Errorf(
"failed to get signatures for repository %s: %v", repoName, err)) "failed to get signatures for repository %s: %v", repoName, err))
return return
} }
@ -298,12 +300,14 @@ func (ra *RepositoryAPI) Delete() {
digest, _, err := rc.ManifestExist(t) digest, _, err := rc.ManifestExist(t)
if err != nil { if err != nil {
log.Errorf("Failed to Check the digest of tag: %s, error: %v", t, err.Error()) log.Errorf("Failed to Check the digest of tag: %s, error: %v", t, err.Error())
ra.CustomAbort(http.StatusInternalServerError, err.Error()) ra.SendInternalServerError(err)
return
} }
log.Debugf("Tag: %s, digest: %s", t, digest) log.Debugf("Tag: %s, digest: %s", t, digest)
if _, ok := signedTags[digest]; ok { if _, ok := signedTags[digest]; ok {
log.Errorf("Found signed tag, repostory: %s, tag: %s, deletion will be canceled", repoName, t) log.Errorf("Found signed tag, repostory: %s, tag: %s, deletion will be canceled", repoName, t)
ra.CustomAbort(http.StatusPreconditionFailed, fmt.Sprintf("tag %s is signed", t)) ra.SendPreconditionFailedError(fmt.Errorf("tag %s is signed", t))
return
} }
} }
} }
@ -311,7 +315,7 @@ func (ra *RepositoryAPI) Delete() {
for _, t := range tags { for _, t := range tags {
image := fmt.Sprintf("%s:%s", repoName, t) image := fmt.Sprintf("%s:%s", repoName, t)
if err = dao.DeleteLabelsOfResource(common.ResourceTypeImage, image); err != nil { if err = dao.DeleteLabelsOfResource(common.ResourceTypeImage, image); err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to delete labels of image %s: %v", image, err)) ra.SendInternalServerError(fmt.Errorf("failed to delete labels of image %s: %v", image, err))
return return
} }
if err = rc.DeleteTag(t); err != nil { if err = rc.DeleteTag(t); err != nil {
@ -319,11 +323,9 @@ func (ra *RepositoryAPI) Delete() {
if regErr.Code == http.StatusNotFound { if regErr.Code == http.StatusNotFound {
continue continue
} }
log.Errorf("failed to delete tag %s: %v", t, err)
ra.CustomAbort(regErr.Code, regErr.Message)
} }
log.Errorf("error occurred while deleting tag %s:%s: %v", repoName, t, err) ra.ParseAndHandleError(fmt.Sprintf("failed to delete tag %s", t), err)
ra.CustomAbort(http.StatusInternalServerError, "internal error") return
} }
log.Infof("delete tag: %s:%s", repoName, t) log.Infof("delete tag: %s:%s", repoName, t)
@ -356,12 +358,13 @@ func (ra *RepositoryAPI) Delete() {
exist, err := repositoryExist(repoName, rc) exist, err := repositoryExist(repoName, rc)
if err != nil { if err != nil {
log.Errorf("failed to check the existence of repository %s: %v", repoName, err) log.Errorf("failed to check the existence of repository %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "") ra.SendInternalServerError(fmt.Errorf("failed to check the existence of repository %s: %v", repoName, err))
return
} }
if !exist { if !exist {
repository, err := dao.GetRepositoryByName(repoName) repository, err := dao.GetRepositoryByName(repoName)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get repository %s: %v", repoName, err)) ra.SendInternalServerError(fmt.Errorf("failed to get repository %s: %v", repoName, err))
return return
} }
if repository == nil { if repository == nil {
@ -371,13 +374,14 @@ func (ra *RepositoryAPI) Delete() {
if err = dao.DeleteLabelsOfResource(common.ResourceTypeRepository, if err = dao.DeleteLabelsOfResource(common.ResourceTypeRepository,
strconv.FormatInt(repository.RepositoryID, 10)); err != nil { strconv.FormatInt(repository.RepositoryID, 10)); err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to delete labels of repository %s: %v", ra.SendInternalServerError(fmt.Errorf("failed to delete labels of repository %s: %v",
repoName, err)) repoName, err))
return return
} }
if err = dao.DeleteRepository(repoName); err != nil { if err = dao.DeleteRepository(repoName); err != nil {
log.Errorf("failed to delete repository %s: %v", repoName, err) log.Errorf("failed to delete repository %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "") ra.SendInternalServerError(fmt.Errorf("failed to delete repository %s: %v", repoName, err))
return
} }
} }
} }
@ -388,38 +392,38 @@ func (ra *RepositoryAPI) GetTag() {
tag := ra.GetString(":tag") tag := ra.GetString(":tag")
exist, _, err := ra.checkExistence(repository, tag) exist, _, err := ra.checkExistence(repository, tag)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of resource, error: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to check the existence of resource, error: %v", err))
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("resource: %s:%s not found", repository, tag)) ra.SendNotFoundError(fmt.Errorf("resource: %s:%s not found", repository, tag))
return return
} }
project, _ := utils.ParseRepository(repository) project, _ := utils.ParseRepository(repository)
resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTag) resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTag)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) { if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repository) client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repository)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to initialize the client for %s: %v", ra.SendInternalServerError(fmt.Errorf("failed to initialize the client for %s: %v",
repository, err)) repository, err))
return return
} }
_, exist, err = client.ManifestExist(tag) _, exist, err = client.ManifestExist(tag)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of %s:%s: %v", repository, tag, err)) ra.SendInternalServerError(fmt.Errorf("failed to check the existence of %s:%s: %v", repository, tag, err))
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("%s not found", tag)) ra.SendNotFoundError(fmt.Errorf("%s not found", tag))
return return
} }
@ -432,38 +436,41 @@ func (ra *RepositoryAPI) GetTag() {
// Retag tags an existing image to another tag in this repo, the source image is specified by request body. // Retag tags an existing image to another tag in this repo, the source image is specified by request body.
func (ra *RepositoryAPI) Retag() { func (ra *RepositoryAPI) Retag() {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
project, repo := utils.ParseRepository(repoName) project, repo := utils.ParseRepository(repoName)
if !utils.ValidateRepo(repo) { if !utils.ValidateRepo(repo) {
ra.HandleBadRequest(fmt.Sprintf("invalid repo '%s'", repo)) ra.SendBadRequestError(fmt.Errorf("invalid repo '%s'", repo))
return return
} }
request := models.RetagRequest{} request := models.RetagRequest{}
ra.DecodeJSONReq(&request) if err := ra.DecodeJSONReq(&request); err != nil {
ra.SendBadRequestError(err)
return
}
srcImage, err := models.ParseImage(request.SrcImage) srcImage, err := models.ParseImage(request.SrcImage)
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid src image string '%s', should in format '<project>/<repo>:<tag>'", request.SrcImage)) ra.SendBadRequestError(fmt.Errorf("invalid src image string '%s', should in format '<project>/<repo>:<tag>'", request.SrcImage))
return return
} }
if !utils.ValidateTag(request.Tag) { if !utils.ValidateTag(request.Tag) {
ra.HandleBadRequest(fmt.Sprintf("invalid tag '%s'", request.Tag)) ra.SendBadRequestError(fmt.Errorf("invalid tag '%s'", request.Tag))
return return
} }
// Check whether source image exists // Check whether source image exists
exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag) exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("check existence of %s error: %v", request.SrcImage, err)) ra.SendInternalServerError(fmt.Errorf("check existence of %s error: %v", request.SrcImage, err))
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("image %s not exist", request.SrcImage)) ra.SendNotFoundError(fmt.Errorf("image %s not exist", request.SrcImage))
return return
} }
@ -474,7 +481,7 @@ func (ra *RepositoryAPI) Retag() {
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", project)) ra.SendNotFoundError(fmt.Errorf("project %s not found", project))
return return
} }
@ -482,11 +489,11 @@ func (ra *RepositoryAPI) Retag() {
if !request.Override { if !request.Override {
exist, _, err := ra.checkExistence(repoName, request.Tag) exist, _, err := ra.checkExistence(repoName, request.Tag)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("check existence of %s:%s error: %v", repoName, request.Tag, err)) ra.SendInternalServerError(fmt.Errorf("check existence of %s:%s error: %v", repoName, request.Tag, err))
return return
} }
if exist { if exist {
ra.HandleConflict(fmt.Sprintf("tag '%s' already existed for '%s'", request.Tag, repoName)) ra.SendConflictError(fmt.Errorf("tag '%s' already existed for '%s'", request.Tag, repoName))
return return
} }
} }
@ -495,7 +502,7 @@ func (ra *RepositoryAPI) Retag() {
srcResource := rbac.NewProjectNamespace(srcImage.Project).Resource(rbac.ResourceRepository) srcResource := rbac.NewProjectNamespace(srcImage.Project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionPull, srcResource) { if !ra.SecurityCtx.Can(rbac.ActionPull, srcResource) {
log.Errorf("user has no read permission to project '%s'", srcImage.Project) log.Errorf("user has no read permission to project '%s'", srcImage.Project)
ra.HandleForbidden(fmt.Sprintf("%s has no read permission to project %s", ra.SecurityCtx.GetUsername(), srcImage.Project)) ra.SendForbiddenError(fmt.Errorf("%s has no read permission to project %s", ra.SecurityCtx.GetUsername(), srcImage.Project))
return return
} }
@ -503,7 +510,7 @@ func (ra *RepositoryAPI) Retag() {
destResource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository) destResource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionPush, destResource) { if !ra.SecurityCtx.Can(rbac.ActionPush, destResource) {
log.Errorf("user has no write permission to project '%s'", project) log.Errorf("user has no write permission to project '%s'", project)
ra.HandleForbidden(fmt.Sprintf("%s has no write permission to project %s", ra.SecurityCtx.GetUsername(), project)) ra.SendForbiddenError(fmt.Errorf("%s has no write permission to project %s", ra.SecurityCtx.GetUsername(), project))
return return
} }
@ -513,7 +520,7 @@ func (ra *RepositoryAPI) Retag() {
Repo: repo, Repo: repo,
Tag: request.Tag, Tag: request.Tag,
}); err != nil { }); err != nil {
ra.HandleInternalServerError(fmt.Sprintf("%v", err)) ra.SendInternalServerError(fmt.Errorf("%v", err))
} }
} }
@ -522,7 +529,7 @@ func (ra *RepositoryAPI) GetTags() {
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
labelID, err := ra.GetInt64("label_id", 0) labelID, err := ra.GetInt64("label_id", 0)
if err != nil { if err != nil {
ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id"))) ra.SendBadRequestError(fmt.Errorf("invalid label_id: %s", ra.GetString("label_id")))
return return
} }
@ -535,29 +542,30 @@ func (ra *RepositoryAPI) GetTags() {
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return return
} }
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTag) resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTag)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) { if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName) client, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.SendInternalServerError(errors.New("internal error"))
return
} }
tags, err := client.ListTag() tags, err := client.ListTag()
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get tag of %s: %v", repoName, err)) ra.SendInternalServerError(fmt.Errorf("failed to get tag of %s: %v", repoName, err))
return return
} }
@ -568,7 +576,7 @@ func (ra *RepositoryAPI) GetTags() {
ResourceType: common.ResourceTypeImage, ResourceType: common.ResourceTypeImage,
}) })
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to list resource labels: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to list resource labels: %v", err))
return return
} }
labeledTags := map[string]struct{}{} labeledTags := map[string]struct{}{}
@ -731,7 +739,7 @@ func (ra *RepositoryAPI) GetManifests() {
} }
if version != "v1" && version != "v2" { if version != "v1" && version != "v2" {
ra.HandleBadRequest("version should be v1 or v2") ra.SendBadRequestError(errors.New("version should be v1 or v2"))
return return
} }
@ -744,36 +752,32 @@ func (ra *RepositoryAPI) GetManifests() {
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return return
} }
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagManifest) resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagManifest)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) { if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
rc, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName) rc, err := coreutils.NewRepositoryClientForUI(ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.SendInternalServerError(errors.New("internal error"))
return
} }
manifest, err := getManifest(rc, tag, version) manifest, err := getManifest(rc, tag, version)
if err != nil { if err != nil {
log.Errorf("error occurred while getting manifest of %s:%s: %v", repoName, tag, err) ra.ParseAndHandleError(fmt.Sprintf("error occurred while getting manifest of %s:%s", repoName, tag), err)
return
if regErr, ok := err.(*commonhttp.Error); ok {
ra.CustomAbort(regErr.Code, regErr.Message)
}
ra.CustomAbort(http.StatusInternalServerError, "internal error")
} }
ra.Data["json"] = manifest ra.Data["json"] = manifest
@ -826,7 +830,7 @@ func getManifest(client *registry.Repository,
func (ra *RepositoryAPI) GetTopRepos() { func (ra *RepositoryAPI) GetTopRepos() {
count, err := ra.GetInt("count", 10) count, err := ra.GetInt("count", 10)
if err != nil || count <= 0 { if err != nil || count <= 0 {
ra.HandleBadRequest(fmt.Sprintf("invalid count: %s", ra.GetString("count"))) ra.SendBadRequestError(fmt.Errorf("invalid count: %s", ra.GetString("count")))
return return
} }
@ -839,7 +843,7 @@ func (ra *RepositoryAPI) GetTopRepos() {
if ra.SecurityCtx.IsAuthenticated() { if ra.SecurityCtx.IsAuthenticated() {
list, err := ra.SecurityCtx.GetMyProjects() list, err := ra.SecurityCtx.GetMyProjects()
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get projects which the user %s is a member of: %v", ra.SendInternalServerError(fmt.Errorf("failed to get projects which the user %s is a member of: %v",
ra.SecurityCtx.GetUsername(), err)) ra.SecurityCtx.GetUsername(), err))
return return
} }
@ -853,7 +857,8 @@ func (ra *RepositoryAPI) GetTopRepos() {
repos, err := dao.GetTopRepos(projectIDs, count) repos, err := dao.GetTopRepos(projectIDs, count)
if err != nil { if err != nil {
log.Errorf("failed to get top repos: %v", err) log.Errorf("failed to get top repos: %v", err)
ra.CustomAbort(http.StatusInternalServerError, "internal server error") ra.SendInternalServerError(errors.New("internal server error"))
return
} }
ra.Data["json"] = assembleReposInParallel(repos) ra.Data["json"] = assembleReposInParallel(repos)
@ -865,35 +870,38 @@ func (ra *RepositoryAPI) Put() {
name := ra.GetString(":splat") name := ra.GetString(":splat")
repository, err := dao.GetRepositoryByName(name) repository, err := dao.GetRepositoryByName(name)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get repository %s: %v", name, err)) ra.SendInternalServerError(fmt.Errorf("failed to get repository %s: %v", name, err))
return return
} }
if repository == nil { if repository == nil {
ra.HandleNotFound(fmt.Sprintf("repository %s not found", name)) ra.SendNotFoundError(fmt.Errorf("repository %s not found", name))
return return
} }
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
project, _ := utils.ParseRepository(name) project, _ := utils.ParseRepository(name)
resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository) resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionUpdate, resource) { if !ra.SecurityCtx.Can(rbac.ActionUpdate, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
desc := struct { desc := struct {
Description string `json:"description"` Description string `json:"description"`
}{} }{}
ra.DecodeJSONReq(&desc) if err := ra.DecodeJSONReq(&desc); err != nil {
ra.SendBadRequestError(err)
return
}
repository.Description = desc.Description repository.Description = desc.Description
if err = dao.UpdateRepository(*repository); err != nil { if err = dao.UpdateRepository(*repository); err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to update repository %s: %v", name, err)) ra.SendInternalServerError(fmt.Errorf("failed to update repository %s: %v", name, err))
return return
} }
} }
@ -911,17 +919,17 @@ func (ra *RepositoryAPI) GetSignatures() {
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return return
} }
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepository) resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepository)
if !ra.SecurityCtx.Can(rbac.ActionRead, resource) { if !ra.SecurityCtx.Can(rbac.ActionRead, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
@ -929,7 +937,8 @@ func (ra *RepositoryAPI) GetSignatures() {
ra.SecurityCtx.GetUsername(), repoName) ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {
log.Errorf("Error while fetching signature from notary: %v", err) log.Errorf("Error while fetching signature from notary: %v", err)
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.SendInternalServerError(errors.New("internal error"))
return
} }
ra.Data["json"] = targets ra.Data["json"] = targets
ra.ServeJSON() ra.ServeJSON()
@ -939,7 +948,7 @@ func (ra *RepositoryAPI) GetSignatures() {
func (ra *RepositoryAPI) ScanImage() { func (ra *RepositoryAPI) ScanImage() {
if !config.WithClair() { if !config.WithClair() {
log.Warningf("Harbor is not deployed with Clair, scan is disabled.") log.Warningf("Harbor is not deployed with Clair, scan is disabled.")
ra.RenderError(http.StatusServiceUnavailable, "") ra.SendInternalServerError(errors.New("harbor is not deployed with Clair, scan is disabled"))
return return
} }
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
@ -952,23 +961,23 @@ func (ra *RepositoryAPI) ScanImage() {
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName)) ra.SendNotFoundError(fmt.Errorf("project %s not found", projectName))
return return
} }
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob) resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob)
if !ra.SecurityCtx.Can(rbac.ActionCreate, resource) { if !ra.SecurityCtx.Can(rbac.ActionCreate, resource) {
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
err = coreutils.TriggerImageScan(repoName, tag) err = coreutils.TriggerImageScan(repoName, tag)
if err != nil { if err != nil {
log.Errorf("Error while calling job service to trigger image scan: %v", err) log.Errorf("Error while calling job service to trigger image scan: %v", err)
ra.HandleInternalServerError("Failed to scan image, please check log for details") ra.SendInternalServerError(errors.New("Failed to scan image, please check log for details"))
return return
} }
} }
@ -977,18 +986,18 @@ func (ra *RepositoryAPI) ScanImage() {
func (ra *RepositoryAPI) VulnerabilityDetails() { func (ra *RepositoryAPI) VulnerabilityDetails() {
if !config.WithClair() { if !config.WithClair() {
log.Warningf("Harbor is not deployed with Clair, it's not impossible to get vulnerability details.") log.Warningf("Harbor is not deployed with Clair, it's not impossible to get vulnerability details.")
ra.RenderError(http.StatusServiceUnavailable, "") ra.SendInternalServerError(errors.New("harbor is not deployed with Clair, it's not impossible to get vulnerability details"))
return return
} }
repository := ra.GetString(":splat") repository := ra.GetString(":splat")
tag := ra.GetString(":tag") tag := ra.GetString(":tag")
exist, digest, err := ra.checkExistence(repository, tag) exist, digest, err := ra.checkExistence(repository, tag)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of resource, error: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to check the existence of resource, error: %v", err))
return return
} }
if !exist { if !exist {
ra.HandleNotFound(fmt.Sprintf("resource: %s:%s not found", repository, tag)) ra.SendNotFoundError(fmt.Errorf("resource: %s:%s not found", repository, tag))
return return
} }
project, _ := utils.ParseRepository(repository) project, _ := utils.ParseRepository(repository)
@ -996,16 +1005,16 @@ func (ra *RepositoryAPI) VulnerabilityDetails() {
resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTagVulnerability) resource := rbac.NewProjectNamespace(project).Resource(rbac.ResourceRepositoryTagVulnerability)
if !ra.SecurityCtx.Can(rbac.ActionList, resource) { if !ra.SecurityCtx.Can(rbac.ActionList, resource) {
if !ra.SecurityCtx.IsAuthenticated() { if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized() ra.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
ra.HandleForbidden(ra.SecurityCtx.GetUsername()) ra.SendForbiddenError(errors.New(ra.SecurityCtx.GetUsername()))
return return
} }
res := []*models.VulnerabilityItem{} res := []*models.VulnerabilityItem{}
overview, err := dao.GetImgScanOverview(digest) overview, err := dao.GetImgScanOverview(digest)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get the scan overview, error: %v", err)) ra.SendInternalServerError(fmt.Errorf("failed to get the scan overview, error: %v", err))
return return
} }
if overview != nil && len(overview.DetailsKey) > 0 { if overview != nil && len(overview.DetailsKey) > 0 {
@ -1013,7 +1022,7 @@ func (ra *RepositoryAPI) VulnerabilityDetails() {
log.Debugf("The key for getting details: %s", overview.DetailsKey) log.Debugf("The key for getting details: %s", overview.DetailsKey)
details, err := clairClient.GetResult(overview.DetailsKey) details, err := clairClient.GetResult(overview.DetailsKey)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("Failed to get scan details from Clair, error: %v", err)) ra.SendInternalServerError(fmt.Errorf("Failed to get scan details from Clair, error: %v", err))
return return
} }
res = transformVulnerabilities(details) res = transformVulnerabilities(details)

View File

@ -114,7 +114,10 @@ func (r *RepositoryLabelAPI) isValidLabelReq() bool {
} }
l := &models.Label{} l := &models.Label{}
r.DecodeJSONReq(l) if err := r.DecodeJSONReq(l); err != nil {
r.SendBadRequestError(err)
return false
}
label, ok := r.validate(l.ID, p.ProjectID) label, ok := r.validate(l.ID, p.ProjectID)
if !ok { if !ok {

View File

@ -15,6 +15,7 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
@ -41,7 +42,7 @@ func (r *RobotAPI) Prepare() {
method := r.Ctx.Request.Method method := r.Ctx.Request.Method
if !r.SecurityCtx.IsAuthenticated() { if !r.SecurityCtx.IsAuthenticated() {
r.HandleUnauthorized() r.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
@ -53,7 +54,7 @@ func (r *RobotAPI) Prepare() {
} else { } else {
errMsg = "invalid project ID: " + fmt.Sprintf("%d", pid) errMsg = "invalid project ID: " + fmt.Sprintf("%d", pid)
} }
r.HandleBadRequest(errMsg) r.SendBadRequestError(errors.New(errMsg))
return return
} }
project, err := r.ProjectMgr.Get(pid) project, err := r.ProjectMgr.Get(pid)
@ -62,7 +63,7 @@ func (r *RobotAPI) Prepare() {
return return
} }
if project == nil { if project == nil {
r.HandleNotFound(fmt.Sprintf("project %d not found", pid)) r.SendNotFoundError(fmt.Errorf("project %d not found", pid))
return return
} }
r.project = project r.project = project
@ -70,18 +71,18 @@ func (r *RobotAPI) Prepare() {
if method == http.MethodPut || method == http.MethodDelete { if method == http.MethodPut || method == http.MethodDelete {
id, err := r.GetInt64FromPath(":id") id, err := r.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
r.HandleBadRequest("invalid robot ID") r.SendBadRequestError(errors.New("invalid robot ID"))
return return
} }
robot, err := dao.GetRobotByID(id) robot, err := dao.GetRobotByID(id)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to get robot %d: %v", id, err)) r.SendInternalServerError(fmt.Errorf("failed to get robot %d: %v", id, err))
return return
} }
if robot == nil { if robot == nil {
r.HandleNotFound(fmt.Sprintf("robot %d not found", id)) r.SendNotFoundError(fmt.Errorf("robot %d not found", id))
return return
} }
@ -92,7 +93,7 @@ func (r *RobotAPI) Prepare() {
func (r *RobotAPI) requireAccess(action rbac.Action) bool { func (r *RobotAPI) requireAccess(action rbac.Action) bool {
resource := rbac.NewProjectNamespace(r.project.ProjectID).Resource(rbac.ResourceRobot) resource := rbac.NewProjectNamespace(r.project.ProjectID).Resource(rbac.ResourceRobot)
if !r.SecurityCtx.Can(action, resource) { if !r.SecurityCtx.Can(action, resource) {
r.HandleForbidden(r.SecurityCtx.GetUsername()) r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
return false return false
} }
@ -106,10 +107,14 @@ func (r *RobotAPI) Post() {
} }
var robotReq models.RobotReq var robotReq models.RobotReq
if err := r.DecodeJSONReq(&robotReq); err != nil {
r.SendBadRequestError(err)
return
}
// Token duration in minutes // Token duration in minutes
tokenDuration := time.Duration(config.RobotTokenDuration()) * time.Minute tokenDuration := time.Duration(config.RobotTokenDuration()) * time.Minute
expiresAt := time.Now().UTC().Add(tokenDuration).Unix() expiresAt := time.Now().UTC().Add(tokenDuration).Unix()
r.DecodeJSONReq(&robotReq)
createdName := common.RobotPrefix + robotReq.Name createdName := common.RobotPrefix + robotReq.Name
// first to add a robot account, and get its id. // first to add a robot account, and get its id.
@ -122,10 +127,10 @@ func (r *RobotAPI) Post() {
id, err := dao.AddRobot(&robot) id, err := dao.AddRobot(&robot)
if err != nil { if err != nil {
if err == dao.ErrDupRows { if err == dao.ErrDupRows {
r.HandleConflict() r.SendConflictError(errors.New("conflict robot account"))
return return
} }
r.HandleInternalServerError(fmt.Sprintf("failed to create robot account: %v", err)) r.SendInternalServerError(fmt.Errorf("failed to create robot account: %v", err))
return return
} }
@ -133,20 +138,20 @@ func (r *RobotAPI) Post() {
// token is not stored in the database. // token is not stored in the database.
jwtToken, err := token.New(id, r.project.ProjectID, expiresAt, robotReq.Access) jwtToken, err := token.New(id, r.project.ProjectID, expiresAt, robotReq.Access)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to valid parameters to generate token for robot account, %v", err)) r.SendInternalServerError(fmt.Errorf("failed to valid parameters to generate token for robot account, %v", err))
err := dao.DeleteRobot(id) err := dao.DeleteRobot(id)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to delete the robot account: %d, %v", id, err)) r.SendInternalServerError(fmt.Errorf("failed to delete the robot account: %d, %v", id, err))
} }
return return
} }
rawTk, err := jwtToken.Raw() rawTk, err := jwtToken.Raw()
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to sign token for robot account, %v", err)) r.SendInternalServerError(fmt.Errorf("failed to sign token for robot account, %v", err))
err := dao.DeleteRobot(id) err := dao.DeleteRobot(id)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to delete the robot account: %d, %v", id, err)) r.SendInternalServerError(fmt.Errorf("failed to delete the robot account: %d, %v", id, err))
} }
return return
} }
@ -172,14 +177,18 @@ func (r *RobotAPI) List() {
count, err := dao.CountRobot(&query) count, err := dao.CountRobot(&query)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to list robots on project: %d, %v", r.project.ProjectID, err)) r.SendInternalServerError(fmt.Errorf("failed to list robots on project: %d, %v", r.project.ProjectID, err))
return
}
query.Page, query.Size, err = r.GetPaginationParams()
if err != nil {
r.SendBadRequestError(err)
return return
} }
query.Page, query.Size = r.GetPaginationParams()
robots, err := dao.ListRobots(&query) robots, err := dao.ListRobots(&query)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to get robots %v", err)) r.SendInternalServerError(fmt.Errorf("failed to get robots %v", err))
return return
} }
@ -196,17 +205,17 @@ func (r *RobotAPI) Get() {
id, err := r.GetInt64FromPath(":id") id, err := r.GetInt64FromPath(":id")
if err != nil || id <= 0 { if err != nil || id <= 0 {
r.HandleBadRequest(fmt.Sprintf("invalid robot ID: %s", r.GetStringFromPath(":id"))) r.SendBadRequestError(fmt.Errorf("invalid robot ID: %s", r.GetStringFromPath(":id")))
return return
} }
robot, err := dao.GetRobotByID(id) robot, err := dao.GetRobotByID(id)
if err != nil { if err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to get robot %d: %v", id, err)) r.SendInternalServerError(fmt.Errorf("failed to get robot %d: %v", id, err))
return return
} }
if robot == nil { if robot == nil {
r.HandleNotFound(fmt.Sprintf("robot %d not found", id)) r.SendNotFoundError(fmt.Errorf("robot %d not found", id))
return return
} }
@ -221,11 +230,16 @@ func (r *RobotAPI) Put() {
} }
var robotReq models.RobotReq var robotReq models.RobotReq
r.DecodeJSONReqAndValidate(&robotReq) isValid, err := r.DecodeJSONReqAndValidate(&robotReq)
if !isValid {
r.SendBadRequestError(err)
return
}
r.robot.Disabled = robotReq.Disabled r.robot.Disabled = robotReq.Disabled
if err := dao.UpdateRobot(r.robot); err != nil { if err := dao.UpdateRobot(r.robot); err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to update robot %d: %v", r.robot.ID, err)) r.SendInternalServerError(fmt.Errorf("failed to update robot %d: %v", r.robot.ID, err))
return return
} }
@ -238,7 +252,7 @@ func (r *RobotAPI) Delete() {
} }
if err := dao.DeleteRobot(r.robot.ID); err != nil { if err := dao.DeleteRobot(r.robot.ID); err != nil {
r.HandleInternalServerError(fmt.Sprintf("failed to delete robot %d: %v", r.robot.ID, err)) r.SendInternalServerError(fmt.Errorf("failed to delete robot %d: %v", r.robot.ID, err))
return return
} }
} }

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"errors"
"net/http" "net/http"
"strconv" "strconv"
@ -20,15 +21,15 @@ func (sc *ScanAllAPI) Prepare() {
sc.BaseController.Prepare() sc.BaseController.Prepare()
if !config.WithClair() { if !config.WithClair() {
log.Warningf("Harbor is not deployed with Clair, it's not possible to scan images.") log.Warningf("Harbor is not deployed with Clair, it's not possible to scan images.")
sc.RenderError(http.StatusServiceUnavailable, "") sc.SendStatusServiceUnavailableError(errors.New(""))
return return
} }
if !sc.SecurityCtx.IsAuthenticated() { if !sc.SecurityCtx.IsAuthenticated() {
sc.HandleUnauthorized() sc.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if !sc.SecurityCtx.IsSysAdmin() { if !sc.SecurityCtx.IsSysAdmin() {
sc.HandleForbidden(sc.SecurityCtx.GetUsername()) sc.SendForbiddenError(errors.New(sc.SecurityCtx.GetUsername()))
return return
} }
} }
@ -49,7 +50,11 @@ func (sc *ScanAllAPI) Prepare() {
// } // }
func (sc *ScanAllAPI) Post() { func (sc *ScanAllAPI) Post() {
ajr := models.AdminJobReq{} ajr := models.AdminJobReq{}
sc.DecodeJSONReqAndValidate(&ajr) isValid, err := sc.DecodeJSONReqAndValidate(&ajr)
if !isValid {
sc.SendBadRequestError(err)
return
}
ajr.Name = common_job.ImageScanAllJob ajr.Name = common_job.ImageScanAllJob
sc.submit(&ajr) sc.submit(&ajr)
sc.Redirect(http.StatusCreated, strconv.FormatInt(ajr.ID, 10)) sc.Redirect(http.StatusCreated, strconv.FormatInt(ajr.ID, 10))
@ -65,7 +70,11 @@ func (sc *ScanAllAPI) Post() {
// } // }
func (sc *ScanAllAPI) Put() { func (sc *ScanAllAPI) Put() {
ajr := models.AdminJobReq{} ajr := models.AdminJobReq{}
sc.DecodeJSONReqAndValidate(&ajr) isValid, err := sc.DecodeJSONReqAndValidate(&ajr)
if !isValid {
sc.SendBadRequestError(err)
return
}
ajr.Name = common_job.ImageScanAllJob ajr.Name = common_job.ImageScanAllJob
sc.updateSchedule(ajr) sc.updateSchedule(ajr)
} }

View File

@ -16,11 +16,11 @@ package api
import ( import (
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
common_http "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/utils" "github.com/goharbor/harbor/src/core/utils"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -39,12 +39,12 @@ type ScanJobAPI struct {
func (sj *ScanJobAPI) Prepare() { func (sj *ScanJobAPI) Prepare() {
sj.BaseController.Prepare() sj.BaseController.Prepare()
if !sj.SecurityCtx.IsAuthenticated() { if !sj.SecurityCtx.IsAuthenticated() {
sj.HandleUnauthorized() sj.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
id, err := sj.GetInt64FromPath(":id") id, err := sj.GetInt64FromPath(":id")
if err != nil { if err != nil {
sj.HandleBadRequest("invalid ID") sj.SendBadRequestError(errors.New("invalid ID"))
return return
} }
sj.jobID = id sj.jobID = id
@ -52,14 +52,16 @@ func (sj *ScanJobAPI) Prepare() {
data, err := dao.GetScanJob(id) data, err := dao.GetScanJob(id)
if err != nil { if err != nil {
log.Errorf("Failed to load job data for job: %d, error: %v", id, err) log.Errorf("Failed to load job data for job: %d, error: %v", id, err)
sj.CustomAbort(http.StatusInternalServerError, "Failed to get Job data") sj.SendInternalServerError(errors.New("Failed to get Job data"))
return
} }
projectName := strings.SplitN(data.Repository, "/", 2)[0] projectName := strings.SplitN(data.Repository, "/", 2)[0]
resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob) resource := rbac.NewProjectNamespace(projectName).Resource(rbac.ResourceRepositoryTagScanJob)
if !sj.SecurityCtx.Can(rbac.ActionRead, resource) { if !sj.SecurityCtx.Can(rbac.ActionRead, resource) {
log.Errorf("User does not have read permission for project: %s", projectName) log.Errorf("User does not have read permission for project: %s", projectName)
sj.HandleForbidden(sj.SecurityCtx.GetUsername()) sj.SendForbiddenError(errors.New(sj.SecurityCtx.GetUsername()))
return
} }
sj.projectName = projectName sj.projectName = projectName
sj.jobUUID = data.UUID sj.jobUUID = data.UUID
@ -69,20 +71,14 @@ func (sj *ScanJobAPI) Prepare() {
func (sj *ScanJobAPI) GetLog() { func (sj *ScanJobAPI) GetLog() {
logBytes, err := utils.GetJobServiceClient().GetJobLog(sj.jobUUID) logBytes, err := utils.GetJobServiceClient().GetJobLog(sj.jobUUID)
if err != nil { if err != nil {
if httpErr, ok := err.(*common_http.Error); ok { sj.ParseAndHandleError(fmt.Sprintf("Failed to get job logs, uuid: %s, error: %v", sj.jobUUID, err), err)
sj.RenderError(httpErr.Code, "")
log.Errorf(fmt.Sprintf("failed to get log of job %d: %d %s",
sj.jobID, httpErr.Code, httpErr.Message))
return
}
sj.HandleInternalServerError(fmt.Sprintf("Failed to get job logs, uuid: %s, error: %v", sj.jobUUID, err))
return return
} }
sj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes))) sj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes)))
sj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain") sj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain")
_, err = sj.Ctx.ResponseWriter.Write(logBytes) _, err = sj.Ctx.ResponseWriter.Write(logBytes)
if err != nil { if err != nil {
sj.HandleInternalServerError(fmt.Sprintf("Failed to write job logs, uuid: %s, error: %v", sj.jobUUID, err)) sj.SendInternalServerError(fmt.Errorf("Failed to write job logs, uuid: %s, error: %v", sj.jobUUID, err))
} }
} }

View File

@ -16,7 +16,6 @@ package api
import ( import (
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
@ -69,7 +68,7 @@ func (s *SearchAPI) Get() {
if isAuthenticated { if isAuthenticated {
mys, err := s.SecurityCtx.GetMyProjects() mys, err := s.SecurityCtx.GetMyProjects()
if err != nil { if err != nil {
s.HandleInternalServerError(fmt.Sprintf( s.SendInternalServerError(fmt.Errorf(
"failed to get projects: %v", err)) "failed to get projects: %v", err))
return return
} }
@ -111,7 +110,8 @@ func (s *SearchAPI) Get() {
}) })
if err != nil { if err != nil {
log.Errorf("failed to get total of repositories of project %d: %v", p.ProjectID, err) log.Errorf("failed to get total of repositories of project %d: %v", p.ProjectID, err)
s.CustomAbort(http.StatusInternalServerError, "") s.SendInternalServerError(fmt.Errorf("failed to get total of repositories of project %d: %v", p.ProjectID, err))
return
} }
p.RepoCount = total p.RepoCount = total
@ -122,7 +122,8 @@ func (s *SearchAPI) Get() {
repositoryResult, err := filterRepositories(projects, keyword) repositoryResult, err := filterRepositories(projects, keyword)
if err != nil { if err != nil {
log.Errorf("failed to filter repositories: %v", err) log.Errorf("failed to filter repositories: %v", err)
s.CustomAbort(http.StatusInternalServerError, "") s.SendInternalServerError(fmt.Errorf("failed to filter repositories: %v", err))
return
} }
result := &searchResult{ result := &searchResult{
@ -139,7 +140,9 @@ func (s *SearchAPI) Get() {
chartResults, err := searchHandler(keyword, proNames) chartResults, err := searchHandler(keyword, proNames)
if err != nil { if err != nil {
log.Errorf("failed to filter charts: %v", err) log.Errorf("failed to filter charts: %v", err)
s.CustomAbort(http.StatusInternalServerError, err.Error()) s.SendInternalServerError(err)
return
} }
result.Chart = &chartResults result.Chart = &chartResults

View File

@ -15,8 +15,8 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"net/http"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
@ -48,7 +48,7 @@ type StatisticAPI struct {
func (s *StatisticAPI) Prepare() { func (s *StatisticAPI) Prepare() {
s.BaseController.Prepare() s.BaseController.Prepare()
if !s.SecurityCtx.IsAuthenticated() { if !s.SecurityCtx.IsAuthenticated() {
s.HandleUnauthorized() s.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
s.username = s.SecurityCtx.GetUsername() s.username = s.SecurityCtx.GetUsername()
@ -76,7 +76,8 @@ func (s *StatisticAPI) Get() {
}) })
if err != nil { if err != nil {
log.Errorf("failed to get total of public repositories: %v", err) log.Errorf("failed to get total of public repositories: %v", err)
s.CustomAbort(http.StatusInternalServerError, "") s.SendInternalServerError(fmt.Errorf("failed to get total of public repositories: %v", err))
return
} }
statistic[PubRC] = n statistic[PubRC] = n
} }
@ -85,7 +86,8 @@ func (s *StatisticAPI) Get() {
result, err := s.ProjectMgr.List(nil) result, err := s.ProjectMgr.List(nil)
if err != nil { if err != nil {
log.Errorf("failed to get total of projects: %v", err) log.Errorf("failed to get total of projects: %v", err)
s.CustomAbort(http.StatusInternalServerError, "") s.SendInternalServerError(fmt.Errorf("failed to get total of projects: %v", err))
return
} }
statistic[TPC] = result.Total statistic[TPC] = result.Total
statistic[PriPC] = result.Total - statistic[PubPC] statistic[PriPC] = result.Total - statistic[PubPC]
@ -93,7 +95,8 @@ func (s *StatisticAPI) Get() {
n, err := dao.GetTotalOfRepositories() n, err := dao.GetTotalOfRepositories()
if err != nil { if err != nil {
log.Errorf("failed to get total of repositories: %v", err) log.Errorf("failed to get total of repositories: %v", err)
s.CustomAbort(http.StatusInternalServerError, "") s.SendInternalServerError(fmt.Errorf("failed to get total of repositories: %v", err))
return
} }
statistic[TRC] = n statistic[TRC] = n
statistic[PriRC] = n - statistic[PubRC] statistic[PriRC] = n - statistic[PubRC]
@ -124,7 +127,7 @@ func (s *StatisticAPI) Get() {
ProjectIDs: ids, ProjectIDs: ids,
}) })
if err != nil { if err != nil {
s.HandleInternalServerError(fmt.Sprintf( s.SendInternalServerError(fmt.Errorf(
"failed to get total of repositories for user %s: %v", "failed to get total of repositories for user %s: %v",
s.username, err)) s.username, err))
return return

View File

@ -15,12 +15,14 @@
package api package api
import ( import (
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"sync" "sync"
"fmt"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
clairdao "github.com/goharbor/harbor/src/common/dao/clair" clairdao "github.com/goharbor/harbor/src/common/dao/clair"
@ -105,28 +107,24 @@ type GeneralInfo struct {
WithChartMuseum bool `json:"with_chartmuseum"` WithChartMuseum bool `json:"with_chartmuseum"`
} }
// validate for validating user if an admin. // GetVolumeInfo gets specific volume storage info.
func (sia *SystemInfoAPI) validate() { func (sia *SystemInfoAPI) GetVolumeInfo() {
if !sia.SecurityCtx.IsAuthenticated() { if !sia.SecurityCtx.IsAuthenticated() {
sia.HandleUnauthorized() sia.SendUnAuthorizedError(errors.New("UnAuthorized"))
sia.StopRun() return
} }
if !sia.SecurityCtx.IsSysAdmin() { if !sia.SecurityCtx.IsSysAdmin() {
sia.HandleForbidden(sia.SecurityCtx.GetUsername()) sia.SendForbiddenError(errors.New(sia.SecurityCtx.GetUsername()))
sia.StopRun() return
} }
}
// GetVolumeInfo gets specific volume storage info.
func (sia *SystemInfoAPI) GetVolumeInfo() {
sia.validate()
systeminfo.Init() systeminfo.Init()
capacity, err := imagestorage.GlobalDriver.Cap() capacity, err := imagestorage.GlobalDriver.Cap()
if err != nil { if err != nil {
log.Errorf("failed to get capacity: %v", err) log.Errorf("failed to get capacity: %v", err)
sia.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) sia.SendInternalServerError(fmt.Errorf("failed to get capacity: %v", err))
return
} }
systemInfo := SystemInfo{ systemInfo := SystemInfo{
HarborStorage: Storage{ HarborStorage: Storage{
@ -147,10 +145,12 @@ func (sia *SystemInfoAPI) GetCert() {
http.ServeFile(sia.Ctx.ResponseWriter, sia.Ctx.Request, defaultRootCert) http.ServeFile(sia.Ctx.ResponseWriter, sia.Ctx.Request, defaultRootCert)
} else if os.IsNotExist(err) { } else if os.IsNotExist(err) {
log.Error("No certificate found.") log.Error("No certificate found.")
sia.CustomAbort(http.StatusNotFound, "No certificate found.") sia.SendNotFoundError(errors.New("no certificate found"))
return
} else { } else {
log.Errorf("Unexpected error: %v", err) log.Errorf("Unexpected error: %v", err)
sia.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) sia.SendInternalServerError(fmt.Errorf("Unexpected error: %v", err))
return
} }
} }
@ -159,7 +159,8 @@ func (sia *SystemInfoAPI) GetGeneralInfo() {
cfg, err := config.GetSystemCfg() cfg, err := config.GetSystemCfg()
if err != nil { if err != nil {
log.Errorf("Error occurred getting config: %v", err) log.Errorf("Error occurred getting config: %v", err)
sia.CustomAbort(http.StatusInternalServerError, "Unexpected error") sia.SendInternalServerError(fmt.Errorf("Unexpected error: %v", err))
return
} }
var registryURL string var registryURL string
if l := strings.Split(cfg[common.ExtEndpoint].(string), "://"); len(l) > 1 { if l := strings.Split(cfg[common.ExtEndpoint].(string), "://"); len(l) > 1 {

View File

@ -15,6 +15,7 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -38,12 +39,12 @@ type TargetAPI struct {
func (t *TargetAPI) Prepare() { func (t *TargetAPI) Prepare() {
t.BaseController.Prepare() t.BaseController.Prepare()
if !t.SecurityCtx.IsAuthenticated() { if !t.SecurityCtx.IsAuthenticated() {
t.HandleUnauthorized() t.SendUnAuthorizedError(errors.New("UnAuthorized"))
return return
} }
if !t.SecurityCtx.IsSysAdmin() { if !t.SecurityCtx.IsSysAdmin() {
t.HandleForbidden(t.SecurityCtx.GetUsername()) t.SendForbiddenError(errors.New(t.SecurityCtx.GetUsername()))
return return
} }
@ -51,7 +52,8 @@ func (t *TargetAPI) Prepare() {
t.secretKey, err = config.SecretKey() t.secretKey, err = config.SecretKey()
if err != nil { if err != nil {
log.Errorf("failed to get secret key: %v", err) log.Errorf("failed to get secret key: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
@ -64,7 +66,7 @@ func (t *TargetAPI) ping(endpoint, username, password string, insecure bool) {
if err != nil { if err != nil {
log.Errorf("failed to ping target: %v", err) log.Errorf("failed to ping target: %v", err)
// do not return any detail information of the error, or may cause SSRF security issue #3755 // do not return any detail information of the error, or may cause SSRF security issue #3755
t.RenderError(http.StatusBadRequest, "failed to ping target") t.SendConflictError(errors.New("failed to ping target"))
return return
} }
} }
@ -78,24 +80,27 @@ func (t *TargetAPI) Ping() {
Password *string `json:"password"` Password *string `json:"password"`
Insecure *bool `json:"insecure"` Insecure *bool `json:"insecure"`
}{} }{}
t.DecodeJSONReq(&req) if err := t.DecodeJSONReq(&req); err != nil {
t.SendBadRequestError(err)
return
}
target := &models.RepTarget{} target := &models.RepTarget{}
if req.ID != nil { if req.ID != nil {
var err error var err error
target, err = dao.GetRepTarget(*req.ID) target, err = dao.GetRepTarget(*req.ID)
if err != nil { if err != nil {
t.HandleInternalServerError(fmt.Sprintf("failed to get target %d: %v", *req.ID, err)) t.SendInternalServerError(fmt.Errorf("failed to get target %d: %v", *req.ID, err))
return return
} }
if target == nil { if target == nil {
t.HandleNotFound(fmt.Sprintf("target %d not found", *req.ID)) t.SendNotFoundError(fmt.Errorf("target %d not found", *req.ID))
return return
} }
if len(target.Password) != 0 { if len(target.Password) != 0 {
target.Password, err = utils.ReversibleDecrypt(target.Password, t.secretKey) target.Password, err = utils.ReversibleDecrypt(target.Password, t.secretKey)
if err != nil { if err != nil {
t.HandleInternalServerError(fmt.Sprintf("failed to decrypt password: %v", err)) t.SendInternalServerError(fmt.Errorf("failed to decrypt password: %v", err))
return return
} }
} }
@ -104,7 +109,7 @@ func (t *TargetAPI) Ping() {
if req.Endpoint != nil { if req.Endpoint != nil {
url, err := utils.ParseEndpoint(*req.Endpoint) url, err := utils.ParseEndpoint(*req.Endpoint)
if err != nil { if err != nil {
t.HandleBadRequest(err.Error()) t.SendBadRequestError(err)
return return
} }
@ -126,16 +131,21 @@ func (t *TargetAPI) Ping() {
// Get ... // Get ...
func (t *TargetAPI) Get() { func (t *TargetAPI) Get() {
id := t.GetIDFromURL() id, err := t.GetIDFromURL()
if err != nil {
t.SendBadRequestError(err)
return
}
target, err := dao.GetRepTarget(id) target, err := dao.GetRepTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get target %d: %v", id, err) log.Errorf("failed to get target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if target == nil { if target == nil {
t.HandleNotFound(fmt.Sprintf("target %d not found", id)) t.SendNotFoundError(fmt.Errorf("target %d not found", id))
return return
} }
@ -151,7 +161,8 @@ func (t *TargetAPI) List() {
targets, err := dao.FilterRepTargets(name) targets, err := dao.FilterRepTargets(name)
if err != nil { if err != nil {
log.Errorf("failed to filter targets %s: %v", name, err) log.Errorf("failed to filter targets %s: %v", name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
for _, target := range targets { for _, target := range targets {
@ -166,27 +177,33 @@ func (t *TargetAPI) List() {
// Post ... // Post ...
func (t *TargetAPI) Post() { func (t *TargetAPI) Post() {
target := &models.RepTarget{} target := &models.RepTarget{}
t.DecodeJSONReqAndValidate(target) isValid, err := t.DecodeJSONReqAndValidate(target)
if !isValid {
t.SendBadRequestError(err)
return
}
ta, err := dao.GetRepTargetByName(target.Name) ta, err := dao.GetRepTargetByName(target.Name)
if err != nil { if err != nil {
log.Errorf("failed to get target %s: %v", target.Name, err) log.Errorf("failed to get target %s: %v", target.Name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if ta != nil { if ta != nil {
t.HandleConflict("name is already used") t.SendConflictError(errors.New("name is already used"))
return return
} }
ta, err = dao.GetRepTargetByEndpoint(target.URL) ta, err = dao.GetRepTargetByEndpoint(target.URL)
if err != nil { if err != nil {
log.Errorf("failed to get target [ %s ]: %v", target.URL, err) log.Errorf("failed to get target [ %s ]: %v", target.URL, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if ta != nil { if ta != nil {
t.HandleConflict(fmt.Sprintf("the target whose endpoint is %s already exists", target.URL)) t.SendConflictError(fmt.Errorf("the target whose endpoint is %s already exists", target.URL))
return return
} }
@ -194,14 +211,16 @@ func (t *TargetAPI) Post() {
target.Password, err = utils.ReversibleEncrypt(target.Password, t.secretKey) target.Password, err = utils.ReversibleEncrypt(target.Password, t.secretKey)
if err != nil { if err != nil {
log.Errorf("failed to encrypt password: %v", err) log.Errorf("failed to encrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
id, err := dao.AddRepTarget(*target) id, err := dao.AddRepTarget(*target)
if err != nil { if err != nil {
log.Errorf("failed to add target: %v", err) log.Errorf("failed to add target: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
t.Redirect(http.StatusCreated, strconv.FormatInt(id, 10)) t.Redirect(http.StatusCreated, strconv.FormatInt(id, 10))
@ -209,16 +228,21 @@ func (t *TargetAPI) Post() {
// Put ... // Put ...
func (t *TargetAPI) Put() { func (t *TargetAPI) Put() {
id := t.GetIDFromURL() id, err := t.GetIDFromURL()
if err != nil {
t.SendBadRequestError(err)
return
}
target, err := dao.GetRepTarget(id) target, err := dao.GetRepTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get target %d: %v", id, err) log.Errorf("failed to get target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if target == nil { if target == nil {
t.HandleNotFound(fmt.Sprintf("target %d not found", id)) t.SendNotFoundError(fmt.Errorf("target %d not found", id))
return return
} }
@ -226,7 +250,8 @@ func (t *TargetAPI) Put() {
target.Password, err = utils.ReversibleDecrypt(target.Password, t.secretKey) target.Password, err = utils.ReversibleDecrypt(target.Password, t.secretKey)
if err != nil { if err != nil {
log.Errorf("failed to decrypt password: %v", err) log.Errorf("failed to decrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
@ -237,7 +262,10 @@ func (t *TargetAPI) Put() {
Password *string `json:"password"` Password *string `json:"password"`
Insecure *bool `json:"insecure"` Insecure *bool `json:"insecure"`
}{} }{}
t.DecodeJSONReq(&req) if err := t.DecodeJSONReq(&req); err != nil {
t.SendBadRequestError(err)
return
}
originalName := target.Name originalName := target.Name
originalURL := target.URL originalURL := target.URL
@ -264,11 +292,12 @@ func (t *TargetAPI) Put() {
ta, err := dao.GetRepTargetByName(target.Name) ta, err := dao.GetRepTargetByName(target.Name)
if err != nil { if err != nil {
log.Errorf("failed to get target %s: %v", target.Name, err) log.Errorf("failed to get target %s: %v", target.Name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if ta != nil { if ta != nil {
t.HandleConflict("name is already used") t.SendConflictError(errors.New("name is already used"))
return return
} }
} }
@ -277,11 +306,12 @@ func (t *TargetAPI) Put() {
ta, err := dao.GetRepTargetByEndpoint(target.URL) ta, err := dao.GetRepTargetByEndpoint(target.URL)
if err != nil { if err != nil {
log.Errorf("failed to get target [ %s ]: %v", target.URL, err) log.Errorf("failed to get target [ %s ]: %v", target.URL, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if ta != nil { if ta != nil {
t.HandleConflict(fmt.Sprintf("the target whose endpoint is %s already exists", target.URL)) t.SendConflictError(fmt.Errorf("the target whose endpoint is %s already exists", target.URL))
return return
} }
} }
@ -290,45 +320,55 @@ func (t *TargetAPI) Put() {
target.Password, err = utils.ReversibleEncrypt(target.Password, t.secretKey) target.Password, err = utils.ReversibleEncrypt(target.Password, t.secretKey)
if err != nil { if err != nil {
log.Errorf("failed to encrypt password: %v", err) log.Errorf("failed to encrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
if err := dao.UpdateRepTarget(*target); err != nil { if err := dao.UpdateRepTarget(*target); err != nil {
log.Errorf("failed to update target %d: %v", id, err) log.Errorf("failed to update target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
// Delete ... // Delete ...
func (t *TargetAPI) Delete() { func (t *TargetAPI) Delete() {
id := t.GetIDFromURL() id, err := t.GetIDFromURL()
if err != nil {
t.SendBadRequestError(err)
return
}
target, err := dao.GetRepTarget(id) target, err := dao.GetRepTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get target %d: %v", id, err) log.Errorf("failed to get target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if target == nil { if target == nil {
t.HandleNotFound(fmt.Sprintf("target %d not found", id)) t.SendNotFoundError(fmt.Errorf("target %d not found", id))
return return
} }
policies, err := dao.GetRepPolicyByTarget(id) policies, err := dao.GetRepPolicyByTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get policies according target %d: %v", id, err) log.Errorf("failed to get policies according target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if len(policies) > 0 { if len(policies) > 0 {
log.Error("the target is used by policies, can not be deleted") log.Error("the target is used by policies, can not be deleted")
t.CustomAbort(http.StatusPreconditionFailed, "the target is used by policies, can not be deleted") t.SendPreconditionFailedError(errors.New("the target is used by policies, can not be deleted"))
return
} }
if err = dao.DeleteRepTarget(id); err != nil { if err = dao.DeleteRepTarget(id); err != nil {
log.Errorf("failed to delete target %d: %v", id, err) log.Errorf("failed to delete target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
} }
@ -345,23 +385,29 @@ func newRegistryClient(endpoint string, insecure bool, username, password string
// ListPolicies ... // ListPolicies ...
func (t *TargetAPI) ListPolicies() { func (t *TargetAPI) ListPolicies() {
id := t.GetIDFromURL() id, err := t.GetIDFromURL()
if err != nil {
t.SendBadRequestError(err)
return
}
target, err := dao.GetRepTarget(id) target, err := dao.GetRepTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get target %d: %v", id, err) log.Errorf("failed to get target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
if target == nil { if target == nil {
t.HandleNotFound(fmt.Sprintf("target %d not found", id)) t.SendNotFoundError(fmt.Errorf("target %d not found", id))
return return
} }
policies, err := dao.GetRepPolicyByTarget(id) policies, err := dao.GetRepPolicyByTarget(id)
if err != nil { if err != nil {
log.Errorf("failed to get policies according target %d: %v", id, err) log.Errorf("failed to get policies according target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) t.SendInternalServerError(errors.New(""))
return
} }
t.Data["json"] = policies t.Data["json"] = policies

View File

@ -15,6 +15,7 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/dao"
@ -55,7 +56,8 @@ func (ua *UserAPI) Prepare() {
mode, err := config.AuthMode() mode, err := config.AuthMode()
if err != nil { if err != nil {
log.Errorf("failed to get auth mode: %v", err) log.Errorf("failed to get auth mode: %v", err)
ua.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) ua.SendInternalServerError(errors.New(""))
return
} }
ua.AuthMode = mode ua.AuthMode = mode
@ -63,7 +65,8 @@ func (ua *UserAPI) Prepare() {
self, err := config.SelfRegistration() self, err := config.SelfRegistration()
if err != nil { if err != nil {
log.Errorf("failed to get self registration: %v", err) log.Errorf("failed to get self registration: %v", err)
ua.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) ua.SendInternalServerError(errors.New(""))
return
} }
ua.SelfRegistration = self ua.SelfRegistration = self
@ -72,7 +75,7 @@ func (ua *UserAPI) Prepare() {
if ua.Ctx.Input.IsPost() { if ua.Ctx.Input.IsPost() {
return return
} }
ua.HandleUnauthorized() ua.SendUnAuthorizedError(errors.New("UnAuthorize"))
return return
} }
@ -80,7 +83,7 @@ func (ua *UserAPI) Prepare() {
Username: ua.SecurityCtx.GetUsername(), Username: ua.SecurityCtx.GetUsername(),
}) })
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get user %s: %v", ua.SendInternalServerError(fmt.Errorf("failed to get user %s: %v",
ua.SecurityCtx.GetUsername(), err)) ua.SecurityCtx.GetUsername(), err))
return return
} }
@ -94,17 +97,20 @@ func (ua *UserAPI) Prepare() {
ua.userID, err = strconv.Atoi(id) ua.userID, err = strconv.Atoi(id)
if err != nil { if err != nil {
log.Errorf("Invalid user id, error: %v", err) log.Errorf("Invalid user id, error: %v", err)
ua.CustomAbort(http.StatusBadRequest, "Invalid user Id") ua.SendBadRequestError(errors.New("invalid user Id"))
return
} }
userQuery := models.User{UserID: ua.userID} userQuery := models.User{UserID: ua.userID}
u, err := dao.GetUser(userQuery) u, err := dao.GetUser(userQuery)
if err != nil { if err != nil {
log.Errorf("Error occurred in GetUser, error: %v", err) log.Errorf("Error occurred in GetUser, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
if u == nil { if u == nil {
log.Errorf("User with Id: %d does not exist", ua.userID) log.Errorf("User with Id: %d does not exist", ua.userID)
ua.CustomAbort(http.StatusNotFound, "") ua.SendNotFoundError(errors.New(""))
return
} }
} }
@ -118,7 +124,7 @@ func (ua *UserAPI) Get() {
u, err := dao.GetUser(userQuery) u, err := dao.GetUser(userQuery)
if err != nil { if err != nil {
log.Errorf("Error occurred in GetUser, error: %v", err) log.Errorf("Error occurred in GetUser, error: %v", err)
ua.RenderFormatedError(http.StatusInternalServerError, err) ua.SendInternalServerError(err)
return return
} }
u.Password = "" u.Password = ""
@ -128,7 +134,7 @@ func (ua *UserAPI) Get() {
if ua.AuthMode == common.OIDCAuth { if ua.AuthMode == common.OIDCAuth {
o, err := ua.getOIDCUserInfo() o, err := ua.getOIDCUserInfo()
if err != nil { if err != nil {
ua.RenderFormatedError(http.StatusInternalServerError, err) ua.SendInternalServerError(err)
return return
} }
u.OIDCUserMeta = o u.OIDCUserMeta = o
@ -139,7 +145,7 @@ func (ua *UserAPI) Get() {
} }
log.Errorf("Current user, id: %d does not have admin role, can not view other user's detail", ua.currentUserID) log.Errorf("Current user, id: %d does not have admin role, can not view other user's detail", ua.currentUserID)
ua.RenderError(http.StatusForbidden, "User does not have admin role") ua.SendForbiddenError(errors.New("user does not have admin role"))
return return
} }
@ -147,11 +153,16 @@ func (ua *UserAPI) Get() {
func (ua *UserAPI) List() { func (ua *UserAPI) List() {
if !ua.IsAdmin { if !ua.IsAdmin {
log.Errorf("Current user, id: %d does not have admin role, can not list users", ua.currentUserID) log.Errorf("Current user, id: %d does not have admin role, can not list users", ua.currentUserID)
ua.RenderError(http.StatusForbidden, "User does not have admin role") ua.SendForbiddenError(errors.New("user does not have admin role"))
return
}
page, size, err := ua.GetPaginationParams()
if err != nil {
ua.SendBadRequestError(err)
return return
} }
page, size := ua.GetPaginationParams()
query := &models.UserQuery{ query := &models.UserQuery{
Username: ua.GetString("username"), Username: ua.GetString("username"),
Email: ua.GetString("email"), Email: ua.GetString("email"),
@ -163,13 +174,13 @@ func (ua *UserAPI) List() {
total, err := dao.GetTotalOfUsers(query) total, err := dao.GetTotalOfUsers(query)
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get total of users: %v", err)) ua.SendInternalServerError(fmt.Errorf("failed to get total of users: %v", err))
return return
} }
users, err := dao.ListUsers(query) users, err := dao.ListUsers(query)
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get users: %v", err)) ua.SendInternalServerError(fmt.Errorf("failed to get users: %v", err))
return return
} }
@ -180,7 +191,11 @@ func (ua *UserAPI) List() {
// Search ... // Search ...
func (ua *UserAPI) Search() { func (ua *UserAPI) Search() {
page, size := ua.GetPaginationParams() page, size, err := ua.GetPaginationParams()
if err != nil {
ua.SendBadRequestError(err)
return
}
query := &models.UserQuery{ query := &models.UserQuery{
Username: ua.GetString("username"), Username: ua.GetString("username"),
Email: ua.GetString("email"), Email: ua.GetString("email"),
@ -192,13 +207,13 @@ func (ua *UserAPI) Search() {
total, err := dao.GetTotalOfUsers(query) total, err := dao.GetTotalOfUsers(query)
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get total of users: %v", err)) ua.SendInternalServerError(fmt.Errorf("failed to get total of users: %v", err))
return return
} }
users, err := dao.ListUsers(query) users, err := dao.ListUsers(query)
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get users: %v", err)) ua.SendInternalServerError(fmt.Errorf("failed to get users: %v", err))
return return
} }
@ -215,42 +230,49 @@ func (ua *UserAPI) Search() {
// Put ... // Put ...
func (ua *UserAPI) Put() { func (ua *UserAPI) Put() {
if !ua.modifiable() { if !ua.modifiable() {
ua.RenderError(http.StatusForbidden, fmt.Sprintf("User with ID %d cannot be modified", ua.userID)) ua.SendForbiddenError(fmt.Errorf("User with ID %d cannot be modified", ua.userID))
return return
} }
user := models.User{UserID: ua.userID} user := models.User{UserID: ua.userID}
ua.DecodeJSONReq(&user) if err := ua.DecodeJSONReq(&user); err != nil {
ua.SendBadRequestError(err)
return
}
err := commonValidate(user) err := commonValidate(user)
if err != nil { if err != nil {
log.Warningf("Bad request in change user profile: %v", err) log.Warningf("Bad request in change user profile: %v", err)
ua.RenderError(http.StatusBadRequest, "change user profile error:"+err.Error()) ua.SendBadRequestError(fmt.Errorf("change user profile error:" + err.Error()))
return return
} }
userQuery := models.User{UserID: ua.userID} userQuery := models.User{UserID: ua.userID}
u, err := dao.GetUser(userQuery) u, err := dao.GetUser(userQuery)
if err != nil { if err != nil {
log.Errorf("Error occurred in GetUser, error: %v", err) log.Errorf("Error occurred in GetUser, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
if u == nil { if u == nil {
log.Errorf("User with Id: %d does not exist", ua.userID) log.Errorf("User with Id: %d does not exist", ua.userID)
ua.CustomAbort(http.StatusNotFound, "") ua.SendNotFoundError(errors.New(""))
return
} }
if u.Email != user.Email { if u.Email != user.Email {
emailExist, err := dao.UserExists(user, "email") emailExist, err := dao.UserExists(user, "email")
if err != nil { if err != nil {
log.Errorf("Error occurred in change user profile: %v", err) log.Errorf("Error occurred in change user profile: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
if emailExist { if emailExist {
log.Warning("email has already been used!") log.Warning("email has already been used!")
ua.RenderError(http.StatusConflict, "email has already been used!") ua.SendConflictError(errors.New("email has already been used"))
return return
} }
} }
if err := dao.ChangeUserProfile(user); err != nil { if err := dao.ChangeUserProfile(user); err != nil {
log.Errorf("Failed to update user profile, error: %v", err) log.Errorf("Failed to update user profile, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, err.Error()) ua.SendInternalServerError(err)
return
} }
} }
@ -258,16 +280,21 @@ func (ua *UserAPI) Put() {
func (ua *UserAPI) Post() { func (ua *UserAPI) Post() {
if !(ua.AuthMode == common.DBAuth) { if !(ua.AuthMode == common.DBAuth) {
ua.CustomAbort(http.StatusForbidden, "") ua.SendForbiddenError(errors.New(""))
return
} }
if !(ua.SelfRegistration || ua.IsAdmin) { if !(ua.SelfRegistration || ua.IsAdmin) {
log.Warning("Registration can only be used by admin role user when self-registration is off.") log.Warning("Registration can only be used by admin role user when self-registration is off.")
ua.CustomAbort(http.StatusForbidden, "") ua.SendForbiddenError(errors.New(""))
return
} }
user := models.User{} user := models.User{}
ua.DecodeJSONReq(&user) if err := ua.DecodeJSONReq(&user); err != nil {
ua.SendBadRequestError(err)
return
}
err := validate(user) err := validate(user)
if err != nil { if err != nil {
log.Warningf("Bad request in Register: %v", err) log.Warningf("Bad request in Register: %v", err)
@ -277,27 +304,30 @@ func (ua *UserAPI) Post() {
userExist, err := dao.UserExists(user, "username") userExist, err := dao.UserExists(user, "username")
if err != nil { if err != nil {
log.Errorf("Error occurred in Register: %v", err) log.Errorf("Error occurred in Register: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
if userExist { if userExist {
log.Warning("username has already been used!") log.Warning("username has already been used!")
ua.RenderError(http.StatusConflict, "username has already been used!") ua.SendConflictError(errors.New("username has already been used"))
return return
} }
emailExist, err := dao.UserExists(user, "email") emailExist, err := dao.UserExists(user, "email")
if err != nil { if err != nil {
log.Errorf("Error occurred in change user profile: %v", err) log.Errorf("Error occurred in change user profile: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
if emailExist { if emailExist {
log.Warning("email has already been used!") log.Warning("email has already been used!")
ua.RenderError(http.StatusConflict, "email has already been used!") ua.SendConflictError(errors.New("email has already been used"))
return return
} }
userID, err := dao.Register(user) userID, err := dao.Register(user)
if err != nil { if err != nil {
log.Errorf("Error occurred in Register: %v", err) log.Errorf("Error occurred in Register: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
ua.Redirect(http.StatusCreated, strconv.FormatInt(userID, 10)) ua.Redirect(http.StatusCreated, strconv.FormatInt(userID, 10))
@ -306,7 +336,7 @@ func (ua *UserAPI) Post() {
// Delete ... // Delete ...
func (ua *UserAPI) Delete() { func (ua *UserAPI) Delete() {
if !ua.IsAdmin || ua.AuthMode != common.DBAuth || ua.userID == 1 || ua.currentUserID == ua.userID { if !ua.IsAdmin || ua.AuthMode != common.DBAuth || ua.userID == 1 || ua.currentUserID == ua.userID {
ua.RenderError(http.StatusForbidden, fmt.Sprintf("User with ID: %d cannot be removed, auth mode: %s, current user ID: %d", ua.userID, ua.AuthMode, ua.currentUserID)) ua.SendForbiddenError(fmt.Errorf("User with ID: %d cannot be removed, auth mode: %s, current user ID: %d", ua.userID, ua.AuthMode, ua.currentUserID))
return return
} }
@ -314,7 +344,7 @@ func (ua *UserAPI) Delete() {
err = dao.DeleteUser(ua.userID) err = dao.DeleteUser(ua.userID)
if err != nil { if err != nil {
log.Errorf("Failed to delete data from database, error: %v", err) log.Errorf("Failed to delete data from database, error: %v", err)
ua.RenderError(http.StatusInternalServerError, "Failed to delete User") ua.SendInternalServerError(errors.New("failed to delete User"))
return return
} }
} }
@ -322,43 +352,46 @@ func (ua *UserAPI) Delete() {
// ChangePassword handles PUT to /api/users/{}/password // ChangePassword handles PUT to /api/users/{}/password
func (ua *UserAPI) ChangePassword() { func (ua *UserAPI) ChangePassword() {
if !ua.modifiable() { if !ua.modifiable() {
ua.RenderError(http.StatusForbidden, fmt.Sprintf("User with ID: %d is not modifiable", ua.userID)) ua.SendForbiddenError(fmt.Errorf("User with ID: %d is not modifiable", ua.userID))
return return
} }
changePwdOfOwn := ua.userID == ua.currentUserID changePwdOfOwn := ua.userID == ua.currentUserID
var req passwordReq var req passwordReq
ua.DecodeJSONReq(&req) if err := ua.DecodeJSONReq(&req); err != nil {
ua.SendBadRequestError(err)
return
}
if changePwdOfOwn && len(req.OldPassword) == 0 { if changePwdOfOwn && len(req.OldPassword) == 0 {
ua.HandleBadRequest("empty old_password") ua.SendBadRequestError(errors.New("empty old_password"))
return return
} }
if len(req.NewPassword) == 0 { if len(req.NewPassword) == 0 {
ua.HandleBadRequest("empty new_password") ua.SendBadRequestError(errors.New("empty new_password"))
return return
} }
user, err := dao.GetUser(models.User{UserID: ua.userID}) user, err := dao.GetUser(models.User{UserID: ua.userID})
if err != nil { if err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to get user %d: %v", ua.userID, err)) ua.SendInternalServerError(fmt.Errorf("failed to get user %d: %v", ua.userID, err))
return return
} }
if user == nil { if user == nil {
ua.HandleNotFound(fmt.Sprintf("user %d not found", ua.userID)) ua.SendNotFoundError(fmt.Errorf("user %d not found", ua.userID))
return return
} }
if changePwdOfOwn { if changePwdOfOwn {
if user.Password != utils.Encrypt(req.OldPassword, user.Salt) { if user.Password != utils.Encrypt(req.OldPassword, user.Salt) {
log.Info("incorrect old_password") log.Info("incorrect old_password")
ua.RenderError(http.StatusForbidden, "incorrect old_password") ua.SendForbiddenError(errors.New("incorrect old_password"))
return return
} }
} }
if user.Password == utils.Encrypt(req.NewPassword, user.Salt) { if user.Password == utils.Encrypt(req.NewPassword, user.Salt) {
ua.HandleBadRequest("the new password can not be same with the old one") ua.SendBadRequestError(errors.New("the new password can not be same with the old one"))
return return
} }
@ -367,7 +400,7 @@ func (ua *UserAPI) ChangePassword() {
Password: req.NewPassword, Password: req.NewPassword,
} }
if err = dao.ChangeUserPassword(updatedUser); err != nil { if err = dao.ChangeUserPassword(updatedUser); err != nil {
ua.HandleInternalServerError(fmt.Sprintf("failed to change password of user %d: %v", ua.userID, err)) ua.SendInternalServerError(fmt.Errorf("failed to change password of user %d: %v", ua.userID, err))
return return
} }
} }
@ -380,10 +413,14 @@ func (ua *UserAPI) ToggleUserAdminRole() {
return return
} }
userQuery := models.User{UserID: ua.userID} userQuery := models.User{UserID: ua.userID}
ua.DecodeJSONReq(&userQuery) if err := ua.DecodeJSONReq(&userQuery); err != nil {
ua.SendBadRequestError(err)
return
}
if err := dao.ToggleUserAdminRole(userQuery.UserID, userQuery.HasAdminRole); err != nil { if err := dao.ToggleUserAdminRole(userQuery.UserID, userQuery.HasAdminRole); err != nil {
log.Errorf("Error occurred in ToggleUserAdminRole: %v", err) log.Errorf("Error occurred in ToggleUserAdminRole: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.") ua.SendInternalServerError(errors.New("internal error"))
return
} }
} }

View File

@ -20,6 +20,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"errors"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group" "github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
@ -42,7 +43,7 @@ const (
func (uga *UserGroupAPI) Prepare() { func (uga *UserGroupAPI) Prepare() {
uga.BaseController.Prepare() uga.BaseController.Prepare()
if !uga.SecurityCtx.IsAuthenticated() { if !uga.SecurityCtx.IsAuthenticated() {
uga.HandleUnauthorized() uga.SendUnAuthorizedError(errors.New("Unauthorized"))
return return
} }
@ -51,13 +52,13 @@ func (uga *UserGroupAPI) Prepare() {
log.Warningf("failed to parse user group id, error: %v", err) log.Warningf("failed to parse user group id, error: %v", err)
} }
if ugid <= 0 && (uga.Ctx.Input.IsPut() || uga.Ctx.Input.IsDelete()) { if ugid <= 0 && (uga.Ctx.Input.IsPut() || uga.Ctx.Input.IsDelete()) {
uga.HandleBadRequest(fmt.Sprintf("invalid user group ID: %s", uga.GetStringFromPath(":ugid"))) uga.SendBadRequestError(fmt.Errorf("invalid user group ID: %s", uga.GetStringFromPath(":ugid")))
return return
} }
uga.id = int(ugid) uga.id = int(ugid)
// Common user can create/update, only harbor admin can delete user group. // Common user can create/update, only harbor admin can delete user group.
if uga.Ctx.Input.IsDelete() && !uga.SecurityCtx.IsSysAdmin() { if uga.Ctx.Input.IsDelete() && !uga.SecurityCtx.IsSysAdmin() {
uga.HandleForbidden(uga.SecurityCtx.GetUsername()) uga.SendForbiddenError(errors.New(uga.SecurityCtx.GetUsername()))
return return
} }
} }
@ -71,7 +72,7 @@ func (uga *UserGroupAPI) Get() {
query := models.UserGroup{GroupType: common.LdapGroupType} // Current query LDAP group only query := models.UserGroup{GroupType: common.LdapGroupType} // Current query LDAP group only
userGroupList, err := group.QueryUserGroup(query) userGroupList, err := group.QueryUserGroup(query)
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Failed to query database for user group list, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("failed to query database for user group list, error: %v", err))
return return
} }
if len(userGroupList) > 0 { if len(userGroupList) > 0 {
@ -81,11 +82,11 @@ func (uga *UserGroupAPI) Get() {
// return a specific user group // return a specific user group
userGroup, err := group.GetUserGroup(ID) userGroup, err := group.GetUserGroup(ID)
if userGroup == nil { if userGroup == nil {
uga.HandleNotFound("The user group does not exist.") uga.SendNotFoundError(errors.New("the user group does not exist"))
return return
} }
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Failed to query database for user group list, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("failed to query database for user group list, error: %v", err))
return return
} }
uga.Data["json"] = userGroup uga.Data["json"] = userGroup
@ -96,43 +97,47 @@ func (uga *UserGroupAPI) Get() {
// Post ... Create User Group // Post ... Create User Group
func (uga *UserGroupAPI) Post() { func (uga *UserGroupAPI) Post() {
userGroup := models.UserGroup{} userGroup := models.UserGroup{}
uga.DecodeJSONReq(&userGroup) if err := uga.DecodeJSONReq(&userGroup); err != nil {
uga.SendBadRequestError(err)
return
}
userGroup.ID = 0 userGroup.ID = 0
userGroup.GroupType = common.LdapGroupType userGroup.GroupType = common.LdapGroupType
userGroup.LdapGroupDN = strings.TrimSpace(userGroup.LdapGroupDN) userGroup.LdapGroupDN = strings.TrimSpace(userGroup.LdapGroupDN)
userGroup.GroupName = strings.TrimSpace(userGroup.GroupName) userGroup.GroupName = strings.TrimSpace(userGroup.GroupName)
if len(userGroup.GroupName) == 0 { if len(userGroup.GroupName) == 0 {
uga.HandleBadRequest(userNameEmptyMsg) uga.SendBadRequestError(errors.New(userNameEmptyMsg))
return return
} }
query := models.UserGroup{GroupType: userGroup.GroupType, LdapGroupDN: userGroup.LdapGroupDN} query := models.UserGroup{GroupType: userGroup.GroupType, LdapGroupDN: userGroup.LdapGroupDN}
result, err := group.QueryUserGroup(query) result, err := group.QueryUserGroup(query)
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in add user group, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("error occurred in add user group, error: %v", err))
return return
} }
if len(result) > 0 { if len(result) > 0 {
uga.HandleConflict("Error occurred in add user group, duplicate user group exist.") uga.SendConflictError(errors.New("error occurred in add user group, duplicate user group exist"))
return return
} }
// User can not add ldap group when the ldap server is offline // User can not add ldap group when the ldap server is offline
ldapGroup, err := auth.SearchGroup(userGroup.LdapGroupDN) ldapGroup, err := auth.SearchGroup(userGroup.LdapGroupDN)
if err == ldap.ErrNotFound || ldapGroup == nil { if err == ldap.ErrNotFound || ldapGroup == nil {
uga.HandleNotFound(fmt.Sprintf("LDAP Group DN is not found: DN:%v", userGroup.LdapGroupDN)) uga.SendNotFoundError(fmt.Errorf("LDAP Group DN is not found: DN:%v", userGroup.LdapGroupDN))
return return
} }
if err == ldap.ErrDNSyntax { if err == ldap.ErrDNSyntax {
uga.HandleBadRequest(fmt.Sprintf("Invalid DN syntax. DN: %v", userGroup.LdapGroupDN)) uga.SendBadRequestError(fmt.Errorf("invalid DN syntax. DN: %v", userGroup.LdapGroupDN))
return return
} }
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in search user group. error: %v", err)) uga.SendInternalServerError(fmt.Errorf("Error occurred in search user group. error: %v", err))
return return
} }
groupID, err := group.AddUserGroup(userGroup) groupID, err := group.AddUserGroup(userGroup)
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in add user group, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("Error occurred in add user group, error: %v", err))
return return
} }
uga.Redirect(http.StatusCreated, strconv.FormatInt(int64(groupID), 10)) uga.Redirect(http.StatusCreated, strconv.FormatInt(int64(groupID), 10))
@ -141,18 +146,21 @@ func (uga *UserGroupAPI) Post() {
// Put ... Only support update name // Put ... Only support update name
func (uga *UserGroupAPI) Put() { func (uga *UserGroupAPI) Put() {
userGroup := models.UserGroup{} userGroup := models.UserGroup{}
uga.DecodeJSONReq(&userGroup) if err := uga.DecodeJSONReq(&userGroup); err != nil {
uga.SendBadRequestError(err)
return
}
ID := uga.id ID := uga.id
userGroup.GroupName = strings.TrimSpace(userGroup.GroupName) userGroup.GroupName = strings.TrimSpace(userGroup.GroupName)
if len(userGroup.GroupName) == 0 { if len(userGroup.GroupName) == 0 {
uga.HandleBadRequest(userNameEmptyMsg) uga.SendBadRequestError(errors.New(userNameEmptyMsg))
return return
} }
userGroup.GroupType = common.LdapGroupType userGroup.GroupType = common.LdapGroupType
log.Debugf("Updated user group %v", userGroup) log.Debugf("Updated user group %v", userGroup)
err := group.UpdateUserGroupName(ID, userGroup.GroupName) err := group.UpdateUserGroupName(ID, userGroup.GroupName)
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in update user group, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("Error occurred in update user group, error: %v", err))
return return
} }
return return
@ -162,7 +170,7 @@ func (uga *UserGroupAPI) Put() {
func (uga *UserGroupAPI) Delete() { func (uga *UserGroupAPI) Delete() {
err := group.DeleteUserGroup(uga.id) err := group.DeleteUserGroup(uga.id)
if err != nil { if err != nil {
uga.HandleInternalServerError(fmt.Sprintf("Error occurred in update user group, error: %v", err)) uga.SendInternalServerError(fmt.Errorf("Error occurred in update user group, error: %v", err))
return return
} }
return return

View File

@ -53,7 +53,8 @@ type oidcUserData struct {
// Prepare include public code path for call request handler of OIDCController // Prepare include public code path for call request handler of OIDCController
func (oc *OIDCController) Prepare() { func (oc *OIDCController) Prepare() {
if mode, _ := config.AuthMode(); mode != common.OIDCAuth { if mode, _ := config.AuthMode(); mode != common.OIDCAuth {
oc.CustomAbort(http.StatusPreconditionFailed, fmt.Sprintf("Auth Mode: %s is not OIDC based.", mode)) oc.SendPreconditionFailedError(fmt.Errorf("Auth Mode: %s is not OIDC based", mode))
return
} }
} }
@ -62,7 +63,7 @@ func (oc *OIDCController) RedirectLogin() {
state := utils.GenerateRandomString() state := utils.GenerateRandomString()
url, err := oidc.AuthCodeURL(state) url, err := oidc.AuthCodeURL(state)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
oc.SetSession(stateKey, state) oc.SetSession(stateKey, state)
@ -74,40 +75,40 @@ func (oc *OIDCController) RedirectLogin() {
// kick off onboard if needed. // kick off onboard if needed.
func (oc *OIDCController) Callback() { func (oc *OIDCController) Callback() {
if oc.Ctx.Request.URL.Query().Get("state") != oc.GetSession(stateKey) { if oc.Ctx.Request.URL.Query().Get("state") != oc.GetSession(stateKey) {
oc.RenderError(http.StatusBadRequest, "State mismatch.") oc.SendBadRequestError(errors.New("State mismatch"))
return return
} }
code := oc.Ctx.Request.URL.Query().Get("code") code := oc.Ctx.Request.URL.Query().Get("code")
ctx := oc.Ctx.Request.Context() ctx := oc.Ctx.Request.Context()
token, err := oidc.ExchangeToken(ctx, code) token, err := oidc.ExchangeToken(ctx, code)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
idToken, err := oidc.VerifyToken(ctx, token.IDToken) idToken, err := oidc.VerifyToken(ctx, token.IDToken)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
d := &oidcUserData{} d := &oidcUserData{}
err = idToken.Claims(d) err = idToken.Claims(d)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
ouDataStr, err := json.Marshal(d) ouDataStr, err := json.Marshal(d)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
u, err := dao.GetUserBySubIss(d.Subject, d.Issuer) u, err := dao.GetUserBySubIss(d.Subject, d.Issuer)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
tokenBytes, err := json.Marshal(token) tokenBytes, err := json.Marshal(token)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
oc.SetSession(tokenKey, tokenBytes) oc.SetSession(tokenKey, tokenBytes)
@ -125,37 +126,40 @@ func (oc *OIDCController) Callback() {
// Onboard handles the request to onboard an user authenticated via OIDC provider // Onboard handles the request to onboard an user authenticated via OIDC provider
func (oc *OIDCController) Onboard() { func (oc *OIDCController) Onboard() {
u := &onboardReq{} u := &onboardReq{}
oc.DecodeJSONReq(u) if err := oc.DecodeJSONReq(u); err != nil {
oc.SendBadRequestError(err)
return
}
username := u.Username username := u.Username
if utils.IsIllegalLength(username, 1, 255) { if utils.IsIllegalLength(username, 1, 255) {
oc.RenderFormatedError(http.StatusBadRequest, errors.New("username with illegal length")) oc.SendBadRequestError(errors.New("username with illegal length"))
return return
} }
if utils.IsContainIllegalChar(username, []string{",", "~", "#", "$", "%"}) { if utils.IsContainIllegalChar(username, []string{",", "~", "#", "$", "%"}) {
oc.RenderFormatedError(http.StatusBadRequest, errors.New("username contains illegal characters")) oc.SendBadRequestError(errors.New("username contains illegal characters"))
return return
} }
userInfoStr, ok := oc.GetSession(userInfoKey).(string) userInfoStr, ok := oc.GetSession(userInfoKey).(string)
if !ok { if !ok {
oc.RenderError(http.StatusBadRequest, "Failed to get OIDC user info from session") oc.SendBadRequestError(errors.New("Failed to get OIDC user info from session"))
return return
} }
log.Debugf("User info string: %s\n", userInfoStr) log.Debugf("User info string: %s\n", userInfoStr)
tb, ok := oc.GetSession(tokenKey).([]byte) tb, ok := oc.GetSession(tokenKey).([]byte)
if !ok { if !ok {
oc.RenderError(http.StatusBadRequest, "Failed to get OIDC token from session") oc.SendBadRequestError(errors.New("Failed to get OIDC token from session"))
return return
} }
s, t, err := secretAndToken(tb) s, t, err := secretAndToken(tb)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
d := &oidcUserData{} d := &oidcUserData{}
err = json.Unmarshal([]byte(userInfoStr), &d) err = json.Unmarshal([]byte(userInfoStr), &d)
if err != nil { if err != nil {
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
return return
} }
oidcUser := models.OIDCUser{ oidcUser := models.OIDCUser{
@ -180,7 +184,7 @@ func (oc *OIDCController) Onboard() {
oc.RenderError(http.StatusConflict, "Duplicate username") oc.RenderError(http.StatusConflict, "Duplicate username")
return return
} }
oc.RenderFormatedError(http.StatusInternalServerError, err) oc.SendInternalServerError(err)
oc.DelSession(userInfoKey) oc.DelSession(userInfoKey)
return return
} }

View File

@ -75,12 +75,12 @@ func (h *Handler) HandleAdminJob() {
log.Infof("received admin job status update event: job-%d, status-%s", h.id, h.status) log.Infof("received admin job status update event: job-%d, status-%s", h.id, h.status)
// create the mapping relationship between the jobs in database and jobservice // create the mapping relationship between the jobs in database and jobservice
if err := dao.SetAdminJobUUID(h.id, h.UUID); err != nil { if err := dao.SetAdminJobUUID(h.id, h.UUID); err != nil {
h.HandleInternalServerError(err.Error()) h.SendInternalServerError(err)
return return
} }
if err := dao.UpdateAdminJobStatus(h.id, h.status); err != nil { if err := dao.UpdateAdminJobStatus(h.id, h.status); err != nil {
log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status) log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status)
h.HandleInternalServerError(err.Error()) h.SendInternalServerError(err)
return return
} }
} }

View File

@ -73,7 +73,7 @@ func (h *Handler) HandleScan() {
log.Debugf("received san job status update event: job-%d, status-%s", h.id, h.status) log.Debugf("received san job status update event: job-%d, status-%s", h.id, h.status)
if err := dao.UpdateScanJobStatus(h.id, h.status); err != nil { if err := dao.UpdateScanJobStatus(h.id, h.status); err != nil {
log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status) log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status)
h.HandleInternalServerError(err.Error()) h.SendInternalServerError(err)
return return
} }
} }
@ -83,7 +83,7 @@ func (h *Handler) HandleReplication() {
log.Debugf("received replication job status update event: job-%d, status-%s", h.id, h.status) log.Debugf("received replication job status update event: job-%d, status-%s", h.id, h.status)
if err := dao.UpdateRepJobStatus(h.id, h.status); err != nil { if err := dao.UpdateRepJobStatus(h.id, h.status); err != nil {
log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status) log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status)
h.HandleInternalServerError(err.Error()) h.SendInternalServerError(err)
return return
} }
} }

View File

@ -1,27 +0,0 @@
package utils
import (
"encoding/json"
"errors"
)
// WrapErrorMessage wraps the error msg to the well formated error message `{ "error": "The error message" }`
func WrapErrorMessage(msg string) string {
errBody := make(map[string]string, 1)
errBody["error"] = msg
data, err := json.Marshal(&errBody)
if err != nil {
return msg
}
return string(data)
}
// WrapError wraps the error to the well formated error `{ "error": "The error message" }`
func WrapError(err error) error {
if err == nil {
return nil
}
return errors.New(WrapErrorMessage(err.Error()))
}

View File

@ -1,33 +0,0 @@
package utils
import (
"encoding/json"
"errors"
"testing"
)
// Test case for error wrapping function.
func TestWrapError(t *testing.T) {
if WrapError(nil) != nil {
t.Fatal("expect nil error but got a non-nil one")
}
err := errors.New("mock error")
formatedErr := WrapError(err)
if formatedErr == nil {
t.Fatal("expect non-nil error but got nil")
}
jsonErr := formatedErr.Error()
structuredErr := make(map[string]string, 1)
if e := json.Unmarshal([]byte(jsonErr), &structuredErr); e != nil {
t.Fatal("expect nil error but got a non-nil one when doing error converting")
}
if msg, ok := structuredErr["error"]; !ok {
t.Fatal("expect an 'error' filed but missing")
} else {
if msg != "mock error" {
t.Fatalf("expect error message '%s' but got '%s'", "mock error", msg)
}
}
}

View File

@ -59,7 +59,7 @@ class TestProjects(unittest.TestCase):
#3. Create a new project(PA) by user(UA), and fail to create a new project; #3. Create a new project(PA) by user(UA), and fail to create a new project;
self.project.create_project(metadata = {"public": "false"}, expect_status_code = 403, self.project.create_project(metadata = {"public": "false"}, expect_status_code = 403,
expect_response_body = "Only system admin can create project", **TestProjects.USER_edit_project_creation_CLIENT) expect_response_body = "{\"code\":403,\"message\":\"Only system admin can create project\"}", **TestProjects.USER_edit_project_creation_CLIENT)
#4. Set project creation to "everyone"; #4. Set project creation to "everyone";
self.conf.set_configurations_of_project_creation_restriction("everyone", **ADMIN_CLIENT) self.conf.set_configurations_of_project_creation_restriction("everyone", **ADMIN_CLIENT)