feat(groups)!: added "groups" mechanism for authZ (#1123)

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>
This commit is contained in:
Lisca Ana-Roberta
2023-03-08 21:47:15 +02:00
committed by GitHub
parent 79783b4b06
commit 336526065f
17 changed files with 1023 additions and 358 deletions
+18 -14
View File
@@ -57,14 +57,16 @@ func TestConfigReloader(t *testing.T) {
"failDelay": 1
},
"accessControl": {
"**": {
"policies": [
{
"users": ["charlie"],
"actions": ["read"]
"repositories": {
"**": {
"policies": [
{
"users": ["charlie"],
"actions": ["read"]
}
],
"defaultPolicy": ["read", "create"]
}
],
"defaultPolicy": ["read", "create"]
},
"adminPolicy": {
"users": ["admin"],
@@ -113,14 +115,16 @@ func TestConfigReloader(t *testing.T) {
"failDelay": 1
},
"accessControl": {
"**": {
"policies": [
{
"users": ["alice"],
"actions": ["read", "create", "update", "delete"]
"repositories": {
"**": {
"policies": [
{
"users": ["alice"],
"actions": ["read", "create", "update", "delete"]
}
],
"defaultPolicy": ["read"]
}
],
"defaultPolicy": ["read"]
},
"adminPolicy": {
"users": ["admin"],
+5 -12
View File
@@ -363,7 +363,7 @@ func validateConfiguration(config *config.Config) error {
}
// check authorization config, it should have basic auth enabled or ldap
if config.HTTP.RawAccessControl != nil {
if config.HTTP.AccessControl != nil {
// checking for anonymous policy only authorization config: no users, no policies but anonymous policy
if err := validateAuthzPolicies(config); err != nil {
return err
@@ -405,8 +405,8 @@ func validateConfiguration(config *config.Config) error {
}
// check glob patterns in authz config are compilable
if config.AccessControl != nil {
for pattern := range config.AccessControl.Repositories {
if config.HTTP.AccessControl != nil {
for pattern := range config.HTTP.AccessControl.Repositories {
ok := glob.ValidatePattern(pattern)
if !ok {
log.Error().Err(glob.ErrBadPattern).Str("pattern", pattern).Msg("authorization pattern could not be compiled")
@@ -603,13 +603,6 @@ func LoadConfiguration(config *config.Config, configPath string) error {
return errors.ErrBadConfig
}
err := config.LoadAccessControlConfig(viperInstance)
if err != nil {
log.Error().Err(err).Msg("unable to unmarshal config's accessControl")
return err
}
// defaults
applyDefaultValues(config, viperInstance)
@@ -625,7 +618,7 @@ func LoadConfiguration(config *config.Config, configPath string) error {
}
func authzContainsOnlyAnonymousPolicy(cfg *config.Config) bool {
adminPolicy := cfg.AccessControl.AdminPolicy
adminPolicy := cfg.HTTP.AccessControl.AdminPolicy
anonymousPolicyPresent := false
log.Info().Msg("checking if anonymous authorization is the only type of authorization policy configured")
@@ -636,7 +629,7 @@ func authzContainsOnlyAnonymousPolicy(cfg *config.Config) bool {
return false
}
for _, repository := range cfg.AccessControl.Repositories {
for _, repository := range cfg.HTTP.AccessControl.Repositories {
if len(repository.DefaultPolicy) > 0 {
log.Info().Interface("repository", repository).
Msg("default policy detected, anonymous authorization is not the only authorization policy configured")
+20 -14
View File
@@ -681,7 +681,7 @@ func TestVerify(t *testing.T) {
defer os.Remove(tmpfile.Name()) // clean up
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"accessControl":{"adminPolicy":{"users":["admin"],
"accessControl":{"repositories":{},"adminPolicy":{"users":["admin"],
"actions":["read","create","update","delete"]}}}}`)
_, err = tmpfile.Write(content)
So(err, ShouldBeNil)
@@ -698,7 +698,7 @@ func TestVerify(t *testing.T) {
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"htpasswd":{"path":"test/data/htpasswd"},"failDelay":1},
"accessControl":{"adminPolicy":{"users":["admin"],
"accessControl":{"repositories":{},"adminPolicy":{"users":["admin"],
"actions":["read","create","update","delete"]}}}}`)
_, err = tmpfile.Write(content)
So(err, ShouldBeNil)
@@ -714,8 +714,8 @@ func TestVerify(t *testing.T) {
defer os.Remove(tmpfile.Name()) // clean up
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"accessControl":{"**":{"anonymousPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]}
"accessControl":{"repositories":{"**":{"anonymousPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]}}
}}}`)
_, err = tmpfile.Write(content)
So(err, ShouldBeNil)
@@ -732,8 +732,10 @@ func TestVerify(t *testing.T) {
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"accessControl":{
"**":{"defaultPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]},
"repositories":{
"**":{"defaultPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]},
},
"adminPolicy":{
"users":["admin"],
"actions":["read","create","update","delete"]
@@ -755,8 +757,10 @@ func TestVerify(t *testing.T) {
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"accessControl":{
"**":{"defaultPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]}
"repositories": {
"**":{"defaultPolicy": ["read", "create"]},
"/repo":{"anonymousPolicy": ["read", "create"]}
}
}
}}`)
_, err = tmpfile.Write(content)
@@ -774,12 +778,14 @@ func TestVerify(t *testing.T) {
content := []byte(`{"storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"accessControl":{
"/repo":{"anonymousPolicy": ["read", "create"]},
"/repo2":{
"policies": [{
"users": ["charlie"],
"actions": ["read", "create", "update"]
}]
"repositories": {
"/repo":{"anonymousPolicy": ["read", "create"]},
"/repo2":{
"policies": [{
"users": ["charlie"],
"actions": ["read", "create", "update"]
}]
}
}
}
}}`)