mirror of
https://github.com/status-im/consul.git
synced 2025-02-10 20:56:46 +00:00
* 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>
145 lines
4.6 KiB
Go
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
|
|
}
|