diff --git a/src/core/filter/readonly.go b/src/core/filter/readonly.go deleted file mode 100644 index 236adede5..000000000 --- a/src/core/filter/readonly.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 Project Harbor Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filter - -import ( - "net/http" - "regexp" - - "github.com/astaxie/beego/context" - "github.com/goharbor/harbor/src/common/utils/log" - "github.com/goharbor/harbor/src/core/config" -) - -const ( - repoURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)(?:[a-z0-9]+(?:[._-][a-z0-9]+)*)$` - tagsURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags$` - tagURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})$` - labelURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})/labels/[0-9]+$` -) - -// ReadonlyFilter filters the deletion or creation (e.g. retag) of repo/tag requests and returns 503. -func ReadonlyFilter(ctx *context.Context) { - filter(ctx.Request, ctx.ResponseWriter) -} - -func filter(req *http.Request, resp http.ResponseWriter) { - if !config.ReadOnly() { - return - } - - if matchRepoTagDelete(req) || matchRetag(req) { - resp.WriteHeader(http.StatusServiceUnavailable) - _, err := resp.Write([]byte("The system is in read only mode. Any modification is prohibited.")) - if err != nil { - log.Errorf("failed to write response body: %v", err) - } - } -} - -// matchRepoTagDelete checks whether a request is a repository or tag deletion request, -// it should be blocked in read-only mode. -func matchRepoTagDelete(req *http.Request) bool { - if req.Method != http.MethodDelete { - return false - } - - if inWhiteList(req) { - return false - } - - re := regexp.MustCompile(tagURL) - s := re.FindStringSubmatch(req.URL.Path) - if len(s) == 3 { - return true - } - - re = regexp.MustCompile(repoURL) - s = re.FindStringSubmatch(req.URL.Path) - if len(s) == 2 { - return true - } - - return false -} - -// matchRetag checks whether a request is a retag request, it should be blocked in read-only mode. -func matchRetag(req *http.Request) bool { - if req.Method != http.MethodPost { - return false - } - - re := regexp.MustCompile(tagsURL) - return re.MatchString(req.URL.Path) -} - -func inWhiteList(req *http.Request) bool { - re := regexp.MustCompile(labelURL) - s := re.FindStringSubmatch(req.URL.Path) - if len(s) == 3 { - return true - } - return false -} diff --git a/src/core/filter/readonly_test.go b/src/core/filter/readonly_test.go deleted file mode 100644 index e1d81e239..000000000 --- a/src/core/filter/readonly_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 Project Harbor Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filter - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/core/config" - "github.com/stretchr/testify/assert" -) - -func TestReadonlyFilter(t *testing.T) { - - var defaultConfig = map[string]interface{}{ - common.ReadOnly: true, - } - config.Upload(defaultConfig) - - assert := assert.New(t) - req1, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/ubuntu", nil) - rec := httptest.NewRecorder() - filter(req1, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) - - req2, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world", nil) - rec = httptest.NewRecorder() - filter(req2, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) - - req3, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/14.04", nil) - rec = httptest.NewRecorder() - filter(req3, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) - - req4, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/latest", nil) - rec = httptest.NewRecorder() - filter(req4, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) - - req5, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/vmware/hello-world", nil) - rec = httptest.NewRecorder() - filter(req5, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) - - req6, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil) - rec = httptest.NewRecorder() - filter(req6, rec) - assert.Equal(http.StatusServiceUnavailable, rec.Code) -} - -func TestMatchRetag(t *testing.T) { - req1, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil) - assert.True(t, matchRetag(req1)) - - req2, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/v1.0", nil) - assert.False(t, matchRetag(req2)) - - req3, _ := http.NewRequest("GET", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil) - assert.False(t, matchRetag(req3)) - - req4, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world", nil) - assert.False(t, matchRetag(req4)) -} diff --git a/src/core/filter/security.go b/src/core/filter/security.go index e0470358e..d1bf7b5b1 100644 --- a/src/core/filter/security.go +++ b/src/core/filter/security.go @@ -21,7 +21,6 @@ import ( "strings" beegoctx "github.com/astaxie/beego/context" - "github.com/docker/distribution/reference" "github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common/api" "github.com/goharbor/harbor/src/common/dao" @@ -60,30 +59,6 @@ const ( var ( reqCtxModifiers []ReqCtxModifier - // basic auth request context modifier only takes effect on the patterns - // in the slice - basicAuthReqPatterns = []*pathMethod{ - // create project - { - path: "/api/projects", - method: http.MethodPost, - }, - // token service - { - path: "/service/token", - method: http.MethodGet, - }, - // delete repository - { - path: "/api/repositories/" + reference.NameRegexp.String(), - method: http.MethodDelete, - }, - // delete tag - { - path: "/api/repositories/" + reference.NameRegexp.String() + "/tags/" + reference.TagRegexp.String(), - method: http.MethodDelete, - }, - } ) // Init ReqCtxMofiers list diff --git a/src/core/main.go b/src/core/main.go index 1d485f176..cd90737e2 100755 --- a/src/core/main.go +++ b/src/core/main.go @@ -229,7 +229,6 @@ func main() { filter.Init() beego.InsertFilter("/api/*", beego.BeforeStatic, filter.SessionCheck) beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter) - beego.InsertFilter("/*", beego.BeforeRouter, filter.ReadonlyFilter) server.RegisterRoutes()