use delete manifest to handle immutable and signature

1, Use signature manager to get signature
2, Check the immutable and signature status when deleting.
3, Remove the immutable middleware for delelte manifest

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2020-03-02 14:54:15 +08:00
parent 5f65afff0a
commit 3bb574db35
6 changed files with 11 additions and 139 deletions

View File

@ -1,11 +1,8 @@
package contenttrust package contenttrust
import ( 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" 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" serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware" "github.com/goharbor/harbor/src/server/middleware"
"net/http" "net/http"
@ -27,7 +24,7 @@ func Middleware() func(http.Handler) http.Handler {
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
next.ServeHTTP(rec, req) next.ServeHTTP(rec, req)
if rec.Result().StatusCode == http.StatusOK { if rec.Result().StatusCode == http.StatusOK {
match, err := matchNotaryDigest(mf) match, err := isArtifactSigned(req, mf)
if err != nil { if err != nil {
serror.SendError(rw, err) serror.SendError(rw, err)
return return
@ -61,36 +58,12 @@ func validate(req *http.Request) (bool, middleware.ArtifactInfo) {
return true, af return true, af
} }
func matchNotaryDigest(af middleware.ArtifactInfo) (bool, error) { // isArtifactSigned use the sign manager to check the signature, it could handle pull by tag or digtest
if NotaryEndpoint == "" { // if pull by digest, any tag of the artifact is signed, will return true.
NotaryEndpoint = config.InternalNotaryEndpoint() func isArtifactSigned(req *http.Request, art middleware.ArtifactInfo) (bool, error) {
} checker, err := signature.GetManager().GetCheckerByRepo(req.Context(), art.Repository)
targets, err := notary.GetInternalTargets(NotaryEndpoint, util.TokenUsername, af.Repository)
if err != nil { if err != nil {
return false, err return false, err
} }
for _, t := range targets { return checker.IsArtifactSigned(art.Digest), nil
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
} }

View File

@ -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
}

View File

@ -1,3 +0,0 @@
package immutable
// Tests are in the push_mf_test

View File

@ -12,8 +12,8 @@ import (
"net/http" "net/http"
) )
// MiddlewarePush ... // Middleware ...
func MiddlewarePush() func(http.Handler) http.Handler { func Middleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if err := handlePush(req); err != nil { if err := handlePush(req); err != nil {

View File

@ -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(internal_orm.NewContext(context.TODO(), dao.GetOrmer())))
*req = *(req.WithContext(context.WithValue(req.Context(), middleware.ArtifactInfoKey, afInfo))) *req = *(req.WithContext(context.WithValue(req.Context(), middleware.ArtifactInfoKey, afInfo)))
h := MiddlewarePush()(n) h := Middleware()(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.ServeHTTP(util.NewCustomResponseWriter(rr), req) h.ServeHTTP(util.NewCustomResponseWriter(rr), req)
return rr.Code return rr.Code
@ -203,12 +173,6 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
code2 := doPutManifestRequest(projectID, projectName, "photon", "latest", dgt) code2 := doPutManifestRequest(projectID, projectName, "photon", "latest", dgt)
suite.Equal(http.StatusCreated, code2) 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) { func TestMain(m *testing.M) {

View File

@ -58,12 +58,11 @@ func RegisterRoutes() {
root.NewRoute(). root.NewRoute().
Method(http.MethodDelete). Method(http.MethodDelete).
Path("/*/manifests/:reference"). Path("/*/manifests/:reference").
Middleware(immutable.MiddlewareDelete()).
HandlerFunc(deleteManifest) HandlerFunc(deleteManifest)
root.NewRoute(). root.NewRoute().
Method(http.MethodPut). Method(http.MethodPut).
Path("/*/manifests/:reference"). Path("/*/manifests/:reference").
Middleware(immutable.MiddlewarePush()). Middleware(immutable.Middleware()).
Middleware(blob.PutManifestMiddleware()). Middleware(blob.PutManifestMiddleware()).
HandlerFunc(putManifest) HandlerFunc(putManifest)
// initiate blob upload // initiate blob upload