package auth import ( "context" "strings" "github.com/kopia/kopia/internal/acl" "github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/snapshot" "github.com/kopia/kopia/snapshot/policy" ) // Authorizer gets authorization info for logged in user. type Authorizer interface { Authorize(ctx context.Context, rep repo.Repository, username string) AuthorizationInfo Refresh(ctx context.Context) error } // AccessLevel specifies access level when accessing repository objects. type AccessLevel = acl.AccessLevel // Access levels forwarded to 'acl' package to allow it to easily implement AuthorizationInfo interface. const ( AccessLevelNone = acl.AccessLevelNone AccessLevelRead = acl.AccessLevelRead // RO access AccessLevelAppend = acl.AccessLevelAppend // RO + create new AccessLevelFull = acl.AccessLevelFull // read/write/delete ) // AuthorizationInfo determines logged in user's access level. type AuthorizationInfo interface { // ContentAccessLevel determines whether the user can read/write contents. ContentAccessLevel() AccessLevel // ManifestAccessLevel determines whether the user has access to a manifest with given labels. ManifestAccessLevel(labels map[string]string) AccessLevel } type noAccessAuthorizationInfo struct{} func (noAccessAuthorizationInfo) ContentAccessLevel() AccessLevel { return AccessLevelNone } func (noAccessAuthorizationInfo) ManifestAccessLevel(labels map[string]string) AccessLevel { _ = labels return AccessLevelNone } // NoAccess returns AuthorizationInfo which grants no permissions. func NoAccess() AuthorizationInfo { return noAccessAuthorizationInfo{} } type legacyAuthorizationInfo struct { usernameAtHostname string } func (la legacyAuthorizationInfo) ContentAccessLevel() AccessLevel { return AccessLevelFull } func (la legacyAuthorizationInfo) ManifestAccessLevel(labels map[string]string) AccessLevel { if labels[manifest.TypeLabelKey] == policy.ManifestType { // everybody can read global policy. switch labels[policy.PolicyTypeLabel] { case policy.PolicyTypeGlobal: return AccessLevelRead case policy.PolicyTypeHost: if strings.HasSuffix(la.usernameAtHostname, "@"+labels[snapshot.HostnameLabel]) { return AccessLevelRead } } } // full access to policies/snapshots for the username@hostname if labels[snapshot.UsernameLabel]+"@"+labels[snapshot.HostnameLabel] == la.usernameAtHostname { return AccessLevelFull } // no access otherwise return AccessLevelNone } type legacyAuthorizer struct{} func (legacyAuthorizer) Authorize(_ context.Context, _ repo.Repository, username string) AuthorizationInfo { return legacyAuthorizationInfo{usernameAtHostname: username} } func (legacyAuthorizer) Refresh(_ context.Context) error { return nil } // LegacyAuthorizer is an Authorizer that returns authorizer with legacy (pre-ACL) // authorization rules (authenticated users can see their own snapshots/policies only). func LegacyAuthorizer() Authorizer { return legacyAuthorizer{} }