forked from minio/kms-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
policy.go
145 lines (132 loc) · 4.13 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
// Copyright 2023 - 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 (
"net/http"
"strings"
"time"
)
// A Rule controls HTTP requests and is part of a policy.
type Rule struct{}
// A Policy represents a set of rules that determine whether an HTTP request
// is accepted or rejected. It consists of two sets of rules:
// allow rules and deny rules.
//
// If any rule from the deny set matches an incoming HTTP request, the request
// is rejected. Conversely, if any rule from the allow set matches, the request
// is accepted. If no rule matches, the request is also rejected.
// Therefore, an empty Policy, without any rules, rejects any request.
//
// A rule set is defined by a collection of API path patterns.
// An API path pattern consists of the KES server API path and an
// optional resource pattern. For example, "/v1/key/describe/my-key*"
// consists of the "/v1/key/describe" API path and the resource
// pattern "my-key*".
//
// When matching API path patterns:
// - If the resource pattern does not end with an asterisk ('*') character,
// the API path pattern only matches requests with an URL path equal to the pattern.
// - If the resource pattern ends with an asterisk ('*') character,
// the API path pattern matches if the API path pattern (without the asterisk) is a prefix of the URL path.
//
// An API path pattern cannot contain more than one asterisk character.
// API path patterns can be viewed as a subset of glob patterns.
//
// Here's an example defining a policy:
//
// policy := Policy{
// Allow: map[string]kes.Rule{
// "/v1/status": {},
// "/v1/key/describe/my-key*": {},
// "/v1/key/generate/my-key*": {},
// "/v1/key/decrypt/my-key*": {},
// },
// }
type Policy struct {
Allow map[string]Rule // Set of allow rules
Deny map[string]Rule // Set of deny rules
CreatedAt time.Time
CreatedBy Identity
}
// Verify reports whether the given HTTP request is allowed.
// It returns no error if:
//
// (1) No deny pattern matches the URL path *AND*
// (2) At least one allow pattern matches the URL path.
//
// Otherwise, Verify returns ErrNotAllowed.
func (p *Policy) Verify(r *http.Request) error {
for pattern := range p.Deny {
if match(r.URL.Path, pattern) {
return ErrNotAllowed
}
}
for pattern := range p.Allow {
if match(r.URL.Path, pattern) {
return nil
}
}
return ErrNotAllowed
}
// IsSubset reports whether the Policy p is a subset of o.
// If it is then any request allowed by p is also allowed
// by o and any request rejected by o is also rejected by p.
//
// Usually, p is a subset of o when it contains less or
// less generic allow rules and/or more or more generic
// deny rules.
//
// Two policies, A and B, are equivalent, but not necessarily
// equal, if:
//
// A.IsSubset(B) && B.IsSubset(A)
func (p *Policy) IsSubset(o *Policy) bool {
for allow := range p.Allow {
// First, we check whether p's allow rule set
// is a subset of o's allow rule set.
var matched bool
for pattern := range o.Allow {
if matched = match(pattern, allow); matched {
break
}
}
if !matched {
return false
}
// Next, we check whether one of p's allow rules
// matches any of o's deny rules. If so, p would
// allow something o denies unless p also contains
// a deny rule equal or more generic than o's.
for super := range o.Deny {
if !match(allow, super) {
continue
}
matched = false
for deny := range p.Deny {
if matched = match(deny, super); matched {
break
}
}
if !matched {
return false
}
}
}
return true
}
// 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
}
func match(pattern, s string) bool {
if pattern == "" {
return false
}
if i := len(pattern) - 1; pattern[i] == '*' {
return strings.HasPrefix(s, pattern[:i])
}
return s == pattern
}