mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
ba6f347d8d
Which could be imported independently. See more details: 1. "zotregistry.io/zot/pkg/test/common" - currently used as tcommon "zotregistry.io/zot/pkg/test/common" - inside pkg/test test "zotregistry.io/zot/pkg/test/common" - in tests . "zotregistry.io/zot/pkg/test/common" - in tests Decouple zb from code in test/pkg in order to keep the size small. 2. "zotregistry.io/zot/pkg/test/image-utils" - curently used as . "zotregistry.io/zot/pkg/test/image-utils" 3. "zotregistry.io/zot/pkg/test/deprecated" - curently used as "zotregistry.io/zot/pkg/test/deprecated" This one will bre replaced gradually by image-utils in the future. 4. "zotregistry.io/zot/pkg/test/signature" - (cosign + notation) use as "zotregistry.io/zot/pkg/test/signature" 5. "zotregistry.io/zot/pkg/test/auth" - (bearer + oidc) curently used as authutils "zotregistry.io/zot/pkg/test/auth" 6. "zotregistry.io/zot/pkg/test/oci-utils" - curently used as ociutils "zotregistry.io/zot/pkg/test/oci-utils" Some unused functions were removed, some were replaced, and in a few cases specific funtions were moved to the files they were used in. Added an interface for the StoreController, this reduces the number of imports of the entire image store, decreasing binary size for tests. If the zb code was still coupled with pkg/test, this would have reflected in zb size. Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
935 lines
28 KiB
Go
935 lines
28 KiB
Go
//go:build sync && metrics && mgmt && userprefs && search
|
|
// +build sync,metrics,mgmt,userprefs,search
|
|
|
|
package extensions_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
"gopkg.in/resty.v1"
|
|
|
|
"zotregistry.io/zot/pkg/api"
|
|
"zotregistry.io/zot/pkg/api/config"
|
|
"zotregistry.io/zot/pkg/api/constants"
|
|
"zotregistry.io/zot/pkg/extensions"
|
|
extconf "zotregistry.io/zot/pkg/extensions/config"
|
|
syncconf "zotregistry.io/zot/pkg/extensions/config/sync"
|
|
authutils "zotregistry.io/zot/pkg/test/auth"
|
|
test "zotregistry.io/zot/pkg/test/common"
|
|
)
|
|
|
|
const (
|
|
ServerCert = "../../test/data/server.cert"
|
|
ServerKey = "../../test/data/server.key"
|
|
)
|
|
|
|
func TestEnableExtension(t *testing.T) {
|
|
Convey("Verify log if sync disabled in config", t, func() {
|
|
globalDir := t.TempDir()
|
|
port := test.GetFreePort()
|
|
conf := config.New()
|
|
falseValue := false
|
|
|
|
syncConfig := &syncconf.Config{
|
|
Enable: &falseValue,
|
|
Registries: []syncconf.RegistryConfig{},
|
|
}
|
|
|
|
// conf.Extensions.Sync.Enable = &falseValue
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Sync = syncConfig
|
|
conf.HTTP.Port = port
|
|
|
|
logFile, err := os.CreateTemp(globalDir, "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
conf.Log.Level = "info"
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
|
|
defer ctlrManager.StopServer()
|
|
|
|
ctlr.Config.Storage.RootDirectory = globalDir
|
|
|
|
ctlrManager.StartAndWait(port)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring,
|
|
"Sync registries config not provided or disabled, skipping sync")
|
|
})
|
|
}
|
|
|
|
func TestMetricsExtension(t *testing.T) {
|
|
Convey("Verify Metrics enabled for storage subpaths", t, func() {
|
|
globalDir := t.TempDir()
|
|
conf := config.New()
|
|
port := test.GetFreePort()
|
|
conf.HTTP.Port = port
|
|
|
|
logFile, err := os.CreateTemp(globalDir, "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
defaultValue := true
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Metrics = &extconf.MetricsConfig{
|
|
BaseConfig: extconf.BaseConfig{Enable: &defaultValue},
|
|
Prometheus: &extconf.PrometheusConfig{},
|
|
}
|
|
conf.Log.Level = "info"
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
|
|
subPaths := make(map[string]config.StorageConfig)
|
|
subPaths["/a"] = config.StorageConfig{
|
|
Dedupe: false,
|
|
RootDirectory: t.TempDir(),
|
|
}
|
|
|
|
ctlr.Config.Storage.RootDirectory = globalDir
|
|
ctlr.Config.Storage.SubPaths = subPaths
|
|
|
|
ctlrManager.StartAndWait(port)
|
|
|
|
data, _ := os.ReadFile(logFile.Name())
|
|
|
|
So(string(data), ShouldContainSubstring,
|
|
"Prometheus instrumentation Path not set, changing to '/metrics'.")
|
|
})
|
|
}
|
|
|
|
func TestMgmtExtension(t *testing.T) {
|
|
globalDir := t.TempDir()
|
|
conf := config.New()
|
|
port := test.GetFreePort()
|
|
conf.HTTP.Port = port
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp(globalDir, "zot-log*.txt")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
mgmtReadyTimeout := 5 * time.Second
|
|
|
|
defaultValue := true
|
|
|
|
mockOIDCServer, err := authutils.MockOIDCRun()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer func() {
|
|
err := mockOIDCServer.Shutdown()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
mockOIDCConfig := mockOIDCServer.Config()
|
|
|
|
Convey("Verify mgmt auth info route enabled with htpasswd", t, func() {
|
|
htpasswdPath := test.MakeHtpasswdFile()
|
|
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
subPaths := make(map[string]config.StorageConfig)
|
|
subPaths["/a"] = config.StorageConfig{RootDirectory: t.TempDir()}
|
|
|
|
ctlr.Config.Storage.RootDirectory = globalDir
|
|
ctlr.Config.Storage.SubPaths = subPaths
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
So(err, ShouldBeNil)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Patch(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusMethodNotAllowed)
|
|
|
|
// without credentials
|
|
resp, err = resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
|
|
// with credentials
|
|
resp, err = resty.R().SetBasicAuth("test", "test").Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp = extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
|
|
// with wrong credentials
|
|
resp, err = resty.R().SetBasicAuth("test", "wrong").Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with ldap", t, func() {
|
|
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
|
|
BindDN: "binddn",
|
|
BaseDN: "basedn",
|
|
Address: "ldapexample",
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
subPaths := make(map[string]config.StorageConfig)
|
|
subPaths["/a"] = config.StorageConfig{RootDirectory: t.TempDir()}
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
ctlr.Config.Storage.SubPaths = subPaths
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
// ldap is always nil, htpasswd should be populated when ldap is used
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with htpasswd + ldap", t, func() {
|
|
htpasswdPath := test.MakeHtpasswdFile()
|
|
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
|
|
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
|
|
BindDN: "binddn",
|
|
BaseDN: "basedn",
|
|
Address: "ldapexample",
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
subPaths := make(map[string]config.StorageConfig)
|
|
subPaths["/a"] = config.StorageConfig{RootDirectory: t.TempDir()}
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
ctlr.Config.Storage.SubPaths = subPaths
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
|
|
// with credentials
|
|
resp, err = resty.R().SetBasicAuth("test", "test").Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp = extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with htpasswd + ldap + bearer", t, func() {
|
|
htpasswdPath := test.MakeHtpasswdFile()
|
|
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
|
|
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
|
|
BindDN: "binddn",
|
|
BaseDN: "basedn",
|
|
Address: "ldapexample",
|
|
}
|
|
|
|
conf.HTTP.Auth.Bearer = &config.BearerConfig{
|
|
Realm: "realm",
|
|
Service: "service",
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, "realm")
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, "service")
|
|
|
|
// with credentials
|
|
resp, err = resty.R().SetBasicAuth("test", "test").Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp = extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, "realm")
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, "service")
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with ldap + bearer", t, func() {
|
|
conf.HTTP.Auth.HTPasswd.Path = ""
|
|
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
|
|
BindDN: "binddn",
|
|
BaseDN: "basedn",
|
|
Address: "ldapexample",
|
|
}
|
|
|
|
conf.HTTP.Auth.Bearer = &config.BearerConfig{
|
|
Realm: "realm",
|
|
Service: "service",
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
subPaths := make(map[string]config.StorageConfig)
|
|
subPaths["/a"] = config.StorageConfig{RootDirectory: t.TempDir()}
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
ctlr.Config.Storage.SubPaths = subPaths
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd.Path, ShouldEqual, "")
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, "realm")
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, "service")
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with bearer", t, func() {
|
|
conf.HTTP.Auth.HTPasswd.Path = ""
|
|
conf.HTTP.Auth.LDAP = nil
|
|
conf.HTTP.Auth.Bearer = &config.BearerConfig{
|
|
Realm: "realm",
|
|
Service: "service",
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, "realm")
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, "service")
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with openID", t, func() {
|
|
conf.HTTP.Auth.HTPasswd.Path = ""
|
|
conf.HTTP.Auth.LDAP = nil
|
|
conf.HTTP.Auth.Bearer = nil
|
|
|
|
openIDProviders := make(map[string]config.OpenIDProviderConfig)
|
|
openIDProviders["oidc"] = config.OpenIDProviderConfig{
|
|
ClientID: mockOIDCConfig.ClientID,
|
|
ClientSecret: mockOIDCConfig.ClientSecret,
|
|
Issuer: mockOIDCConfig.Issuer,
|
|
}
|
|
|
|
conf.HTTP.Auth.OpenID = &config.OpenIDConfig{
|
|
Providers: openIDProviders,
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
t.Logf("resp: %v", mgmtResp.HTTP.Auth.OpenID)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.OpenID, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.OpenID.Providers, ShouldNotBeEmpty)
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled with empty openID provider list", t, func() {
|
|
htpasswdPath := test.MakeHtpasswdFile()
|
|
|
|
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
|
|
conf.HTTP.Auth.LDAP = nil
|
|
conf.HTTP.Auth.Bearer = nil
|
|
|
|
openIDProviders := make(map[string]config.OpenIDProviderConfig)
|
|
|
|
conf.HTTP.Auth.OpenID = &config.OpenIDConfig{
|
|
Providers: openIDProviders,
|
|
}
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
// without credentials
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
t.Logf("resp: %v", mgmtResp.HTTP.Auth.OpenID)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.OpenID, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Verify mgmt auth info route enabled without any auth", t, func() {
|
|
globalDir := t.TempDir()
|
|
conf := config.New()
|
|
port := test.GetFreePort()
|
|
conf.HTTP.Port = port
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp(globalDir, "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
defaultValue := true
|
|
|
|
conf.Commit = "v1.0.0"
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Log.Output = logFile.Name()
|
|
defer os.Remove(logFile.Name()) // cleanup
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlrManager := test.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
resp, err := resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.DistSpecVersion, ShouldResemble, conf.DistSpecVersion)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
|
|
found, err := test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"setting up mgmt routes", mgmtReadyTimeout)
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = test.ReadLogFileAndSearchString(logFile.Name(),
|
|
"finished setting up mgmt routes", mgmtReadyTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestMgmtWithBearer(t *testing.T) {
|
|
Convey("Make a new controller", t, func() {
|
|
authorizedNamespace := "allowedrepo"
|
|
unauthorizedNamespace := "notallowedrepo"
|
|
authTestServer := authutils.MakeAuthTestServer(ServerKey, unauthorizedNamespace)
|
|
defer authTestServer.Close()
|
|
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
|
|
aurl, err := url.Parse(authTestServer.URL)
|
|
So(err, ShouldBeNil)
|
|
|
|
conf.HTTP.Auth = &config.AuthConfig{
|
|
Bearer: &config.BearerConfig{
|
|
Cert: ServerCert,
|
|
Realm: authTestServer.URL + "/auth/token",
|
|
Service: aurl.Host,
|
|
},
|
|
}
|
|
|
|
defaultValue := true
|
|
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultValue
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultValue
|
|
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
cm := test.NewControllerManager(ctlr)
|
|
cm.StartAndWait(port)
|
|
defer cm.StopServer()
|
|
|
|
resp, err := resty.R().Get(baseURL + "/v2/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
|
|
|
authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate"))
|
|
resp, err = resty.R().
|
|
SetQueryParam("service", authorizationHeader.Service).
|
|
SetQueryParam("scope", authorizationHeader.Scope).
|
|
Get(authorizationHeader.Realm)
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
var goodToken authutils.AccessTokenResponse
|
|
err = json.Unmarshal(resp.Body(), &goodToken)
|
|
So(err, ShouldBeNil)
|
|
|
|
resp, err = resty.R().
|
|
SetHeader("Authorization", fmt.Sprintf("Bearer %s", goodToken.AccessToken)).
|
|
Get(baseURL + "/v2/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
resp, err = resty.R().SetHeader("Authorization",
|
|
fmt.Sprintf("Bearer %s", goodToken.AccessToken)).Options(baseURL + "/v2/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
|
|
|
|
resp, err = resty.R().Post(baseURL + "/v2/" + authorizedNamespace + "/blobs/uploads/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
|
|
|
authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate"))
|
|
resp, err = resty.R().
|
|
SetQueryParam("service", authorizationHeader.Service).
|
|
SetQueryParam("scope", authorizationHeader.Scope).
|
|
Get(authorizationHeader.Realm)
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
err = json.Unmarshal(resp.Body(), &goodToken)
|
|
So(err, ShouldBeNil)
|
|
|
|
resp, err = resty.R().
|
|
SetHeader("Authorization", fmt.Sprintf("Bearer %s", goodToken.AccessToken)).
|
|
Post(baseURL + "/v2/" + authorizedNamespace + "/blobs/uploads/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
|
|
|
|
resp, err = resty.R().
|
|
Post(baseURL + "/v2/" + unauthorizedNamespace + "/blobs/uploads/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
|
|
|
authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate"))
|
|
resp, err = resty.R().
|
|
SetQueryParam("service", authorizationHeader.Service).
|
|
SetQueryParam("scope", authorizationHeader.Scope).
|
|
Get(authorizationHeader.Realm)
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
var badToken authutils.AccessTokenResponse
|
|
err = json.Unmarshal(resp.Body(), &badToken)
|
|
So(err, ShouldBeNil)
|
|
|
|
resp, err = resty.R().
|
|
SetHeader("Authorization", fmt.Sprintf("Bearer %s", badToken.AccessToken)).
|
|
Post(baseURL + "/v2/" + unauthorizedNamespace + "/blobs/uploads/")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
|
|
|
// test mgmt route
|
|
resp, err = resty.R().Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp := extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.DistSpecVersion, ShouldResemble, conf.DistSpecVersion)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, conf.HTTP.Auth.Bearer.Realm)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, conf.HTTP.Auth.Bearer.Service)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
|
|
resp, err = resty.R().SetBasicAuth("", "").Get(baseURL + constants.FullMgmt)
|
|
So(err, ShouldBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
mgmtResp = extensions.StrippedConfig{}
|
|
err = json.Unmarshal(resp.Body(), &mgmtResp)
|
|
So(err, ShouldBeNil)
|
|
So(mgmtResp.DistSpecVersion, ShouldResemble, conf.DistSpecVersion)
|
|
So(mgmtResp.HTTP.Auth.Bearer, ShouldNotBeNil)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Realm, ShouldEqual, conf.HTTP.Auth.Bearer.Realm)
|
|
So(mgmtResp.HTTP.Auth.Bearer.Service, ShouldEqual, conf.HTTP.Auth.Bearer.Service)
|
|
So(mgmtResp.HTTP.Auth.HTPasswd, ShouldBeNil)
|
|
So(mgmtResp.HTTP.Auth.LDAP, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestAllowedMethodsHeaderMgmt(t *testing.T) {
|
|
defaultVal := true
|
|
|
|
Convey("Test http options response", t, func() {
|
|
conf := config.New()
|
|
port := test.GetFreePort()
|
|
conf.HTTP.Port = port
|
|
conf.Extensions = &extconf.ExtensionConfig{}
|
|
conf.Extensions.Search = &extconf.SearchConfig{}
|
|
conf.Extensions.Search.Enable = &defaultVal
|
|
conf.Extensions.Search.CVE = nil
|
|
conf.Extensions.UI = &extconf.UIConfig{}
|
|
conf.Extensions.UI.Enable = &defaultVal
|
|
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
ctlr := api.NewController(conf)
|
|
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctrlManager := test.NewControllerManager(ctlr)
|
|
|
|
ctrlManager.StartAndWait(port)
|
|
defer ctrlManager.StopServer()
|
|
|
|
resp, _ := resty.R().Options(baseURL + constants.FullMgmt)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS")
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
|
|
})
|
|
}
|