Files
zeekay c0ca153cc2 rebrand: openziti → hanzozt (Hanzo Zero Trust)
Global rebrand of all OpenZiti references to Hanzo ZT:
- Go module paths: github.com/openziti/* → github.com/hanzozt/*
- Docker images: openziti/* → hanzozt/*
- Java packages: org.openziti.* → org.hanzozt.*
- Brand: OpenZiti → Hanzo ZT
- URLs: openziti.io → hanzozt.dev
2026-02-18 16:13:46 -08:00

442 lines
14 KiB
Go

/*
Copyright NetFoundry, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package boltz
import (
"github.com/michaelquigley/pfxlog"
"strings"
"github.com/hanzozt/storage/ast"
"github.com/pkg/errors"
"go.etcd.io/bbolt"
)
func (store *BaseStore[E]) MakeSymbolPublic(symbol string) {
if _, isMapSymbol := store.mapSymbols[symbol]; isMapSymbol || store.GetSymbol(symbol) != nil {
store.publicSymbols[symbol] = struct{}{}
} else {
pfxlog.Logger().Errorf("%v can't mark unknown symbol %v public", store.GetEntityType(), symbol)
}
}
func (store *BaseStore[E]) GetPublicSymbols() []string {
var symbols []string
for k := range store.publicSymbols {
symbols = append(symbols, k)
}
return symbols
}
func (store *BaseStore[E]) IsPublicSymbol(symbol string) bool {
if _, ok := store.publicSymbols[symbol]; ok {
return true
}
if parts := strings.Split(symbol, "."); len(parts) > 1 {
baseName := parts[0]
if _, isMapSymbol := store.mapSymbols[baseName]; isMapSymbol {
_, isPublic := store.publicSymbols[baseName]
return isPublic
}
}
return false
}
func (store *BaseStore[E]) GetSymbolType(name string) (ast.NodeType, bool) {
if symbol := store.GetSymbol(name); symbol != nil {
return symbol.GetType(), true
}
return 0, false
}
func (store *BaseStore[E]) GetSetSymbolTypes(name string) ast.SymbolTypes {
if symbol := store.GetSymbol(name); symbol != nil {
return symbol.GetLinkedType()
}
return nil
}
func (store *BaseStore[E]) IsSet(name string) (bool, bool) {
if symbol := store.GetSymbol(name); symbol != nil {
return symbol.IsSet(), true
}
return false, false
}
// GetSymbol returns the symbol for the given name, or nil if the symbol doesn't exist
func (store *BaseStore[E]) GetSymbol(name string) EntitySymbol {
/*
Types of symbols that we need to handle
1. Local single values (employee.name)
2. Local set values (sub-buckets of non-id keys) (myEntity.phoneNumbers)
3. Composite single value symbols (employee.manager.name)
4. Composite multi-value symbols (employee.directReports.phoneNumbers)
5. Maps (employee.tags.location, employee.manager.tags.location, employee.directReports.tags.location)
*/
if result := store.symbols.Get(name); result != nil {
// If it's a set symbol, make a runtime copy so we don't share cursor data. If we ever have a case where we
// are evaluating the same symbol in multiple context, this will still break, but since currently any given
// expression only involves a single symbol, this should not be a problem
if setSymbol, ok := result.(EntitySetSymbol); ok {
return setSymbol.GetRuntimeSymbol()
}
return result
}
// if it's a composite symbol, create a symbol on the fly to represent the name
if index := strings.IndexRune(name, '.'); index > 0 {
parts := strings.Split(name, ".")
// If it's a map symbol, create that now and return it
if mapSymbol := store.mapSymbols[parts[0]]; mapSymbol != nil {
return mapSymbol.createElementSymbol(name, parts)
}
if result := store.GetSymbol(parts[0]); result != nil {
linkedEntitySymbol, ok := result.(linkedEntitySymbol)
if !ok || linkedEntitySymbol.getLinkedType() == nil {
return nil // Can only have composite symbols if it's linked
}
subSymbolName := strings.Join(parts[1:], ".")
rest := linkedEntitySymbol.getLinkedType().GetSymbol(subSymbolName)
if rest == nil {
return nil
}
return store.createCompositeEntitySymbol(name, linkedEntitySymbol, rest)
}
}
return nil
}
func (store *BaseStore[E]) createCompositeEntitySymbol(name string, first linkedEntitySymbol, rest EntitySymbol) EntitySymbol {
ces, ok := rest.(compositeEntitySymbol)
var chain []EntitySymbol
if !ok {
chain = []EntitySymbol{first, rest}
} else {
chain = []EntitySymbol{first}
chain = append(chain, ces.getChain()...)
}
noneSet := true
var last EntitySymbol
var iterable []iterableEntitySymbol
for _, symbol := range chain {
if symbol.IsSet() {
noneSet = false
}
if iterableSymbol, ok := symbol.(iterableEntitySymbol); ok {
iterable = append(iterable, iterableSymbol)
}
last = symbol
}
// strip ids, since they are redundant
if _, ok := last.(*entityIdSymbol); ok {
chain = chain[:len(chain)-1]
if len(chain) == 1 {
return chain[0]
}
last = chain[len(chain)-1]
}
if noneSet {
return &nonSetCompositeEntitySymbol{
name: name,
symbolType: rest.GetType(),
chain: chain,
}
}
if len(chain) == len(iterable) || last.IsSet() {
return &compositeEntitySetSymbol{
name: name,
symbolType: rest.GetType(),
chain: iterable,
cursor: nil,
cursorLastF: func(tx *bbolt.Tx, key []byte) (fieldType FieldType, bytes []byte) {
return GetTypeAndValue(key)
},
}
}
return &compositeEntitySetSymbol{
name: name,
symbolType: rest.GetType(),
chain: iterable,
cursor: nil,
cursorLastF: last.Eval,
}
}
func (store *BaseStore[E]) addSymbol(name string, public bool, symbol EntitySymbol) EntitySymbol {
store.symbols.Put(name, symbol)
if public {
store.publicSymbols[name] = struct{}{}
}
return symbol
}
func (store *BaseStore[E]) inheritMapSymbol(symbol *entityMapSymbol) {
store.mapSymbols[symbol.key] = symbol
}
func (store *BaseStore[E]) GrantSymbols(child ConfigurableStore) {
for name, value := range store.symbols.AsMap() {
child.addSymbol(name, store.IsPublicSymbol(name), value)
}
for name, value := range store.mapSymbols {
child.inheritMapSymbol(value)
if store.IsPublicSymbol(name) {
child.MakeSymbolPublic(name)
}
}
}
func (store *BaseStore[E]) AddIdSymbol(name string, nodeType ast.NodeType) EntitySymbol {
return store.addSymbol(name, true, &entityIdSymbol{
store: store,
symbolType: nodeType,
path: []string{"id"},
})
}
func (store *BaseStore[E]) MapSymbol(name string, mapper SymbolMapper) {
if symbol := store.symbols.Get(name); symbol != nil {
store.symbols.Put(name, &symbolMapWrapper{
EntitySymbol: symbol,
SymbolMapper: mapper,
})
}
}
func (store *BaseStore[E]) AddSymbol(name string, nodeType ast.NodeType, prefix ...string) EntitySymbol {
return store.AddSymbolWithKey(name, nodeType, name, prefix...)
}
func (store *BaseStore[E]) AddFkSymbol(name string, linkedStore Store, prefix ...string) EntitySymbol {
return store.AddFkSymbolWithKey(name, name, linkedStore, prefix...)
}
func (store *BaseStore[E]) AddSymbolWithKey(name string, nodeType ast.NodeType, key string, prefix ...string) EntitySymbol {
return store.addSymbol(name, true, store.newEntitySymbol(name, nodeType, key, nil, prefix...))
}
func (store *BaseStore[E]) AddFkSymbolWithKey(name string, key string, linkedStore Store, prefix ...string) EntitySymbol {
return store.addSymbol(name, true, store.newEntitySymbol(name, ast.NodeTypeString, key, linkedStore, prefix...))
}
func (store *BaseStore[E]) AddMapSymbol(name string, nodeType ast.NodeType, key string, prefix ...string) {
store.mapSymbols[name] = &entityMapSymbol{
store: store,
key: key,
symbolType: nodeType,
prefix: prefix,
}
}
func (store *BaseStore[E]) AddEntitySymbol(symbol EntitySymbol) {
store.symbols.Put(symbol.GetName(), symbol)
}
func (store *BaseStore[E]) NewEntitySymbol(name string, nodeType ast.NodeType) EntitySymbol {
return store.newEntitySymbol(name, nodeType, name, nil)
}
func (store *BaseStore[E]) newEntitySymbol(name string, nodeType ast.NodeType, key string, linkedType Store, prefix ...string) *entitySymbol {
var path []string
var bucketF func(entityBucket *TypedBucket) *TypedBucket
if len(prefix) == 0 {
path = []string{key}
bucketF = func(entityBucket *TypedBucket) *TypedBucket {
return entityBucket
}
} else {
path = append(path, prefix...)
path = append(path, key)
bucketF = func(entityBucket *TypedBucket) *TypedBucket {
if entityBucket == nil {
return nil
}
return entityBucket.GetPath(prefix...)
}
}
return &entitySymbol{
store: store,
name: name,
getBucketF: bucketF,
symbolType: nodeType,
prefix: prefix,
key: key,
path: path,
linkedType: linkedType,
}
}
func (store *BaseStore[E]) AddSetSymbol(name string, nodeType ast.NodeType) EntitySetSymbol {
return store.addSetSymbol(name, nodeType, nil)
}
func (store *BaseStore[E]) AddPublicSetSymbol(name string, nodeType ast.NodeType) EntitySetSymbol {
result := store.addSetSymbol(name, nodeType, nil)
store.publicSymbols[name] = struct{}{}
return result
}
func (store *BaseStore[E]) AddFkSetSymbol(name string, listStore Store) EntitySetSymbol {
return store.addSetSymbol(name, ast.NodeTypeString, listStore)
}
func (store *BaseStore[E]) addSetSymbol(name string, nodeType ast.NodeType, listStore Store) EntitySetSymbol {
entitySymbol := store.newEntitySymbol(name, nodeType, name, listStore)
result := &entitySetSymbolImpl{
entitySymbol: entitySymbol,
}
store.symbols.Put(name, result)
return result
}
func (store *BaseStore[E]) AddExtEntitySymbols() {
store.AddIdSymbol(FieldId, ast.NodeTypeString)
store.AddSymbol(FieldCreatedAt, ast.NodeTypeDatetime)
store.AddSymbol(FieldUpdatedAt, ast.NodeTypeDatetime)
store.AddMapSymbol(FieldTags, ast.NodeTypeAnyType, FieldTags)
store.MakeSymbolPublic(FieldTags)
store.AddSymbol(FieldIsSystemEntity, ast.NodeTypeBool)
}
func (store *BaseStore[E]) NewScanner(sort []ast.SortField) Scanner {
if len(sort) > SortMax {
sort = sort[:SortMax]
}
if len(sort) == 0 || sort[0].Symbol() == "id" {
if len(sort) < 1 || sort[0].IsAscending() {
return &uniqueIndexScanner{store: store, forward: true}
}
return &uniqueIndexScanner{store: store, forward: false}
}
return &sortingScanner{store: store}
}
func (store *BaseStore[E]) newRowComparator(sort []ast.SortField) (RowComparator, error) {
// always have id as last sort element. this way if other sorts come out equal, we still
// can order on something, instead of having duplicates which causes rows to get discarded
sort = append(sort, ast.NewSortFieldNode("id", true))
var symbolsComparators []symbolComparator
for _, sortField := range sort {
symbol := store.symbols.Get(sortField.Symbol())
forward := sortField.IsAscending()
if symbol == nil {
return nil, errors.Errorf("no such sort field: %v", sortField.Symbol())
}
if symbol.IsSet() {
return nil, errors.Errorf("invalid sort field: %v", sortField.Symbol())
}
var symbolComparator symbolComparator
switch symbol.GetType() {
case ast.NodeTypeBool:
symbolComparator = &boolSymbolComparator{symbol: symbol, forward: forward}
case ast.NodeTypeDatetime:
symbolComparator = &datetimeSymbolComparator{symbol: symbol, forward: forward}
case ast.NodeTypeFloat64:
symbolComparator = &float64SymbolComparator{symbol: symbol, forward: forward}
case ast.NodeTypeInt64:
symbolComparator = &int64SymbolComparator{symbol: symbol, forward: forward}
case ast.NodeTypeString:
symbolComparator = &stringSymbolComparator{symbol: symbol, forward: forward}
default:
return nil, errors.Errorf("unsupported sort field type %v for field : %v", ast.NodeTypeName(symbol.GetType()), sortField.Symbol())
}
symbolsComparators = append(symbolsComparators, symbolComparator)
}
return &rowComparatorImpl{symbols: symbolsComparators}, nil
}
func (store *BaseStore[E]) QueryIds(tx *bbolt.Tx, queryString string) ([]string, int64, error) {
query, err := ast.Parse(store, queryString)
if err != nil {
return nil, 0, err
}
return store.QueryIdsC(tx, query)
}
func (store *BaseStore[E]) QueryIdsC(tx *bbolt.Tx, query ast.Query) ([]string, int64, error) {
scanner := store.NewScanner(query.GetSortFields())
return scanner.Scan(tx, query)
}
func (store *BaseStore[E]) IterateIds(tx *bbolt.Tx, filter ast.BoolNode) ast.SeekableSetCursor {
entitiesBucket := store.GetEntitiesBucket(tx)
if entitiesBucket == nil {
return ast.EmptyCursor
}
cursor := newFilteredCursor(tx, store, entitiesBucket.OpenSeekableCursor(), filter)
return cursor
}
func (store *BaseStore[E]) IterateValidIds(tx *bbolt.Tx, filter ast.BoolNode) ast.SeekableSetCursor {
result := store.IterateIds(tx, filter)
if store.isExtended {
validIdsCursor := &ValidIdsCursors{
tx: tx,
store: store,
wrapped: result,
}
if validIdsCursor.IsValid() && !validIdsCursor.IsExtendedDataPresent() {
validIdsCursor.Next()
}
result = validIdsCursor
}
return result
}
func (store *BaseStore[E]) QueryWithCursorC(tx *bbolt.Tx, cursorProvider ast.SetCursorProvider, query ast.Query) ([]string, int64, error) {
scanner := store.NewScanner(query.GetSortFields())
return scanner.ScanCursor(tx, cursorProvider, query)
}
type ValidIdsCursors struct {
tx *bbolt.Tx
store Store
wrapped ast.SeekableSetCursor
}
func (cursor *ValidIdsCursors) Seek(bytes []byte) {
cursor.wrapped.Seek(bytes)
for cursor.IsValid() && !cursor.IsExtendedDataPresent() {
cursor.wrapped.Next()
}
}
func (cursor *ValidIdsCursors) Next() {
cursor.wrapped.Next()
for cursor.IsValid() && !cursor.IsExtendedDataPresent() {
cursor.wrapped.Next()
}
}
func (cursor *ValidIdsCursors) IsExtendedDataPresent() bool {
return nil != cursor.store.GetEntityBucket(cursor.tx, cursor.wrapped.Current())
}
func (cursor *ValidIdsCursors) IsValid() bool {
return cursor.wrapped.IsValid()
}
func (cursor *ValidIdsCursors) Current() []byte {
return cursor.wrapped.Current()
}