diff options
Diffstat (limited to 'vendor/github.com/authzed/spicedb/internal/graph/membershipset.go')
| -rw-r--r-- | vendor/github.com/authzed/spicedb/internal/graph/membershipset.go | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/internal/graph/membershipset.go b/vendor/github.com/authzed/spicedb/internal/graph/membershipset.go new file mode 100644 index 0000000..8ab20f4 --- /dev/null +++ b/vendor/github.com/authzed/spicedb/internal/graph/membershipset.go @@ -0,0 +1,243 @@ +package graph + +import ( + "github.com/authzed/spicedb/internal/caveats" + core "github.com/authzed/spicedb/pkg/proto/core/v1" + v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + "github.com/authzed/spicedb/pkg/tuple" +) + +var ( + caveatOr = caveats.Or + caveatAnd = caveats.And + caveatSub = caveats.Subtract + wrapCaveat = caveats.CaveatAsExpr +) + +// CheckResultsMap defines a type that is a map from resource ID to ResourceCheckResult. +// This must match that defined in the DispatchCheckResponse for the `results_by_resource_id` +// field. +type CheckResultsMap map[string]*v1.ResourceCheckResult + +// NewMembershipSet constructs a new helper set for tracking the membership found for a dispatched +// check request. +func NewMembershipSet() *MembershipSet { + return &MembershipSet{ + hasDeterminedMember: false, + membersByID: map[string]*core.CaveatExpression{}, + } +} + +func membershipSetFromMap(mp map[string]*core.CaveatExpression) *MembershipSet { + ms := NewMembershipSet() + for resourceID, result := range mp { + ms.addMember(resourceID, result) + } + return ms +} + +// MembershipSet is a helper set that trackes the membership results for a dispatched Check +// request, including tracking of the caveats associated with found resource IDs. +type MembershipSet struct { + membersByID map[string]*core.CaveatExpression + hasDeterminedMember bool +} + +// AddDirectMember adds a resource ID that was *directly* found for the dispatched check, with +// optional caveat found on the relationship. +func (ms *MembershipSet) AddDirectMember(resourceID string, caveat *core.ContextualizedCaveat) { + ms.addMember(resourceID, wrapCaveat(caveat)) +} + +// AddMemberViaRelationship adds a resource ID that was found via another relationship, such +// as the result of an arrow operation. The `parentRelationship` is the relationship that was +// followed before the resource itself was resolved. This method will properly apply the caveat(s) +// from both the parent relationship and the resource's result itself, assuming either have a caveat +// associated. +func (ms *MembershipSet) AddMemberViaRelationship( + resourceID string, + resourceCaveatExpression *core.CaveatExpression, + parentRelationship tuple.Relationship, +) { + ms.AddMemberWithParentCaveat(resourceID, resourceCaveatExpression, parentRelationship.OptionalCaveat) +} + +// AddMemberWithParentCaveat adds the given resource ID as a member with the parent caveat +// combined via intersection with the resource's caveat. The parent caveat may be nil. +func (ms *MembershipSet) AddMemberWithParentCaveat( + resourceID string, + resourceCaveatExpression *core.CaveatExpression, + parentCaveat *core.ContextualizedCaveat, +) { + intersection := caveatAnd(wrapCaveat(parentCaveat), resourceCaveatExpression) + ms.addMember(resourceID, intersection) +} + +// AddMemberWithOptionalCaveats adds the given resource ID as a member with the optional caveats combined +// via intersection. +func (ms *MembershipSet) AddMemberWithOptionalCaveats( + resourceID string, + caveats []*core.CaveatExpression, +) { + if len(caveats) == 0 { + ms.addMember(resourceID, nil) + return + } + + intersection := caveats[0] + for _, caveat := range caveats[1:] { + intersection = caveatAnd(intersection, caveat) + } + + ms.addMember(resourceID, intersection) +} + +func (ms *MembershipSet) addMember(resourceID string, caveatExpr *core.CaveatExpression) { + existing, ok := ms.membersByID[resourceID] + if !ok { + ms.hasDeterminedMember = ms.hasDeterminedMember || caveatExpr == nil + ms.membersByID[resourceID] = caveatExpr + return + } + + // If a determined membership result has already been found (i.e. there is no caveat), + // then nothing more to do. + if existing == nil { + return + } + + // If the new caveat expression is nil, then we are adding a determined result. + if caveatExpr == nil { + ms.hasDeterminedMember = true + ms.membersByID[resourceID] = nil + return + } + + // Otherwise, the caveats get unioned together. + ms.membersByID[resourceID] = caveatOr(existing, caveatExpr) +} + +// UnionWith combines the results found in the given map with the members of this set. +// The changes are made in-place. +func (ms *MembershipSet) UnionWith(resultsMap CheckResultsMap) { + for resourceID, details := range resultsMap { + if details.Membership != v1.ResourceCheckResult_NOT_MEMBER { + ms.addMember(resourceID, details.Expression) + } + } +} + +// IntersectWith intersects the results found in the given map with the members of this set. +// The changes are made in-place. +func (ms *MembershipSet) IntersectWith(resultsMap CheckResultsMap) { + for resourceID := range ms.membersByID { + if details, ok := resultsMap[resourceID]; !ok || details.Membership == v1.ResourceCheckResult_NOT_MEMBER { + delete(ms.membersByID, resourceID) + } + } + + ms.hasDeterminedMember = false + for resourceID, details := range resultsMap { + existing, ok := ms.membersByID[resourceID] + if !ok || details.Membership == v1.ResourceCheckResult_NOT_MEMBER { + continue + } + if existing == nil && details.Expression == nil { + ms.hasDeterminedMember = true + continue + } + + ms.membersByID[resourceID] = caveatAnd(existing, details.Expression) + } +} + +// Subtract subtracts the results found in the given map with the members of this set. +// The changes are made in-place. +func (ms *MembershipSet) Subtract(resultsMap CheckResultsMap) { + ms.hasDeterminedMember = false + for resourceID, expression := range ms.membersByID { + if details, ok := resultsMap[resourceID]; ok && details.Membership != v1.ResourceCheckResult_NOT_MEMBER { + // If the incoming member has no caveat, then this removal is absolute. + if details.Expression == nil { + delete(ms.membersByID, resourceID) + continue + } + + // Otherwise, the caveat expression gets combined with an intersection of the inversion + // of the expression. + ms.membersByID[resourceID] = caveatSub(expression, details.Expression) + } else { + if expression == nil { + ms.hasDeterminedMember = true + } + } + } +} + +// HasConcreteResourceID returns whether the resourceID was found in the set +// and has no caveat attached. +func (ms *MembershipSet) HasConcreteResourceID(resourceID string) bool { + if ms == nil { + return false + } + + found, ok := ms.membersByID[resourceID] + return ok && found == nil +} + +// GetResourceID returns a bool indicating whether the resource is found in the set and the +// associated caveat expression, if any. +func (ms *MembershipSet) GetResourceID(resourceID string) (bool, *core.CaveatExpression) { + if ms == nil { + return false, nil + } + + caveat, ok := ms.membersByID[resourceID] + return ok, caveat +} + +// Size returns the number of elements in the membership set. +func (ms *MembershipSet) Size() int { + if ms == nil { + return 0 + } + + return len(ms.membersByID) +} + +// IsEmpty returns true if the set is empty. +func (ms *MembershipSet) IsEmpty() bool { + if ms == nil { + return true + } + + return len(ms.membersByID) == 0 +} + +// HasDeterminedMember returns whether there exists at least one non-caveated member of the set. +func (ms *MembershipSet) HasDeterminedMember() bool { + if ms == nil { + return false + } + + return ms.hasDeterminedMember +} + +// AsCheckResultsMap converts the membership set back into a CheckResultsMap for placement into +// a DispatchCheckResult. +func (ms *MembershipSet) AsCheckResultsMap() CheckResultsMap { + resultsMap := make(CheckResultsMap, len(ms.membersByID)) + for resourceID, caveat := range ms.membersByID { + membership := v1.ResourceCheckResult_MEMBER + if caveat != nil { + membership = v1.ResourceCheckResult_CAVEATED_MEMBER + } + + resultsMap[resourceID] = &v1.ResourceCheckResult{ + Membership: membership, + Expression: caveat, + } + } + + return resultsMap +} |
