Skip to content

Commit

Permalink
acl: demonstrate new authz interface
Browse files Browse the repository at this point in the history
  • Loading branch information
dnephin committed Jan 26, 2022
1 parent 8ace474 commit 0eaa784
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 4 deletions.
10 changes: 10 additions & 0 deletions acl/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,23 @@ func IsErrPermissionDenied(err error) bool {
}

type PermissionDeniedError struct {
Resource Resource
AccessLevel AccessLevel
AccessorID string
ResourceID string

// TODO: remove Cause, use other fields
Cause string
}

func (e PermissionDeniedError) Error() string {
if e.Cause != "" {
return errPermissionDenied + ": " + e.Cause
}
if e.Resource != "" {
return fmt.Sprintf("%s: token %s is missing %s:%s for %s",
errPermissionDenied, e.AccessorID, e.Resource, e.AccessLevel, e.ResourceID)
}
return errPermissionDenied
}

Expand Down
22 changes: 22 additions & 0 deletions agent/consul/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@ func (r *ACLResolver) ResolveToken(token string) (ACLResolveResult, error) {
return ACLResolveResult{Authorizer: acl.NewChainedAuthorizer(chain), ACLIdentity: identity}, nil
}

// TODO: move to a new package under acl/
type ACLResolveResult struct {
acl.Authorizer
// TODO: likely we can reduce this interface
Expand All @@ -1132,6 +1133,27 @@ func (a ACLResolveResult) AccessorID() string {
return a.ACLIdentity.ID()
}

type ACLKVResourceID interface {
KVResourceID() string
FillAuthzContext(*acl.AuthorizerContext)
}

func (a ACLResolveResult) HasKeyWrite(id ACLKVResourceID) error {
key := id.KVResourceID()
var authzCtx acl.AuthorizerContext
id.FillAuthzContext(&authzCtx)

if a.Authorizer.KeyWrite(key, &authzCtx) == acl.Allow {
return nil
}
return acl.PermissionDeniedError{
Resource: acl.ResourceKey,
ResourceID: key,
AccessorID: a.AccessorID(),
AccessLevel: acl.AccessWrite,
}
}

func (r *ACLResolver) ACLsEnabled() bool {
// Whether we desire ACLs to be enabled according to configuration
if !r.config.ACLsEnabled {
Expand Down
6 changes: 3 additions & 3 deletions agent/consul/kvs_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type KVS struct {
// preApply does all the verification of a KVS update that is performed BEFORE
// we submit as a Raft log entry. This includes enforcing the lock delay which
// must only be done on the leader.
func kvsPreApply(logger hclog.Logger, srv *Server, authz acl.Authorizer, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) {
func kvsPreApply(logger hclog.Logger, srv *Server, authz ACLResolveResult, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) {
// Verify the entry.
if dirEnt.Key == "" && op != api.KVDeleteTree {
return false, fmt.Errorf("Must provide key")
Expand Down Expand Up @@ -66,8 +66,8 @@ func kvsPreApply(logger hclog.Logger, srv *Server, authz acl.Authorizer, op api.
var authzContext acl.AuthorizerContext
dirEnt.FillAuthzContext(&authzContext)

if authz.KeyWrite(dirEnt.Key, &authzContext) != acl.Allow {
return false, acl.ErrPermissionDenied
if err := authz.HasKeyWrite(dirEnt); err != nil {
return false, err
}
}

Expand Down
2 changes: 1 addition & 1 deletion agent/consul/txn_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Txn struct {

// preCheck is used to verify the incoming operations before any further
// processing takes place. This checks things like ACLs.
func (t *Txn) preCheck(authorizer acl.Authorizer, ops structs.TxnOps) structs.TxnErrors {
func (t *Txn) preCheck(authorizer ACLResolveResult, ops structs.TxnOps) structs.TxnErrors {
var errors structs.TxnErrors

// Perform the pre-apply checks for any KV operations.
Expand Down
4 changes: 4 additions & 0 deletions agent/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,10 @@ func (d *DirEntry) IDValue() string {
return d.Key
}

func (d *DirEntry) KVResourceID() string {
return d.Key
}

type DirEntries []*DirEntry

// KVSRequest is used to operate on the Key-Value store
Expand Down

0 comments on commit 0eaa784

Please sign in to comment.