mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 01:27:49 +01:00
Merge pull request #10490 from wy65701436/middleware-readonly
move readonly middleware to new v2 handler
This commit is contained in:
commit
6e3733aa7f
@ -89,6 +89,9 @@ const (
|
|||||||
PreconditionCode = "PRECONDITION"
|
PreconditionCode = "PRECONDITION"
|
||||||
// GeneralCode ...
|
// GeneralCode ...
|
||||||
GeneralCode = "UNKNOWN"
|
GeneralCode = "UNKNOWN"
|
||||||
|
|
||||||
|
// DENIED it's used by middleware(readonly, vul and content trust) and returned to docker client to index the request is denied.
|
||||||
|
DENIED = "DENIED"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New ...
|
// New ...
|
||||||
|
27
src/server/middleware/readonly.go
Normal file
27
src/server/middleware/readonly.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
internal_errors "github.com/goharbor/harbor/src/internal/error"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readonlyHandler struct {
|
||||||
|
next http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOnly middleware reject request when harbor set to readonly
|
||||||
|
func ReadOnly() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if config.ReadOnly() {
|
||||||
|
log.Warningf("The request is prohibited in readonly mode, url is: %s", req.URL.Path)
|
||||||
|
pkgE := internal_errors.New(nil).WithCode(internal_errors.DENIED).WithMessage("The system is in read only mode. Any modification is prohibited.")
|
||||||
|
http.Error(rw, internal_errors.NewErrs(pkgE).Error(), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
50
src/server/middleware/readonly_test.go
Normal file
50
src/server/middleware/readonly_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
config2 "github.com/goharbor/harbor/src/common/config"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
conf := map[string]interface{}{
|
||||||
|
common.ReadOnly: "true",
|
||||||
|
}
|
||||||
|
kp := &config2.PresetKeyProvider{Key: "naa4JtarA1Zsc3uY"}
|
||||||
|
config.InitWithSettings(conf, kp)
|
||||||
|
result := m.Run()
|
||||||
|
if result != 0 {
|
||||||
|
os.Exit(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadOnly(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
// delete
|
||||||
|
req := httptest.NewRequest(http.MethodDelete, "/readonly1", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
ReadOnly()(next).ServeHTTP(rec, req)
|
||||||
|
assert.Equal(rec.Code, http.StatusForbidden)
|
||||||
|
|
||||||
|
update := map[string]interface{}{
|
||||||
|
common.ReadOnly: "false",
|
||||||
|
}
|
||||||
|
config.GetCfgManager().UpdateConfig(update)
|
||||||
|
|
||||||
|
req2 := httptest.NewRequest(http.MethodDelete, "/readonly2", nil)
|
||||||
|
rec2 := httptest.NewRecorder()
|
||||||
|
ReadOnly()(next).ServeHTTP(rec2, req2)
|
||||||
|
assert.Equal(rec2.Code, http.StatusOK)
|
||||||
|
|
||||||
|
}
|
22
src/server/registry/blob/blob.go
Normal file
22
src/server/registry/blob/blob.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package blob
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHandler returns the handler to handler catalog request
|
||||||
|
func NewHandler(proxy *httputil.ReverseProxy) http.Handler {
|
||||||
|
return &handler{
|
||||||
|
proxy: proxy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
proxy *httputil.ReverseProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP ...
|
||||||
|
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
h.proxy.ServeHTTP(w, req)
|
||||||
|
}
|
1
src/server/registry/blob/blob_test.go
Normal file
1
src/server/registry/blob/blob_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package blob
|
@ -16,6 +16,8 @@ package registry
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/server/registry/blob"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -45,14 +47,20 @@ func New(url *url.URL) http.Handler {
|
|||||||
// handle manifest
|
// handle manifest
|
||||||
// TODO maybe we should split it into several sub routers based on the method
|
// TODO maybe we should split it into several sub routers based on the method
|
||||||
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
|
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
|
||||||
manifestRouter.NewRoute().Methods(http.MethodGet, http.MethodHead, http.MethodPut, http.MethodDelete).
|
manifestRouter.NewRoute().Methods(http.MethodGet).Handler(manifest.NewHandler(project.Mgr, proxy))
|
||||||
Handler(manifest.NewHandler(project.Mgr, proxy))
|
manifestRouter.NewRoute().Methods(http.MethodHead).Handler(manifest.NewHandler(project.Mgr, proxy))
|
||||||
|
manifestRouter.NewRoute().Methods(http.MethodPut).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
||||||
|
manifestRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
||||||
|
|
||||||
// handle blob
|
// handle blob
|
||||||
// as we need to apply middleware to the blob requests, so create a sub router to handle the blob APIs
|
// as we need to apply middleware to the blob requests, so create a sub router to handle the blob APIs
|
||||||
blobRouter := rootRouter.PathPrefix("/v2/{name:.*}/blobs/").Subrouter()
|
blobRouter := rootRouter.PathPrefix("/v2/{name:.*}/blobs/").Subrouter()
|
||||||
blobRouter.NewRoute().Methods(http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete).
|
blobRouter.NewRoute().Methods(http.MethodGet).Handler(blob.NewHandler(proxy))
|
||||||
Handler(proxy)
|
blobRouter.NewRoute().Methods(http.MethodHead).Handler(blob.NewHandler(proxy))
|
||||||
|
blobRouter.NewRoute().Methods(http.MethodPost).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), middleware.ReadOnly()))
|
||||||
|
blobRouter.NewRoute().Methods(http.MethodPut).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), middleware.ReadOnly()))
|
||||||
|
blobRouter.NewRoute().Methods(http.MethodPatch).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), middleware.ReadOnly()))
|
||||||
|
blobRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), middleware.ReadOnly()))
|
||||||
|
|
||||||
// all other APIs are proxy to the backend docker registry
|
// all other APIs are proxy to the backend docker registry
|
||||||
rootRouter.PathPrefix("/").Handler(proxy)
|
rootRouter.PathPrefix("/").Handler(proxy)
|
||||||
|
Loading…
Reference in New Issue
Block a user