Files
ziti/controller/persistence/policy_common.go
T
Andrew Martinez ac187ffa99 posture check implementation
- fix testing bugs
- remove testing timout function setters
- fixes deadlock due to posture data timeouts
- fix replace statements
- use map for posture data change tracking
2020-10-29 09:10:15 -04:00

246 lines
9.3 KiB
Go

package persistence
import (
"bytes"
"fmt"
"github.com/openziti/foundation/storage/ast"
"github.com/openziti/foundation/storage/boltz"
"github.com/openziti/foundation/util/errorz"
"github.com/openziti/foundation/util/stringz"
"github.com/openziti/foundation/validation"
"github.com/pkg/errors"
"go.etcd.io/bbolt"
"strings"
)
type roleAttributeChangeContext struct {
tx *bbolt.Tx
rolesSymbol boltz.EntitySetSymbol
linkCollection boltz.LinkCollection
relatedLinkCollection boltz.LinkCollection
denormLinkCollection boltz.RefCountedLinkCollection
errorz.ErrorHolder
}
func (store *baseStore) updateServicePolicyRelatedRoles(ctx *roleAttributeChangeContext, entityId []byte, newRoleAttributes []boltz.FieldTypeAndValue) {
cursor := ctx.rolesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
entityRoles := FieldValuesToIds(newRoleAttributes)
semanticSymbol := store.stores.servicePolicy.symbolSemantic
policyTypeSymbol := store.stores.servicePolicy.symbolPolicyType
isServices := ctx.rolesSymbol == store.stores.servicePolicy.symbolServiceRoles
isIdentity := ctx.rolesSymbol == store.stores.servicePolicy.symbolIdentityRoles
for ; cursor.IsValid(); cursor.Next() {
policyId := cursor.Current()
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
roles, ids, err := splitRolesAndIds(roleSet)
if err != nil {
ctx.SetError(err)
return
}
semantic := SemanticAllOf
if _, semanticValue := semanticSymbol.Eval(ctx.tx, policyId); semanticValue != nil {
semantic = string(semanticValue)
}
policyType := PolicyTypeDial
if fieldType, policyTypeValue := policyTypeSymbol.Eval(ctx.tx, policyId); fieldType == boltz.TypeInt32 {
policyType = *boltz.BytesToInt32(policyTypeValue)
}
if policyType == PolicyTypeDial {
if isServices {
ctx.denormLinkCollection = store.stores.edgeService.dialIdentitiesCollection
} else if isIdentity {
ctx.denormLinkCollection = store.stores.identity.dialServicesCollection
} else {
ctx.denormLinkCollection = store.stores.postureCheck.dialServicesCollection
}
} else if isServices {
ctx.denormLinkCollection = store.stores.edgeService.bindIdentitiesCollection
} else if isIdentity {
ctx.denormLinkCollection = store.stores.identity.bindServicesCollection
} else {
ctx.denormLinkCollection = store.stores.postureCheck.bindServicesCollection
}
evaluatePolicyAgainstEntity(ctx, semantic, entityId, policyId, ids, roles, entityRoles)
}
}
func EvaluatePolicy(ctx *roleAttributeChangeContext, policy Policy, roleAttributesSymbol boltz.EntitySetSymbol) {
policyId := []byte(policy.GetId())
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
roles, ids, err := splitRolesAndIds(roleSet)
if err != nil {
ctx.SetError(err)
return
}
if err := validateEntityIds(ctx.tx, ctx.linkCollection.GetLinkedSymbol().GetStore(), ctx.rolesSymbol.GetName(), ids); err != nil {
ctx.SetError(err)
return
}
cursor := roleAttributesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
for ; cursor.IsValid(); cursor.Next() {
entityId := cursor.Current()
entityRoleAttributes := roleAttributesSymbol.EvalStringList(ctx.tx, entityId)
evaluatePolicyAgainstEntity(ctx, policy.GetSemantic(), entityId, policyId, ids, roles, entityRoleAttributes)
}
}
func validateEntityIds(tx *bbolt.Tx, store boltz.ListStore, field string, ids []string) error {
var invalid []string
for _, val := range ids {
if !store.IsEntityPresent(tx, val) {
invalid = append(invalid, val)
}
}
if len(invalid) > 0 {
return validation.NewFieldError(fmt.Sprintf("no %v found with the given ids", store.GetEntityType()), field, invalid)
}
return nil
}
func UpdateRelatedRoles(ctx *roleAttributeChangeContext, entityId []byte, newRoleAttributes []boltz.FieldTypeAndValue, semanticSymbol boltz.EntitySymbol) {
cursor := ctx.rolesSymbol.GetStore().IterateIds(ctx.tx, ast.BoolNodeTrue)
entityRoles := FieldValuesToIds(newRoleAttributes)
for ; cursor.IsValid(); cursor.Next() {
policyId := cursor.Current()
roleSet := ctx.rolesSymbol.EvalStringList(ctx.tx, policyId)
roles, ids, err := splitRolesAndIds(roleSet)
if err != nil {
ctx.SetError(err)
return
}
semantic := SemanticAllOf
if _, semanticValue := semanticSymbol.Eval(ctx.tx, policyId); semanticValue != nil {
semantic = string(semanticValue)
}
evaluatePolicyAgainstEntity(ctx, semantic, entityId, policyId, ids, roles, entityRoles)
}
}
func evaluatePolicyAgainstEntity(ctx *roleAttributeChangeContext, semantic string, entityId, policyId []byte, ids, roles, roleAttributes []string) {
if stringz.Contains(ids, string(entityId)) || stringz.Contains(roles, "all") ||
(strings.EqualFold(semantic, SemanticAllOf) && len(roles) > 0 && stringz.ContainsAll(roleAttributes, roles...)) ||
(strings.EqualFold(semantic, SemanticAnyOf) && len(roles) > 0 && stringz.ContainsAny(roleAttributes, roles...)) {
ProcessEntityPolicyMatched(ctx, entityId, policyId)
} else {
ProcessEntityPolicyUnmatched(ctx, entityId, policyId)
}
}
func ProcessEntityPolicyMatched(ctx *roleAttributeChangeContext, entityId, policyId []byte) {
if added, err := ctx.linkCollection.AddLink(ctx.tx, policyId, entityId); ctx.SetError(err) || !added {
return
}
cursor := ctx.relatedLinkCollection.IterateLinks(ctx.tx, policyId)
for ; cursor.IsValid(); cursor.Next() {
relatedEntityId := cursor.Current()
_, err := ctx.denormLinkCollection.IncrementLinkCount(ctx.tx, entityId, relatedEntityId)
if ctx.SetError(err) {
return
}
}
}
func ProcessEntityPolicyUnmatched(ctx *roleAttributeChangeContext, entityId, policyId []byte) {
if removed, err := ctx.linkCollection.RemoveLink(ctx.tx, policyId, entityId); ctx.SetError(err) || !removed {
return
}
cursor := ctx.relatedLinkCollection.IterateLinks(ctx.tx, policyId)
for ; cursor.IsValid(); cursor.Next() {
relatedEntityId := cursor.Current()
_, err := ctx.denormLinkCollection.DecrementLinkCount(ctx.tx, entityId, relatedEntityId)
if ctx.SetError(err) {
return
}
}
}
type denormCheckCtx struct {
name string
tx *bbolt.Tx
sourceStore boltz.CrudStore
targetStore boltz.CrudStore
policyStore boltz.CrudStore
sourceCollection boltz.LinkCollection
targetCollection boltz.LinkCollection
targetDenormCollection boltz.RefCountedLinkCollection
policyFilter func(policyId []byte) bool
errorSink func(err error, fixed bool)
repair bool
}
func validatePolicyDenormalization(ctx *denormCheckCtx) error {
for sourceCursor := ctx.sourceStore.IterateIds(ctx.tx, ast.BoolNodeTrue); sourceCursor.IsValid(); sourceCursor.Next() {
sourceEntityId := sourceCursor.Current()
for targetCursor := ctx.targetStore.IterateIds(ctx.tx, ast.BoolNodeTrue); targetCursor.IsValid(); targetCursor.Next() {
targetEntityId := targetCursor.Current()
var relatedPolicies []string
for policyCursor := ctx.policyStore.IterateIds(ctx.tx, ast.BoolNodeTrue); policyCursor.IsValid(); policyCursor.Next() {
policyId := policyCursor.Current()
if ctx.policyFilter == nil || ctx.policyFilter(policyId) {
sourceRelated := isRelatedByLinkCollection(ctx.tx, ctx.sourceCollection, policyId, sourceEntityId)
targetRelated := isRelatedByLinkCollection(ctx.tx, ctx.targetCollection, policyId, targetEntityId)
if sourceRelated && targetRelated {
relatedPolicies = append(relatedPolicies, string(policyId))
}
}
}
linkCount := len(relatedPolicies)
var sourceLinkCount, targetLinkCount *int32
var err error
if ctx.repair {
sourceLinkCount, targetLinkCount, err = ctx.targetDenormCollection.SetLinkCount(ctx.tx, sourceEntityId, targetEntityId, linkCount)
} else {
sourceLinkCount, targetLinkCount = ctx.targetDenormCollection.GetLinkCounts(ctx.tx, sourceEntityId, targetEntityId)
}
if err != nil {
return err
}
logDiscrepencies(ctx, linkCount, sourceEntityId, targetEntityId, sourceLinkCount, targetLinkCount)
}
}
return nil
}
func logDiscrepencies(ctx *denormCheckCtx, count int, sourceId, targetId []byte, sourceLinkCount, targetLinkCount *int32) {
oldValuesMatch := (sourceLinkCount == nil && targetLinkCount == nil) || (sourceLinkCount != nil && targetLinkCount != nil && *sourceLinkCount == *targetLinkCount)
if !oldValuesMatch {
err := errors.Errorf("%v: ismatched link counts. %v %v (%v) <-> %v %v (%v), should be both are %v", ctx.name,
ctx.sourceStore.GetSingularEntityType(), string(sourceId), sourceLinkCount,
ctx.targetStore.GetSingularEntityType(), string(targetId), targetLinkCount, count)
ctx.errorSink(err, ctx.repair)
}
if ((sourceLinkCount == nil || *sourceLinkCount == 0) && count != 0) ||
(sourceLinkCount != nil && *sourceLinkCount != int32(count)) {
sourceCount := int32(0)
if sourceLinkCount != nil {
sourceCount = *sourceLinkCount
}
err := errors.Errorf("%v: incorrect link counts for %v %v <-> %v %v is %v, should be %v", ctx.name,
ctx.sourceStore.GetSingularEntityType(), string(sourceId),
ctx.targetStore.GetSingularEntityType(), string(targetId),
sourceCount, count)
ctx.errorSink(err, ctx.repair)
}
}
func isRelatedByLinkCollection(tx *bbolt.Tx, linkCollection boltz.LinkCollection, entityId, relatedId []byte) bool {
cursor := linkCollection.IterateLinks(tx, entityId)
cursor.Seek(relatedId)
return bytes.Equal(cursor.Current(), relatedId)
}