0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-02-17 23:45:36 -05:00

Merge pull request #114 from rchincha/ro

auth: support a read-only mode
This commit is contained in:
Ramkumar Chinchani 2020-07-13 10:09:03 -07:00 committed by GitHub
commit e639b4814e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 2 deletions

View file

@ -91,6 +91,12 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc {
authFail(w, realm, 5) authFail(w, realm, 5)
return return
} }
if (r.Method != http.MethodGet && r.Method != http.MethodHead) && c.Config.HTTP.ReadOnly {
// Reject modification requests in read-only mode
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// Process request // Process request
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
@ -175,6 +181,12 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc {
return return
} }
if (r.Method != http.MethodGet && r.Method != http.MethodHead) && c.Config.HTTP.ReadOnly {
// Reject modification requests in read-only mode
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
basicAuth := r.Header.Get("Authorization") basicAuth := r.Header.Get("Authorization")
if basicAuth == "" { if basicAuth == "" {
authFail(w, realm, delay) authFail(w, realm, delay)

View file

@ -46,6 +46,7 @@ type HTTPConfig struct {
Auth *AuthConfig Auth *AuthConfig
Realm string Realm string
AllowReadAccess bool `mapstructure:",omitempty"` AllowReadAccess bool `mapstructure:",omitempty"`
ReadOnly bool `mapstructure:",omitempty"`
} }
type LDAPConfig struct { type LDAPConfig struct {

View file

@ -33,10 +33,12 @@ const (
BaseURL1 = "http://127.0.0.1:8081" BaseURL1 = "http://127.0.0.1:8081"
BaseURL2 = "http://127.0.0.1:8082" BaseURL2 = "http://127.0.0.1:8082"
BaseURL3 = "http://127.0.0.1:8083" BaseURL3 = "http://127.0.0.1:8083"
BaseURL4 = "http://127.0.0.1:8084"
BaseSecureURL2 = "https://127.0.0.1:8082" BaseSecureURL2 = "https://127.0.0.1:8082"
SecurePort1 = "8081" SecurePort1 = "8081"
SecurePort2 = "8082" SecurePort2 = "8082"
SecurePort3 = "8083" SecurePort3 = "8083"
SecurePort4 = "8084"
username = "test" username = "test"
passphrase = "test" passphrase = "test"
ServerCert = "../../test/data/server.cert" ServerCert = "../../test/data/server.cert"
@ -44,6 +46,7 @@ const (
CACert = "../../test/data/ca.crt" CACert = "../../test/data/ca.crt"
AuthorizedNamespace = "everyone/isallowed" AuthorizedNamespace = "everyone/isallowed"
UnauthorizedNamespace = "fortknox/notallowed" UnauthorizedNamespace = "fortknox/notallowed"
ALICE = "alice"
) )
type ( type (
@ -109,8 +112,8 @@ func TestNew(t *testing.T) {
func TestHtpasswdSingleCred(t *testing.T) { func TestHtpasswdSingleCred(t *testing.T) {
Convey("Single cred", t, func() { Convey("Single cred", t, func() {
singleCredtests := []string{} singleCredtests := []string{}
user := "alice" user := ALICE
password := "alice" password := ALICE
singleCredtests = append(singleCredtests, getCredString(user, password)) singleCredtests = append(singleCredtests, getCredString(user, password))
singleCredtests = append(singleCredtests, getCredString(user, password)+"\n") singleCredtests = append(singleCredtests, getCredString(user, password)+"\n")
@ -1439,3 +1442,76 @@ func parseBearerAuthHeader(authHeaderRaw string) *authHeader {
return &h return &h
} }
func TestHTTPReadOnly(t *testing.T) {
Convey("Single cred", t, func() {
singleCredtests := []string{}
user := ALICE
password := ALICE
singleCredtests = append(singleCredtests, getCredString(user, password))
singleCredtests = append(singleCredtests, getCredString(user, password)+"\n")
for _, testString := range singleCredtests {
func() {
config := api.NewConfig()
config.HTTP.Port = SecurePort4
// enable read-only mode
config.HTTP.ReadOnly = true
htpasswdPath := makeHtpasswdFileFromString(testString)
defer os.Remove(htpasswdPath)
config.HTTP.Auth = &api.AuthConfig{
HTPasswd: api.AuthHTPasswd{
Path: htpasswdPath,
},
}
c := api.NewController(config)
dir, err := ioutil.TempDir("", "oci-repo-test")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
c.Config.Storage.RootDirectory = dir
go func(controller *api.Controller) {
// this blocks
if err := controller.Run(); err != nil {
return
}
}(c)
// wait till ready
for {
_, err := resty.R().Get(BaseURL4)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
defer func(controller *api.Controller) {
ctx := context.Background()
_ = controller.Server.Shutdown(ctx)
}(c)
// with creds, should get expected status code
resp, _ := resty.R().SetBasicAuth(user, password).Get(BaseURL4 + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
// with creds, should get expected status code
resp, _ = resty.R().SetBasicAuth(user, password).Get(BaseURL4 + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
// with creds, any modifications should still fail on read-only mode
resp, err = resty.R().SetBasicAuth(user, password).
Post(BaseURL4 + "/v2/" + AuthorizedNamespace + "/blobs/uploads/")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 405)
//with invalid creds, it should fail
resp, _ = resty.R().SetBasicAuth("chuck", "chuck").Get(BaseURL4 + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 401)
}()
}
})
}