mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 21:35:52 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
153 lines
4.9 KiB
Go
153 lines
4.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package acl
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// These error constants define the standard ACL error types. The values
|
|
// must not be changed since the error values are sent via RPC calls
|
|
// from older clients and may not have the correct type.
|
|
const (
|
|
errNotFound = "ACL not found"
|
|
errRootDenied = "Cannot resolve root ACL"
|
|
errDisabled = "ACL support disabled"
|
|
errPermissionDenied = "Permission denied"
|
|
errInvalidParent = "Invalid Parent"
|
|
)
|
|
|
|
var (
|
|
// ErrNotFound indicates there is no matching ACL.
|
|
ErrNotFound = errors.New(errNotFound)
|
|
|
|
// ErrRootDenied is returned when attempting to resolve a root ACL.
|
|
ErrRootDenied = errors.New(errRootDenied)
|
|
|
|
// ErrDisabled is returned when ACL changes are not permitted since
|
|
// they are disabled.
|
|
ErrDisabled = errors.New(errDisabled)
|
|
|
|
// ErrPermissionDenied is returned when an ACL based rejection
|
|
// happens.
|
|
ErrPermissionDenied = PermissionDeniedError{}
|
|
|
|
// ErrInvalidParent is returned when a remotely resolve ACL
|
|
// token claims to have a non-root parent
|
|
ErrInvalidParent = errors.New(errInvalidParent)
|
|
)
|
|
|
|
// IsErrNotFound checks if the given error message is comparable to
|
|
// ErrNotFound.
|
|
func IsErrNotFound(err error) bool {
|
|
return err != nil && strings.Contains(err.Error(), errNotFound)
|
|
}
|
|
|
|
// IsErrRootDenied checks if the given error message is comparable to
|
|
// ErrRootDenied.
|
|
func IsErrRootDenied(err error) bool {
|
|
return err != nil && strings.Contains(err.Error(), errRootDenied)
|
|
}
|
|
|
|
// IsErrDisabled checks if the given error message is comparable to
|
|
// ErrDisabled.
|
|
func IsErrDisabled(err error) bool {
|
|
return err != nil && strings.Contains(err.Error(), errDisabled)
|
|
}
|
|
|
|
// IsErrPermissionDenied checks if the given error message is comparable
|
|
// to ErrPermissionDenied.
|
|
func IsErrPermissionDenied(err error) bool {
|
|
return err != nil && strings.Contains(err.Error(), errPermissionDenied)
|
|
}
|
|
|
|
// Arguably this should be some sort of union type.
|
|
// The usage of Cause and the rest of the fields is entirely disjoint.
|
|
type PermissionDeniedError struct {
|
|
Cause string
|
|
|
|
// Accessor contains information on the accessor used e.g. "token <GUID>"
|
|
Accessor string
|
|
// Resource (e.g. Service)
|
|
Resource Resource
|
|
// Access level (e.g. Read)
|
|
AccessLevel AccessLevel
|
|
// e.g. "sidecar-proxy-1"
|
|
ResourceID ResourceDescriptor
|
|
}
|
|
|
|
// Initially we may not have attribution information; that will become more complete as we work this change through
|
|
// There are generally three classes of errors
|
|
// 1) Named entities without a context
|
|
// 2) Unnamed entities with a context
|
|
// 3) Completely context free checks (global permissions)
|
|
// 4) Errors that only have a cause (for example bad token)
|
|
func (e PermissionDeniedError) Error() string {
|
|
var message strings.Builder
|
|
message.WriteString(errPermissionDenied)
|
|
|
|
// Type 4)
|
|
if e.Cause != "" {
|
|
fmt.Fprintf(&message, ": %s", e.Cause)
|
|
return message.String()
|
|
}
|
|
// Should only be empty when default struct is used.
|
|
if e.Resource == "" {
|
|
return message.String()
|
|
}
|
|
|
|
if e.Accessor == AnonymousTokenID {
|
|
message.WriteString(": anonymous token")
|
|
} else {
|
|
fmt.Fprintf(&message, ": token with AccessorID '%s'", e.Accessor)
|
|
}
|
|
|
|
fmt.Fprintf(&message, " lacks permission '%s:%s'", e.Resource, e.AccessLevel.String())
|
|
|
|
if e.ResourceID.Name != "" {
|
|
fmt.Fprintf(&message, " on %s", e.ResourceID.ToString())
|
|
}
|
|
|
|
if e.Accessor == AnonymousTokenID {
|
|
message.WriteString(". The anonymous token is used implicitly when a request does not specify a token.")
|
|
}
|
|
return message.String()
|
|
}
|
|
|
|
func PermissionDenied(msg string, args ...interface{}) PermissionDeniedError {
|
|
cause := fmt.Sprintf(msg, args...)
|
|
return PermissionDeniedError{Cause: cause}
|
|
}
|
|
|
|
// TODO Extract information from Authorizer
|
|
func PermissionDeniedByACL(authz Authorizer, context *AuthorizerContext, resource Resource, accessLevel AccessLevel, resourceID string) PermissionDeniedError {
|
|
desc := NewResourceDescriptor(resourceID, context)
|
|
return PermissionDeniedError{Accessor: extractAccessorID(authz), Resource: resource, AccessLevel: accessLevel, ResourceID: desc}
|
|
}
|
|
|
|
func PermissionDeniedByACLUnnamed(authz Authorizer, context *AuthorizerContext, resource Resource, accessLevel AccessLevel) PermissionDeniedError {
|
|
desc := NewResourceDescriptor("", context)
|
|
return PermissionDeniedError{Accessor: extractAccessorID(authz), Resource: resource, AccessLevel: accessLevel, ResourceID: desc}
|
|
}
|
|
|
|
func extractAccessorID(authz interface{}) string {
|
|
var accessor string
|
|
switch v := authz.(type) {
|
|
case AllowAuthorizer:
|
|
accessor = v.AccessorID
|
|
case string:
|
|
accessor = v
|
|
}
|
|
return accessor
|
|
}
|
|
|
|
func ACLResourceNotExistError(resourceType string, entMeta EnterpriseMeta) error {
|
|
if ns := entMeta.NamespaceOrEmpty(); ns != "" {
|
|
return fmt.Errorf("Requested %s not found in namespace %s: %w", resourceType, ns, ErrNotFound)
|
|
}
|
|
return fmt.Errorf("Requested %s does not exist: %w", resourceType, ErrNotFound)
|
|
}
|