0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/extensions/extensions_test.go

1001 lines
31 KiB
Go
Raw Normal View History

//go:build sync || metrics || mgmt || apikey
// +build sync metrics mgmt apikey
package extensions_test
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"path"
"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"
"zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/storage/local"
"zotregistry.io/zot/pkg/test"
)
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)
}
defaultValue := true
mockOIDCServer, err := test.MockOIDCRun()
if err != nil {
panic(err)
}
defer func() {
err := mockOIDCServer.Shutdown()
if err != nil {
panic(err)
}
}()
mockOIDCConfig := mockOIDCServer.Config()
Convey("Verify mgmt route enabled with htpasswd", t, func() {
htpasswdPath := test.MakeHtpasswdFile()
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
conf.Extensions = &extconf.ExtensionConfig{}
conf.Extensions.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
Convey("unsupported http method call", func() {
// without credentials
resp, err := resty.R().Patch(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusMethodNotAllowed)
})
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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.FullMgmtPrefix)
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.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
})
Convey("Verify mgmt route enabled with ldap", t, func() {
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
BindDN: "binddn",
BaseDN: "basedn",
Address: "ldapexample",
}
conf.Extensions = &extconf.ExtensionConfig{}
conf.Extensions.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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 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["dex"] = 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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()
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
// without credentials
resp, err := resty.R().Get(baseURL + constants.FullMgmtPrefix)
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 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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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.FullMgmtPrefix)
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)
data, _ := os.ReadFile(logFile.Name())
So(string(data), ShouldContainSubstring, "setting up mgmt routes")
})
Convey("Verify mgmt route enabled for uploading certificates and public keys", 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"
imageStore := local.NewImageStore(globalDir, false, 0, false, false,
log.NewLogger("debug", logFile.Name()), monitoring.NewMetricsServer(false,
log.NewLogger("debug", logFile.Name())), nil, nil)
storeController := storage.StoreController{
DefaultStore: imageStore,
}
config, layers, manifest, err := test.GetRandomImageComponents(10)
So(err, ShouldBeNil)
err = test.WriteImageToFileSystem(
test.Image{
Manifest: manifest,
Layers: layers,
Config: config,
Reference: "0.0.1",
}, "repo", storeController,
)
So(err, ShouldBeNil)
sigConfig, sigLayers, sigManifest, err := test.GetRandomImageComponents(10)
So(err, ShouldBeNil)
ref, _ := test.GetCosignSignatureTagForManifest(manifest)
err = test.WriteImageToFileSystem(
test.Image{
Manifest: sigManifest,
Layers: sigLayers,
Config: sigConfig,
Reference: ref,
}, "repo", storeController,
)
So(err, ShouldBeNil)
conf.Extensions = &extconf.ExtensionConfig{}
conf.Extensions.Search = &extconf.SearchConfig{}
conf.Extensions.Search.Enable = &defaultValue
conf.Extensions.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
Enable: &defaultValue,
},
}
conf.Log.Output = logFile.Name()
defer os.Remove(logFile.Name()) // cleanup
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = globalDir
ctlrManager := test.NewControllerManager(ctlr)
ctlrManager.StartAndWait(port)
defer ctlrManager.StopServer()
rootDir := t.TempDir()
test.NotationPathLock.Lock()
defer test.NotationPathLock.Unlock()
test.LoadNotationPath(rootDir)
// generate a keypair
err = test.GenerateNotationCerts(rootDir, "test")
So(err, ShouldBeNil)
certificateContent, err := os.ReadFile(path.Join(rootDir, "notation/localkeys", "test.crt"))
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
client := resty.New()
resp, err := client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "notation").SetQueryParam("truststoreName", "test").
SetBody(certificateContent).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
resp, err = client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "notation").
SetBody(certificateContent).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "notation").SetQueryParam("truststoreName", "").
SetBody(certificateContent).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "notation").SetQueryParam("truststoreName", "test").
SetQueryParam("truststoreType", "signatureAuthority").
SetBody([]byte("wrong content")).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
resp, err = client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "invalidTool").
SetBody(certificateContent).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetHeader("Content-type", "application/octet-stream").
SetQueryParam("resource", "signatures").SetQueryParam("tool", "cosign").
SetBody([]byte("wrong content")).Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
resp, err = client.R().SetQueryParam("resource", "signatures").SetQueryParam("tool", "cosign").
Get(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetQueryParam("resource", "signatures").Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetQueryParam("resource", "config").Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = client.R().SetQueryParam("resource", "invalid").Post(baseURL + constants.FullMgmtPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
found, err := test.ReadLogFileAndSearchString(logFile.Name(), "setting up mgmt routes", time.Second)
So(err, ShouldBeNil)
So(found, ShouldBeTrue)
found, err = test.ReadLogFileAndSearchString(logFile.Name(), "updating signatures validity", 10*time.Second)
So(err, ShouldBeNil)
So(found, ShouldBeTrue)
found, err = test.ReadLogFileAndSearchString(logFile.Name(), "verifying signatures successfully completed",
time.Second)
So(err, ShouldBeNil)
So(found, ShouldBeTrue)
})
}
func TestMgmtWithBearer(t *testing.T) {
Convey("Make a new controller", t, func() {
authorizedNamespace := "allowedrepo"
unauthorizedNamespace := "notallowedrepo"
authTestServer := test.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.Mgmt = &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{
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 := test.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 test.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 = test.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 = test.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 test.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.FullMgmtPrefix)
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.FullMgmtPrefix)
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{
Mgmt: &extconf.MgmtConfig{
BaseConfig: extconf.BaseConfig{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.FullMgmtPrefix)
So(resp, ShouldNotBeNil)
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,POST,OPTIONS")
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
})
}
func TestAllowedMethodsHeaderAPIKey(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{
APIKey: &extconf.APIKeyConfig{
BaseConfig: extconf.BaseConfig{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.FullAPIKeyPrefix)
So(resp, ShouldNotBeNil)
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "POST,DELETE,OPTIONS")
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
})
}