mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
Merge pull request #10908 from wy65701436/middleware-blocker
add delete manifest middleware
This commit is contained in:
commit
e79f4fd270
@ -1,11 +1,8 @@
|
||||
package contenttrust
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/core/middlewares/util"
|
||||
internal_errors "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/signature/notary"
|
||||
"github.com/goharbor/harbor/src/pkg/signature"
|
||||
serror "github.com/goharbor/harbor/src/server/error"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
"net/http"
|
||||
@ -27,7 +24,7 @@ func Middleware() func(http.Handler) http.Handler {
|
||||
rec := httptest.NewRecorder()
|
||||
next.ServeHTTP(rec, req)
|
||||
if rec.Result().StatusCode == http.StatusOK {
|
||||
match, err := matchNotaryDigest(mf)
|
||||
match, err := isArtifactSigned(req, mf)
|
||||
if err != nil {
|
||||
serror.SendError(rw, err)
|
||||
return
|
||||
@ -61,36 +58,12 @@ func validate(req *http.Request) (bool, middleware.ArtifactInfo) {
|
||||
return true, af
|
||||
}
|
||||
|
||||
func matchNotaryDigest(af middleware.ArtifactInfo) (bool, error) {
|
||||
if NotaryEndpoint == "" {
|
||||
NotaryEndpoint = config.InternalNotaryEndpoint()
|
||||
}
|
||||
targets, err := notary.GetInternalTargets(NotaryEndpoint, util.TokenUsername, af.Repository)
|
||||
// isArtifactSigned use the sign manager to check the signature, it could handle pull by tag or digtest
|
||||
// if pull by digest, any tag of the artifact is signed, will return true.
|
||||
func isArtifactSigned(req *http.Request, art middleware.ArtifactInfo) (bool, error) {
|
||||
checker, err := signature.GetManager().GetCheckerByRepo(req.Context(), art.Repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, t := range targets {
|
||||
if af.Digest != "" {
|
||||
d, err := notary.DigestFromTarget(t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if af.Digest == d {
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
if t.Tag == af.Tag {
|
||||
log.Debugf("found reference: %s in notary, try to match digest.", af.Tag)
|
||||
d, err := notary.DigestFromTarget(t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if af.Digest == d {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Debugf("image: %#v, not found in notary", af)
|
||||
return false, nil
|
||||
return checker.IsArtifactSigned(art.Digest), nil
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
package immutable
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/api/artifact"
|
||||
"github.com/goharbor/harbor/src/api/tag"
|
||||
common_util "github.com/goharbor/harbor/src/common/utils"
|
||||
internal_errors "github.com/goharbor/harbor/src/internal/error"
|
||||
serror "github.com/goharbor/harbor/src/server/error"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// MiddlewareDelete ...
|
||||
func MiddlewareDelete() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if err := handleDelete(req); err != nil {
|
||||
var e *ErrImmutable
|
||||
if errors.As(err, &e) {
|
||||
pkgE := internal_errors.New(e).WithCode(internal_errors.PreconditionCode)
|
||||
serror.SendError(rw, pkgE)
|
||||
return
|
||||
}
|
||||
pkgE := internal_errors.New(fmt.Errorf("error occurred when to handle request in immutable handler: %v", err)).WithCode(internal_errors.GeneralCode)
|
||||
serror.SendError(rw, pkgE)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// handleDelete ...
|
||||
func handleDelete(req *http.Request) error {
|
||||
art, ok := middleware.ArtifactInfoFromContext(req.Context())
|
||||
if !ok {
|
||||
return errors.New("cannot get the manifest information from request context")
|
||||
}
|
||||
|
||||
af, err := artifact.Ctl.GetByReference(req.Context(), art.Repository, art.Digest, &artifact.Option{
|
||||
WithTag: true,
|
||||
TagOption: &tag.Option{WithImmutableStatus: true},
|
||||
})
|
||||
if err != nil {
|
||||
if internal_errors.IsErr(err, internal_errors.NotFoundCode) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
_, repoName := common_util.ParseRepository(art.Repository)
|
||||
for _, tag := range af.Tags {
|
||||
if tag.Immutable {
|
||||
return NewErrImmutable(repoName, tag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package immutable
|
||||
|
||||
// Tests are in the push_mf_test
|
@ -12,8 +12,8 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// MiddlewarePush ...
|
||||
func MiddlewarePush() func(http.Handler) http.Handler {
|
||||
// Middleware ...
|
||||
func Middleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if err := handlePush(req); err != nil {
|
||||
|
@ -54,37 +54,7 @@ func doPutManifestRequest(projectID int64, projectName, name, tag, dgt string, n
|
||||
}
|
||||
*req = *(req.WithContext(internal_orm.NewContext(context.TODO(), dao.GetOrmer())))
|
||||
*req = *(req.WithContext(context.WithValue(req.Context(), middleware.ArtifactInfoKey, afInfo)))
|
||||
h := MiddlewarePush()(n)
|
||||
h.ServeHTTP(util.NewCustomResponseWriter(rr), req)
|
||||
|
||||
return rr.Code
|
||||
}
|
||||
|
||||
func doDeleteManifestRequest(projectID int64, projectName, name, tag, dgt string, next ...http.HandlerFunc) int {
|
||||
repository := fmt.Sprintf("%s/%s", projectName, name)
|
||||
|
||||
url := fmt.Sprintf("/v2/%s/manifests/%s", repository, tag)
|
||||
req, _ := http.NewRequest("DELETE", url, nil)
|
||||
|
||||
afInfo := &middleware.ArtifactInfo{
|
||||
ProjectName: projectName,
|
||||
Repository: repository,
|
||||
Tag: tag,
|
||||
Digest: dgt,
|
||||
}
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
var n http.HandlerFunc
|
||||
if len(next) > 0 {
|
||||
n = next[0]
|
||||
} else {
|
||||
n = func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
}
|
||||
*req = *(req.WithContext(internal_orm.NewContext(context.TODO(), dao.GetOrmer())))
|
||||
*req = *(req.WithContext(context.WithValue(req.Context(), middleware.ArtifactInfoKey, afInfo)))
|
||||
h := MiddlewareDelete()(n)
|
||||
h := Middleware()(n)
|
||||
h.ServeHTTP(util.NewCustomResponseWriter(rr), req)
|
||||
|
||||
return rr.Code
|
||||
@ -203,12 +173,6 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
|
||||
code2 := doPutManifestRequest(projectID, projectName, "photon", "latest", dgt)
|
||||
suite.Equal(http.StatusCreated, code2)
|
||||
|
||||
code3 := doDeleteManifestRequest(projectID, projectName, "photon", "release-1.10", dgt)
|
||||
suite.Equal(http.StatusPreconditionFailed, code3)
|
||||
|
||||
code4 := doDeleteManifestRequest(projectID, projectName, "photon", "latest", dgt)
|
||||
suite.Equal(http.StatusPreconditionFailed, code4)
|
||||
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -58,12 +58,11 @@ func RegisterRoutes() {
|
||||
root.NewRoute().
|
||||
Method(http.MethodDelete).
|
||||
Path("/*/manifests/:reference").
|
||||
Middleware(immutable.MiddlewareDelete()).
|
||||
HandlerFunc(deleteManifest)
|
||||
root.NewRoute().
|
||||
Method(http.MethodPut).
|
||||
Path("/*/manifests/:reference").
|
||||
Middleware(immutable.MiddlewarePush()).
|
||||
Middleware(immutable.Middleware()).
|
||||
Middleware(blob.PutManifestMiddleware()).
|
||||
HandlerFunc(putManifest)
|
||||
// initiate blob upload
|
||||
|
Loading…
Reference in New Issue
Block a user