mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +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
|
||||
|
||||
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