-
Notifications
You must be signed in to change notification settings - Fork 0
/
policy.go
209 lines (187 loc) Β· 5.63 KB
/
policy.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2019 - MinIO, Inc. All rights reserved.
// Use of this source code is governed by the AGPLv3
// license that can be found in the LICENSE file.
package kes
import (
"encoding/json"
"errors"
"io"
"time"
)
// Policy contains a set of rules that explicitly allow
// or deny HTTP requests.
//
// These rules are specified as glob patterns. The rule
// applies if the pattern matches the request URL path.
// For more details on the glob syntax in general see [1]
// and for the specific pattern syntax see [2].
//
// A policy contains two different rule sets:
// - Allow rules
// - Deny rules
//
// A policy determines whether a request should be allowed
// or denied in two steps. First, it iterates over all deny
// rules. If any deny rules matches the given request then
// the request is rejected. Then it iterates over all
// allow rules. If any allow rule matches the given request
// then the request is accepted. Otherwise, the request
// is rejected by default.
// Hence, a request is only accepted if at least one allow
// rules and no deny rule matches the request. Also, a deny
// rule takes precedence over an allow rule.
//
// [1]: https://en.wikipedia.org/wiki/Glob_(programming)
// [2]: https://golang.org/pkg/path/#Match
type Policy struct {
Allow []string // Set of allow patterns
Deny []string // Set of deny patterns
Info PolicyInfo // Info contains metadata for the Policy.
}
// PolicyInfo describes a KES policy.
type PolicyInfo struct {
Name string `json:"name"` // Name of the policy
CreatedAt time.Time `json:"created_at,omitempty"` // Point in time when the policy was created
CreatedBy Identity `json:"created_by,omitempty"` // Identity that created the policy
}
// PolicyIterator iterates over a stream of PolicyInfo objects.
// Close the PolicyIterator to release associated resources.
type PolicyIterator struct {
decoder *json.Decoder
closer io.Closer
current PolicyInfo
err error
closed bool
}
// Value returns the current PolicyInfo. It remains valid
// until Next is called again.
func (i *PolicyIterator) Value() PolicyInfo { return i.current }
// Name returns the name of the current policy.
// It is a short-hand for Value().Name.
func (i *PolicyIterator) Name() string { return i.current.Name }
// CreatedAt returns the created at timestamp of the current
// policy. It is a short-hand for Value().CreatedAt.
func (i *PolicyIterator) CreatedAt() time.Time { return i.current.CreatedAt }
// CreatedBy returns the identiy that created the current policy.
// It is a short-hand for Value().CreatedBy.
func (i *PolicyIterator) CreatedBy() Identity { return i.current.CreatedBy }
// Next returns true if there is another PolicyInfo.
// It returns false if there are no more PolicyInfo
// objects or when the PolicyIterator encounters an
// error.
func (i *PolicyIterator) Next() bool {
type Response struct {
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
CreatedBy Identity `json:"created_by"`
Err string `json:"error"`
}
if i.closed || i.err != nil {
return false
}
var resp Response
if err := i.decoder.Decode(&resp); err != nil {
if errors.Is(err, io.EOF) {
i.err = i.Close()
} else {
i.err = err
}
return false
}
if resp.Err != "" {
i.err = errors.New(resp.Err)
return false
}
i.current = PolicyInfo{
Name: resp.Name,
CreatedAt: resp.CreatedAt,
CreatedBy: resp.CreatedBy,
}
return true
}
// WriteTo encodes and writes all remaining PolicyInfos
// from its current iterator position to w. It returns
// the number of bytes written to w and the first error
// encounterred, if any.
func (i *PolicyIterator) WriteTo(w io.Writer) (int64, error) {
type Response struct {
Name string `json:"name"`
CreatedAt time.Time `json:"created_at,omitempty"`
CreatedBy Identity `json:"created_by,omitempty"`
Err string `json:"error,omitempty"`
}
if i.err != nil {
return 0, i.err
}
if i.closed {
return 0, errors.New("kes: WriteTo called after Close")
}
cw := countWriter{W: w}
encoder := json.NewEncoder(&cw)
for {
var resp Response
if err := i.decoder.Decode(&resp); err != nil {
if errors.Is(err, io.EOF) {
i.err = i.Close()
} else {
i.err = err
}
return cw.N, i.err
}
if resp.Err != "" {
i.err = errors.New(resp.Err)
return cw.N, i.err
}
if err := encoder.Encode(resp); err != nil {
i.err = err
return cw.N, err
}
}
}
// Values returns up to the next n PolicyInfo values. Subsequent
// calls will yield further PolicyInfos if there are any.
//
// If n > 0, Values returns at most n PolicyInfo structs. In this case,
// if Values returns an empty slice, it will return an error explaining
// why. At the end of the listing, the error is io.EOF.
//
// If n <= 0, Values returns all remaining PolicyInfo records. In this
// case, Values always closes the PolicyIterator. When it succeeds, it
// returns a nil error, not io.EOF.
func (i *PolicyIterator) Values(n int) ([]PolicyInfo, error) {
values := []PolicyInfo{}
if n > 0 && i.closed {
return values, io.EOF // Return early, don't alloc a slice
}
if n > 0 {
values = make([]PolicyInfo, 0, n)
}
var count int
for i.Next() {
values = append(values, i.Value())
count++
if n > 0 && count >= n {
return values, nil
}
}
if err := i.Close(); err != nil {
return values, err
}
if n > 0 && len(values) == 0 { // As by doc contract
return values, io.EOF
}
return values, nil
}
// Close closes the PolicyIterator and releases
// any associated resources.
func (i *PolicyIterator) Close() error {
if !i.closed {
err := i.closer.Close()
if i.err == nil {
i.err = err
}
i.closed = true
return err
}
return i.err
}