mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
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:
parent
5f65afff0a
commit
3bb574db35
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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"
|
"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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user