mirror of
https://github.com/kopia/kopia.git
synced 2026-01-26 23:38:04 -05:00
* nit: replaced harcoded string constants with named constants * acl: added management of ACL entries * auth: implemented DefaultAuthorizer which uses ACLs if any entries are found in the system and falls back to LegacyAuthorizer if not * cli: switch to DefaultAuthorizer when starting server * cli: added ACL management * server: refactored authenticator + added refresh Authenticator is now an interface which also supports Refresh. * authz: refactored authorizer to be an interface + added Refresh() * server: refresh authentication and authorizer * e2e tests for ACLs * server: handling of SIGHUP to refresh authn/authz caches * server: reorganized flags to specify auth options: - removed '--allow-repository-users' - it's always on - one of --without-password, --server-password or --random-password can be specified to specify password for the UI user - htpasswd-file - can be specified to provide password for UI or remote users * cli: moved 'kopia user' to 'kopia server user' * server: allow all UI actions if no authenticator is set * acl: removed priority until we have a better understood use case for it * acl: added validation of allowed labels when adding ACL entries * site: added docs for ACLs
128 lines
2.7 KiB
Go
128 lines
2.7 KiB
Go
// Package acl provides management of ACLs that define permissions granted to repository users.
|
|
package acl
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/manifest"
|
|
)
|
|
|
|
const (
|
|
aclManifestType = "acl"
|
|
)
|
|
|
|
func matchOrWildcard(rule, actual string) bool {
|
|
if rule == "*" {
|
|
return true
|
|
}
|
|
|
|
return rule == actual
|
|
}
|
|
|
|
func userMatches(rule, username, hostname string) bool {
|
|
ruleParts := strings.Split(rule, "@")
|
|
if len(ruleParts) != 2 { // nolint:gomnd
|
|
return false
|
|
}
|
|
|
|
return matchOrWildcard(ruleParts[0], username) && matchOrWildcard(ruleParts[1], hostname)
|
|
}
|
|
|
|
// EntriesForUser computes the list of ACL entries matching the given user.
|
|
func EntriesForUser(entries []*Entry, username, hostname string) []*Entry {
|
|
result := []*Entry{}
|
|
|
|
for _, e := range entries {
|
|
if userMatches(e.User, username, hostname) {
|
|
result = append(result, e)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// EffectivePermissions computes the effective access level for a given user@hostname to subject
|
|
// for a given set of ACL Entries.
|
|
func EffectivePermissions(username, hostname string, target map[string]string, entries []*Entry) AccessLevel {
|
|
highest := AccessLevelNone
|
|
|
|
for _, e := range entries {
|
|
if !userMatches(e.User, username, hostname) {
|
|
continue
|
|
}
|
|
|
|
if !e.Target.matches(target, username, hostname) {
|
|
continue
|
|
}
|
|
|
|
if e.Access > highest {
|
|
highest = e.Access
|
|
}
|
|
}
|
|
|
|
return highest
|
|
}
|
|
|
|
// LoadEntries returns the set of all ACLs in the repository, using old list as a cache.
|
|
func LoadEntries(ctx context.Context, rep repo.Repository, old []*Entry) ([]*Entry, error) {
|
|
if rep == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
entries, err := rep.FindManifests(ctx, map[string]string{
|
|
manifest.TypeLabelKey: aclManifestType,
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error listing ACL manifests")
|
|
}
|
|
|
|
om := map[manifest.ID]*Entry{}
|
|
for _, v := range old {
|
|
om[v.ManifestID] = v
|
|
}
|
|
|
|
result := []*Entry{}
|
|
|
|
for _, m := range entries {
|
|
if o := om[m.ID]; o != nil {
|
|
result = append(result, o)
|
|
continue
|
|
}
|
|
|
|
var p Entry
|
|
|
|
_, err := rep.GetManifest(ctx, m.ID, &p)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error loading ACL manifest %v", m.ID)
|
|
}
|
|
|
|
p.ManifestID = m.ID
|
|
|
|
result = append(result, &p)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// AddACL validates and adds the specified ACL entry to the repository.
|
|
func AddACL(ctx context.Context, w repo.RepositoryWriter, e *Entry) error {
|
|
if err := e.Validate(); err != nil {
|
|
return errors.Wrap(err, "error validating ACL")
|
|
}
|
|
|
|
manifestID, err := w.PutManifest(ctx, map[string]string{
|
|
manifest.TypeLabelKey: aclManifestType,
|
|
}, e)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error writing manifest")
|
|
}
|
|
|
|
e.ManifestID = manifestID
|
|
|
|
return nil
|
|
}
|