Skip to content

Commit

Permalink
Merge pull request goharbor#10490 from wy65701436/middleware-readonly
Browse files Browse the repository at this point in the history
move readonly middleware to new v2 handler
ywk253100 authored Jan 17, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents f55ee0a + 935303a commit 6e3733a
Showing 6 changed files with 115 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/internal/error/errors.go
Original file line number Diff line number Diff line change
@@ -89,6 +89,9 @@ const (
PreconditionCode = "PRECONDITION"
// GeneralCode ...
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 ...
27 changes: 27 additions & 0 deletions src/server/middleware/readonly.go
Original file line number Diff line number Diff line change
@@ -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 changes: 50 additions & 0 deletions src/server/middleware/readonly_test.go
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions src/server/registry/blob/blob.go
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions src/server/registry/blob/blob_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package blob
16 changes: 12 additions & 4 deletions src/server/registry/handler.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@ package registry

import (
"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/httputil"
"net/url"
@@ -45,14 +47,20 @@ func New(url *url.URL) http.Handler {
// handle manifest
// TODO maybe we should split it into several sub routers based on the method
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
manifestRouter.NewRoute().Methods(http.MethodGet, http.MethodHead, http.MethodPut, http.MethodDelete).
Handler(manifest.NewHandler(project.Mgr, proxy))
manifestRouter.NewRoute().Methods(http.MethodGet).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
// 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.NewRoute().Methods(http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete).
Handler(proxy)
blobRouter.NewRoute().Methods(http.MethodGet).Handler(blob.NewHandler(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
rootRouter.PathPrefix("/").Handler(proxy)

0 comments on commit 6e3733a

Please sign in to comment.