consul/acl/errors.go
Thomas Eckert 13da1a5285
Native API Gateway Config Entries (#15897)
* Stub Config Entries for Consul Native API Gateway (#15644)
* Add empty InlineCertificate struct and protobuf
* apigateway stubs
* Stub HTTPRoute in api pkg
* Stub HTTPRoute in structs pkg
* Simplify api.APIGatewayConfigEntry to be consistent w/ other entries
* Update makeConfigEntry switch, add docstring for HTTPRouteConfigEntry
* Add TCPRoute to MakeConfigEntry, return unique Kind
* Stub BoundAPIGatewayConfigEntry in agent
* Add RaftIndex to APIGatewayConfigEntry stub
* Add new config entry kinds to validation allow-list
* Add RaftIndex to other added config entry stubs
* Update usage metrics assertions to include new cfg entries
* Add Meta and acl.EnterpriseMeta to all new ConfigEntry types
* Remove unnecessary Services field from added config entry types
* Implement GetMeta(), GetEnterpriseMeta() for added config entry types
* Add meta field to proto, name consistently w/ existing config entries
* Format config_entry.proto
* Add initial implementation of CanRead + CanWrite for new config entry types
* Add unit tests for decoding of new config entry types
* Add unit tests for parsing of new config entry types
* Add unit tests for API Gateway config entry ACLs
* Return typed PermissionDeniedError on BoundAPIGateway CanWrite
* Add unit tests for added config entry ACLs
* Add BoundAPIGateway type to AllConfigEntryKinds
* Return proper kind from BoundAPIGateway
* Add docstrings for new config entry types
* Add missing config entry kinds to proto def
* Update usagemetrics_oss_test.go
* Use utility func for returning PermissionDeniedError
* EventPublisher subscriptions for Consul Native API Gateway (#15757)
* Create new event topics in subscribe proto
* Add tests for PBSubscribe func
* Make configs singular, add all configs to PBToStreamSubscribeRequest
* Add snapshot methods
* Add config_entry_events tests
* Add config entry kind to topic for new configs
* Add unit tests for snapshot methods
* Start adding integration test
* Test using the new controller code
* Update agent/consul/state/config_entry_events.go
* Check value of error
* Add controller stubs for API Gateway (#15837)
* update initial stub implementation
* move files, clean up mutex references
* Remove embed, use idiomatic names for constructors
* Remove stray file introduced in merge
* Add APIGateway validation (#15847)
* Add APIGateway validation
* Add additional validations
* Add cert ref validation
* Add protobuf definitions
* Fix up field types
* Add API structs
* Move struct fields around a bit
* APIGateway InlineCertificate validation (#15856)
* Add APIGateway validation
* Add additional validations
* Add protobuf definitions
* Tabs to spaces
* Add API structs
* Move struct fields around a bit
* Add validation for InlineCertificate
* Fix ACL test
* APIGateway BoundAPIGateway validation (#15858)
* Add APIGateway validation
* Add additional validations
* Add cert ref validation
* Add protobuf definitions
* Fix up field types
* Add API structs
* Move struct fields around a bit
* Add validation for BoundAPIGateway
* APIGateway TCPRoute validation (#15855)
* Add APIGateway validation
* Add additional validations
* Add cert ref validation
* Add protobuf definitions
* Fix up field types
* Add API structs
* Add TCPRoute normalization and validation
* Add forgotten Status
* Add some more field docs in api package
* Fix test
* Format imports
* Rename snapshot test variable names
* Add plumbing for Native API GW Subscriptions (#16003)

Co-authored-by: Sarah Alsmiller <sarah.alsmiller@hashicorp.com>
Co-authored-by: Nathan Coleman <nathan.coleman@hashicorp.com>
Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com>
Co-authored-by: Andrew Stucki <andrew.stucki@hashicorp.com>
2023-01-18 22:14:34 +00:00

145 lines
4.6 KiB
Go

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 == "" {
message.WriteString(": provided token")
} else 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
}