mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-25 01:35:18 -06:00
51181a8257
A bunch of smaller follow-up tasks to #4381. * storage/disk_test: check invalid patches with wildcard partition, too * docs/disk: add caveat re: bundles loaded into memory * storage/disk: auto-manage /system partitions If these are found in the user-provided partitions, we'll error out. * storage/disk: pretty-print partitions with "*" instead of %2A * storage/disk: respect wildcard-replacement in partition validation It is now allowed to replace a partition like /foo/bar by /foo/* also if multiple wildcards are used. Caveats: You cannot add a wildcard partition like /*/*, since it would overlap the managed "/system/*" partition. When attempting to go back from /foo/* to /foo/bar, an error is raised _unconditionally_ -- we could check the existing data, but currently don't. * storage/disk: check prefix when adding wildcard partitions The previously done check would have falsely returned that there is no problem when adding a wildcard partition: lookup of "/foo/*" with '*' not interpreted as a wildcard, but as a string, would yield a not-found, even if there was any data under /foo/. Now, we'll check the prefix-until-wildcard. It's more cautious than theoretically necessary, but safe. Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
158 lines
3.4 KiB
Go
158 lines
3.4 KiB
Go
// Copyright 2021 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package disk
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/open-policy-agent/opa/storage"
|
|
)
|
|
|
|
const pathWildcard = "*"
|
|
|
|
type pathMapper struct {
|
|
dataPrefix string
|
|
dataPrefixNoTrailingSlash string
|
|
policiesPrefix string
|
|
}
|
|
|
|
func newPathMapper(schemaVersion, partitionVersion int64) *pathMapper {
|
|
var pm pathMapper
|
|
pm.dataPrefix = fmt.Sprintf("/%v/%v/data/", schemaVersion, partitionVersion)
|
|
pm.dataPrefixNoTrailingSlash = pm.dataPrefix[:len(pm.dataPrefix)-1]
|
|
pm.policiesPrefix = fmt.Sprintf("/%v/%v/policies/", schemaVersion, partitionVersion)
|
|
return &pm
|
|
}
|
|
|
|
func (pm *pathMapper) PolicyKey2ID(key []byte) string {
|
|
return string(key[len(pm.policiesPrefix):])
|
|
}
|
|
|
|
func (pm *pathMapper) PolicyIDPrefix() []byte {
|
|
return []byte(pm.policiesPrefix)
|
|
}
|
|
|
|
func (pm *pathMapper) PolicyID2Key(id string) []byte {
|
|
return []byte(pm.policiesPrefix + id)
|
|
}
|
|
|
|
func (pm *pathMapper) DataKey2Path(key []byte) (storage.Path, error) {
|
|
p, ok := storage.ParsePathEscaped(string(key))
|
|
if !ok {
|
|
return nil, &storage.Error{Code: storage.InternalErr, Message: fmt.Sprintf("corrupt key: %s", key)}
|
|
}
|
|
// skip /<schema_version>/<partition_version>/<data prefix>
|
|
return p[3:], nil
|
|
}
|
|
|
|
func (pm *pathMapper) DataPrefix2Key(path storage.Path) ([]byte, error) {
|
|
if len(path) == 0 {
|
|
return []byte(pm.dataPrefix), nil
|
|
}
|
|
return []byte(pm.dataPrefixNoTrailingSlash + path.String() + "/"), nil
|
|
}
|
|
|
|
func (pm *pathMapper) DataPath2Key(path storage.Path) ([]byte, error) {
|
|
if len(path) == 0 {
|
|
return nil, &storage.Error{Code: storage.InternalErr, Message: "empty path"}
|
|
}
|
|
return []byte(pm.dataPrefixNoTrailingSlash + path.String()), nil
|
|
}
|
|
|
|
type pathSet []storage.Path
|
|
|
|
func (ps pathSet) String() string {
|
|
if len(ps) == 0 {
|
|
return "[]"
|
|
}
|
|
buf := strings.Builder{}
|
|
buf.WriteRune('[')
|
|
for j, p := range ps.Sorted() {
|
|
if j != 0 {
|
|
buf.WriteRune(' ')
|
|
}
|
|
buf.WriteString(toString(p))
|
|
}
|
|
buf.WriteRune(']')
|
|
return buf.String()
|
|
}
|
|
|
|
func (ps pathSet) IsDisjoint() bool {
|
|
for i := range ps {
|
|
for j := range ps {
|
|
if i != j {
|
|
if hasPrefixWithWildcard(ps[i], ps[j]) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// hasPrefixWithWildcard returns true if p starts with other; respecting
|
|
// wildcards
|
|
func hasPrefixWithWildcard(p, other storage.Path) bool {
|
|
if len(other) > len(p) {
|
|
return false
|
|
}
|
|
for i := range other {
|
|
if p[i] == pathWildcard || other[i] == pathWildcard {
|
|
continue
|
|
}
|
|
if p[i] != other[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// isMatchedBy returns true if p starts with other, or is matched by it
|
|
// respecting wildcards _in other_ -- not in p.
|
|
func isMatchedBy(p, other storage.Path) bool {
|
|
if len(other) != len(p) {
|
|
return false
|
|
}
|
|
for i := range other {
|
|
if other[i] == pathWildcard {
|
|
continue
|
|
}
|
|
if p[i] != other[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (ps pathSet) Diff(other pathSet) pathSet {
|
|
diff := pathSet{}
|
|
for _, x := range ps {
|
|
if !other.Contains(x) {
|
|
diff = append(diff, x)
|
|
}
|
|
}
|
|
return diff
|
|
}
|
|
|
|
func (ps pathSet) Contains(x storage.Path) bool {
|
|
for _, other := range ps {
|
|
if x.Equal(other) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (ps pathSet) Sorted() []storage.Path {
|
|
cpy := make(pathSet, len(ps))
|
|
copy(cpy, ps)
|
|
sort.Slice(cpy, func(i, j int) bool {
|
|
return cpy[i].Compare(cpy[j]) < 0
|
|
})
|
|
return cpy
|
|
}
|