/
authorizer.go
92 lines (76 loc) · 2.68 KB
/
authorizer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// SPDX-FileCopyrightText: 2020 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0
package bascule
import (
"context"
"go.uber.org/multierr"
)
// Authorizer is a strategy for determining if a given token represents
// adequate permissions to access a resource.
type Authorizer[R any] interface {
// Authorize tests if a given token holds the correct permissions to
// access a given resource. If this method needs to access external
// systems, it should pass the supplied context to honor context
// cancelation semantics.
//
// If this method doesn't support the given token, it should return nil.
Authorize(ctx context.Context, token Token, resource R) error
}
// AuthorizerFunc is a closure type that implements Authorizer.
type AuthorizerFunc[R any] func(context.Context, Token, R) error
func (af AuthorizerFunc[R]) Authorize(ctx context.Context, token Token, resource R) error {
return af(ctx, token, resource)
}
// Authorizers is a collection of Authorizers.
type Authorizers[R any] []Authorizer[R]
// Add appends authorizers to this aggregate Authorizers.
func (as *Authorizers[R]) Add(a ...Authorizer[R]) {
if *as == nil {
*as = make(Authorizers[R], 0, len(a))
}
*as = append(*as, a...)
}
// Authorize requires all authorizers in this sequence to allow access. This
// method supplies a logical AND.
//
// Because authorization can be arbitrarily expensive, execution halts at the first failed
// authorization attempt.
func (as Authorizers[R]) Authorize(ctx context.Context, token Token, resource R) error {
for _, a := range as {
if err := a.Authorize(ctx, token, resource); err != nil {
return err
}
}
return nil
}
type requireAny[R any] struct {
a Authorizers[R]
}
// Authorize returns nil at the first authorizer that returns nil, i.e. accepts the access.
// Otherwise, this method returns an aggregate error of all the authorization errors.
func (ra requireAny[R]) Authorize(ctx context.Context, token Token, resource R) error {
var err error
for _, a := range ra.a {
authErr := a.Authorize(ctx, token, resource)
if authErr == nil {
return nil
}
err = multierr.Append(err, authErr)
}
return err
}
// Any returns an Authorizer which is a logical OR: each authorizer is executed in
// order, and any authorizer that allows access results in an immediate return. The
// returned Authorizer's state is distinct and is unaffected by subsequent changes
// to the Authorizers set.
//
// Any error returns from the returned Authorizer will be an aggregate of all the errors
// returned from each element.
func (as Authorizers[R]) Any() Authorizer[R] {
return requireAny[R]{
a: append(
make(Authorizers[R], 0, len(as)),
as...,
),
}
}