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
+31 -63
View File
@@ -1,13 +1,11 @@
package config
import (
"fmt"
"os"
"time"
"github.com/getlantern/deepcopy"
distspec "github.com/opencontainers/distribution-spec/specs-go"
"github.com/spf13/viper"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/storage"
@@ -66,28 +64,29 @@ type RatelimitConfig struct {
}
type HTTPConfig struct {
Address string
Port string
AllowOrigin string // comma separated
TLS *TLSConfig
Auth *AuthConfig
RawAccessControl map[string]interface{} `mapstructure:"accessControl,omitempty"`
Realm string
Ratelimit *RatelimitConfig `mapstructure:",omitempty"`
Address string
Port string
AllowOrigin string // comma separated
TLS *TLSConfig
Auth *AuthConfig
AccessControl *AccessControlConfig
Realm string
Ratelimit *RatelimitConfig `mapstructure:",omitempty"`
}
type LDAPConfig struct {
Port int
Insecure bool
StartTLS bool // if !Insecure, then StartTLS or LDAPs
SkipVerify bool
SubtreeSearch bool
Address string
BindDN string
BindPassword string
BaseDN string
UserAttribute string
CACert string
Port int
Insecure bool
StartTLS bool // if !Insecure, then StartTLS or LDAPs
SkipVerify bool
SubtreeSearch bool
Address string
BindDN string
UserGroupAttribute string
BindPassword string
BaseDN string
UserAttribute string
CACert string
}
type LogConfig struct {
@@ -102,11 +101,19 @@ type GlobalStorageConfig struct {
}
type AccessControlConfig struct {
Repositories Repositories
Repositories Repositories `json:"repositories" mapstructure:"repositories"`
AdminPolicy Policy
Groups Groups
}
type Repositories map[string]PolicyGroup
type (
Repositories map[string]PolicyGroup
Groups map[string]Group
)
type Group struct {
Users []string
}
type PolicyGroup struct {
Policies []Policy
@@ -117,6 +124,7 @@ type PolicyGroup struct {
type Policy struct {
Users []string
Actions []string
Groups []string
}
type Config struct {
@@ -125,7 +133,6 @@ type Config struct {
Commit string
ReleaseTag string
BinaryType string
AccessControl *AccessControlConfig
Storage GlobalStorageConfig
HTTP HTTPConfig
Log *LogConfig
@@ -187,42 +194,3 @@ func (c *Config) Sanitize() *Config {
return sanitizedConfig
}
// LoadAccessControlConfig populates config.AccessControl struct with values from config.
func (c *Config) LoadAccessControlConfig(viperInstance *viper.Viper) error {
if c.HTTP.RawAccessControl == nil {
return nil
}
c.AccessControl = &AccessControlConfig{}
c.AccessControl.Repositories = make(map[string]PolicyGroup)
for policy := range c.HTTP.RawAccessControl {
var policies []Policy
var policyGroup PolicyGroup
if policy == "adminpolicy" {
adminPolicy := viperInstance.GetStringMapStringSlice("http::accessControl::adminPolicy")
c.AccessControl.AdminPolicy.Actions = adminPolicy["actions"]
c.AccessControl.AdminPolicy.Users = adminPolicy["users"]
continue
}
err := viperInstance.UnmarshalKey(fmt.Sprintf("http::accessControl::%s::policies", policy), &policies)
if err != nil {
return err
}
defaultPolicy := viperInstance.GetStringSlice(fmt.Sprintf("http::accessControl::%s::defaultPolicy", policy))
policyGroup.DefaultPolicy = defaultPolicy
anonymousPolicy := viperInstance.GetStringSlice(fmt.Sprintf("http::accessControl::%s::anonymousPolicy", policy))
policyGroup.Policies = policies
policyGroup.AnonymousPolicy = anonymousPolicy
c.AccessControl.Repositories[policy] = policyGroup
}
return nil
}