/
authorizer.go
151 lines (132 loc) · 5.02 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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package identchecker
import (
"context"
"gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
)
// Authorizer is used to check whether a given user is allowed
// to perform a set of operations.
type Authorizer interface {
// Authorize checks whether the given identity (which will be nil
// when there is no authenticated user) is allowed to perform
// the given operations. It should return an error only when
// the authorization cannot be determined, not when the
// user has been denied access.
//
// On success, each element of allowed holds whether the respective
// element of ops has been allowed, and caveats holds any additional
// third party caveats that apply.
// If allowed is shorter then ops, the additional elements are assumed to
// be false.
Authorize(ctx context.Context, id Identity, ops []bakery.Op) (allowed []bool, caveats []checkers.Caveat, err error)
}
var (
// OpenAuthorizer is an Authorizer implementation that will authorize all operations without question.
OpenAuthorizer openAuthorizer
// ClosedAuthorizer is an Authorizer implementation that will return ErrPermissionDenied
// on all authorization requests.
ClosedAuthorizer closedAuthorizer
)
var (
_ Authorizer = OpenAuthorizer
_ Authorizer = ClosedAuthorizer
_ Authorizer = AuthorizerFunc(nil)
_ Authorizer = ACLAuthorizer{}
)
type openAuthorizer struct{}
// Authorize implements Authorizer.Authorize.
func (openAuthorizer) Authorize(ctx context.Context, id Identity, ops []bakery.Op) (allowed []bool, caveats []checkers.Caveat, err error) {
allowed = make([]bool, len(ops))
for i := range allowed {
allowed[i] = true
}
return allowed, nil, nil
}
type closedAuthorizer struct{}
// Authorize implements Authorizer.Authorize.
func (closedAuthorizer) Authorize(ctx context.Context, id Identity, ops []bakery.Op) (allowed []bool, caveats []checkers.Caveat, err error) {
return make([]bool, len(ops)), nil, nil
}
// AuthorizerFunc implements a simplified version of Authorizer
// that operates on a single operation at a time.
type AuthorizerFunc func(ctx context.Context, id Identity, op bakery.Op) (bool, []checkers.Caveat, error)
// Authorize implements Authorizer.Authorize by calling f
// with the given identity for each operation.
func (f AuthorizerFunc) Authorize(ctx context.Context, id Identity, ops []bakery.Op) (allowed []bool, caveats []checkers.Caveat, err error) {
allowed = make([]bool, len(ops))
for i, op := range ops {
ok, fcaveats, err := f(ctx, id, op)
if err != nil {
return nil, nil, errgo.Mask(err)
}
allowed[i] = ok
// TODO merge identical caveats?
caveats = append(caveats, fcaveats...)
}
return allowed, caveats, nil
}
// Everyone is recognized by ACLAuthorizer as the name of a
// group that has everyone in it.
const Everyone = "everyone"
// ACLIdentity may be implemented by Identity implementions
// to report group membership information.
// See ACLAuthorizer for details.
type ACLIdentity interface {
Identity
// Allow reports whether the user should be allowed to access
// any of the users or groups in the given ACL slice.
Allow(ctx context.Context, acl []string) (bool, error)
}
// ACLAuthorizer is an Authorizer implementation that will check access
// control list (ACL) membership of users. It uses GetACL to find out
// the ACLs that apply to the requested operations and will authorize an
// operation if an ACL contains the group "everyone" or if the context
// contains an AuthInfo (see ContextWithAuthInfo) that holds an Identity
// that implements ACLIdentity and its Allow method returns true for the
// ACL.
type ACLAuthorizer struct {
// GetACL returns the ACL that applies to the given operation,
// and reports whether non-authenticated users should
// be allowed access when the ACL contains "everyone".
//
// If an entity cannot be found or the action is not recognised,
// GetACLs should return an empty ACL but no error.
GetACL func(ctx context.Context, op bakery.Op) (acl []string, allowPublic bool, err error)
}
// Authorize implements Authorizer.Authorize by calling ident.Allow to determine
// whether the identity is a member of the ACLs associated with the given
// operations.
func (a ACLAuthorizer) Authorize(ctx context.Context, ident Identity, ops []bakery.Op) (allowed []bool, caveats []checkers.Caveat, err error) {
if len(ops) == 0 {
// Anyone is allowed to do nothing.
return nil, nil, nil
}
ident1, _ := ident.(ACLIdentity)
allowed = make([]bool, len(ops))
for i, op := range ops {
acl, allowPublic, err := a.GetACL(ctx, op)
if err != nil {
return nil, nil, errgo.Mask(err)
}
if ident1 != nil {
allowed[i], err = ident1.Allow(ctx, acl)
if err != nil {
return nil, nil, errgo.Notef(err, "cannot check permissions")
}
} else {
// TODO should we allow "everyone" when the identity is
// non-nil but isn't an ACLIdentity?
allowed[i] = allowPublic && isPublicACL(acl)
}
}
return allowed, nil, nil
}
func isPublicACL(acl []string) bool {
for _, g := range acl {
if g == Everyone {
return true
}
}
return false
}