mirror of
https://github.com/project-zot/zot.git
synced 2024-12-23 22:27:35 -05:00
336526065f
BREAKING CHANGE: repository paths are now specified under a new config key called "repositories" under "accessControl" section in order to handle "groups" feature. Previously the repository paths were specified directly under "accessControl". This PR adds the ability to create groups of users which can be used for authZ policies, instead of just users. { "http": { "accessControl": { "groups": { Just like the users, groups can be part of repository policies/default policies/admin policies. The 'groups' field in accessControl can be missing if there are no groups. The permissions priority is user>group>default>admin policy, verified in this order (in authz.go), and permissions are cumulative. It works with LDAP too, and the group attribute name is configurable. The DN of the group is used as the group name and the functionality is the same. All groups for the given user are added to the context in authn.go. Repository paths are now specified under a new keyword called "repositories" under "accessControl" section in order to handle "groups" feature. Signed-off-by: Ana-Roberta Lisca <ana.kagome@yahoo.com>
388 lines
8.8 KiB
Go
388 lines
8.8 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"zotregistry.io/zot/pkg/cli"
|
|
"zotregistry.io/zot/pkg/test"
|
|
)
|
|
|
|
func TestConfigReloader(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("reload access control config", t, func(c C) {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
username := "alice"
|
|
password := "alice"
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
usernameAndHash := fmt.Sprintf("%s:%s", username, string(hash))
|
|
|
|
htpasswdPath := test.MakeHtpasswdFileFromString(usernameAndHash)
|
|
defer os.Remove(htpasswdPath)
|
|
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"distSpecVersion": "1.1.0-dev",
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s",
|
|
"realm": "zot",
|
|
"auth": {
|
|
"htpasswd": {
|
|
"path": "%s"
|
|
},
|
|
"failDelay": 1
|
|
},
|
|
"accessControl": {
|
|
"repositories": {
|
|
"**": {
|
|
"policies": [
|
|
{
|
|
"users": ["charlie"],
|
|
"actions": ["read"]
|
|
}
|
|
],
|
|
"defaultPolicy": ["read", "create"]
|
|
}
|
|
},
|
|
"adminPolicy": {
|
|
"users": ["admin"],
|
|
"actions": ["read", "create", "update", "delete"]
|
|
}
|
|
}
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
}
|
|
}`, port, htpasswdPath, logFile.Name())
|
|
|
|
cfgfile, err := os.CreateTemp("", "zot-test*.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(cfgfile.Name()) // clean up
|
|
|
|
_, err = cfgfile.Write([]byte(content))
|
|
So(err, ShouldBeNil)
|
|
|
|
// err = cfgfile.Close()
|
|
// So(err, ShouldBeNil)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
|
|
test.WaitTillServerReady(baseURL)
|
|
|
|
content = fmt.Sprintf(`{
|
|
"distSpecVersion": "1.1.0-dev",
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s",
|
|
"realm": "zot",
|
|
"auth": {
|
|
"htpasswd": {
|
|
"path": "%s"
|
|
},
|
|
"failDelay": 1
|
|
},
|
|
"accessControl": {
|
|
"repositories": {
|
|
"**": {
|
|
"policies": [
|
|
{
|
|
"users": ["alice"],
|
|
"actions": ["read", "create", "update", "delete"]
|
|
}
|
|
],
|
|
"defaultPolicy": ["read"]
|
|
}
|
|
},
|
|
"adminPolicy": {
|
|
"users": ["admin"],
|
|
"actions": ["read", "create", "update", "delete"]
|
|
}
|
|
}
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
}
|
|
}`, port, htpasswdPath, logFile.Name())
|
|
|
|
err = cfgfile.Truncate(0)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.Seek(0, io.SeekStart)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.WriteString(content)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cfgfile.Close()
|
|
So(err, ShouldBeNil)
|
|
|
|
// wait for config reload
|
|
time.Sleep(2 * time.Second)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring, "reloaded params")
|
|
So(string(data), ShouldContainSubstring, "new configuration settings")
|
|
So(string(data), ShouldContainSubstring, "\"Users\":[\"alice\"]")
|
|
So(string(data), ShouldContainSubstring, "\"Actions\":[\"read\",\"create\",\"update\",\"delete\"]")
|
|
})
|
|
|
|
Convey("reload sync config", t, func(c C) {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"distSpecVersion": "1.1.0-dev",
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"registries": [{
|
|
"urls": ["http://localhost:8080"],
|
|
"tlsVerify": false,
|
|
"onDemand": true,
|
|
"maxRetries": 3,
|
|
"retryDelay": "15m",
|
|
"certDir": "",
|
|
"content":[
|
|
{
|
|
"prefix": "zot-test",
|
|
"tags": {
|
|
"regex": ".*",
|
|
"semver": true
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
}`, port, logFile.Name())
|
|
|
|
cfgfile, err := os.CreateTemp("", "zot-test*.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(cfgfile.Name()) // clean up
|
|
|
|
_, err = cfgfile.Write([]byte(content))
|
|
So(err, ShouldBeNil)
|
|
|
|
// err = cfgfile.Close()
|
|
// So(err, ShouldBeNil)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
|
|
test.WaitTillServerReady(baseURL)
|
|
|
|
content = fmt.Sprintf(`{
|
|
"distSpecVersion": "1.1.0-dev",
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"registries": [{
|
|
"urls": ["http://localhost:9999"],
|
|
"tlsVerify": true,
|
|
"onDemand": false,
|
|
"maxRetries": 10,
|
|
"retryDelay": "5m",
|
|
"certDir": "certs",
|
|
"content":[
|
|
{
|
|
"prefix": "zot-cve-test",
|
|
"tags": {
|
|
"regex": "tag",
|
|
"semver": false
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
}`, port, logFile.Name())
|
|
|
|
err = cfgfile.Truncate(0)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.Seek(0, io.SeekStart)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.WriteString(content)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cfgfile.Close()
|
|
So(err, ShouldBeNil)
|
|
|
|
// wait for config reload
|
|
time.Sleep(2 * time.Second)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring, "reloaded params")
|
|
So(string(data), ShouldContainSubstring, "new configuration settings")
|
|
So(string(data), ShouldContainSubstring, "\"URLs\":[\"http://localhost:9999\"]")
|
|
So(string(data), ShouldContainSubstring, "\"TLSVerify\":true")
|
|
So(string(data), ShouldContainSubstring, "\"OnDemand\":false")
|
|
So(string(data), ShouldContainSubstring, "\"MaxRetries\":10")
|
|
So(string(data), ShouldContainSubstring, "\"RetryDelay\":300000000000")
|
|
So(string(data), ShouldContainSubstring, "\"CertDir\":\"certs\"")
|
|
So(string(data), ShouldContainSubstring, "\"Prefix\":\"zot-cve-test\"")
|
|
So(string(data), ShouldContainSubstring, "\"Regex\":\"tag\"")
|
|
So(string(data), ShouldContainSubstring, "\"Semver\":false")
|
|
})
|
|
|
|
Convey("reload bad config", t, func(c C) {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"distSpecVersion": "1.1.0-dev",
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"registries": [{
|
|
"urls": ["http://localhost:8080"],
|
|
"tlsVerify": false,
|
|
"onDemand": true,
|
|
"maxRetries": 3,
|
|
"retryDelay": "15m",
|
|
"certDir": "",
|
|
"content":[
|
|
{
|
|
"prefix": "zot-test",
|
|
"tags": {
|
|
"regex": ".*",
|
|
"semver": true
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
}`, port, logFile.Name())
|
|
|
|
cfgfile, err := os.CreateTemp("", "zot-test*.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(cfgfile.Name()) // clean up
|
|
|
|
_, err = cfgfile.Write([]byte(content))
|
|
So(err, ShouldBeNil)
|
|
|
|
// err = cfgfile.Close()
|
|
// So(err, ShouldBeNil)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
|
|
test.WaitTillServerReady(baseURL)
|
|
|
|
content = "[]"
|
|
|
|
err = cfgfile.Truncate(0)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.Seek(0, io.SeekStart)
|
|
So(err, ShouldBeNil)
|
|
|
|
_, err = cfgfile.WriteString(content)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cfgfile.Close()
|
|
So(err, ShouldBeNil)
|
|
|
|
// wait for config reload
|
|
time.Sleep(2 * time.Second)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldNotContainSubstring, "reloaded params")
|
|
So(string(data), ShouldNotContainSubstring, "new configuration settings")
|
|
So(string(data), ShouldContainSubstring, "\"URLs\":[\"http://localhost:8080\"]")
|
|
So(string(data), ShouldContainSubstring, "\"TLSVerify\":false")
|
|
So(string(data), ShouldContainSubstring, "\"OnDemand\":true")
|
|
So(string(data), ShouldContainSubstring, "\"MaxRetries\":3")
|
|
So(string(data), ShouldContainSubstring, "\"CertDir\":\"\"")
|
|
So(string(data), ShouldContainSubstring, "\"Prefix\":\"zot-test\"")
|
|
So(string(data), ShouldContainSubstring, "\"Regex\":\".*\"")
|
|
So(string(data), ShouldContainSubstring, "\"Semver\":true")
|
|
})
|
|
}
|