github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/development/assertions.go (about)

     1  package development
     2  
     3  import (
     4  	"fmt"
     5  
     6  	v1t "github.com/authzed/authzed-go/proto/authzed/api/v1"
     7  
     8  	devinterface "github.com/authzed/spicedb/pkg/proto/developer/v1"
     9  	v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1"
    10  	"github.com/authzed/spicedb/pkg/tuple"
    11  	"github.com/authzed/spicedb/pkg/validationfile/blocks"
    12  )
    13  
    14  const maxDispatchDepth = 25
    15  
    16  // RunAllAssertions runs all assertions found in the given assertions block against the
    17  // developer context, returning whether any errors occurred.
    18  func RunAllAssertions(devContext *DevContext, assertions *blocks.Assertions) ([]*devinterface.DeveloperError, error) {
    19  	trueFailures, err := runAssertions(devContext, assertions.AssertTrue, v1.ResourceCheckResult_MEMBER, "Expected relation or permission %s to exist")
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	caveatedFailures, err := runAssertions(devContext, assertions.AssertCaveated, v1.ResourceCheckResult_CAVEATED_MEMBER, "Expected relation or permission %s to be caveated")
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	falseFailures, err := runAssertions(devContext, assertions.AssertFalse, v1.ResourceCheckResult_NOT_MEMBER, "Expected relation or permission %s to not exist")
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	failures := append(trueFailures, caveatedFailures...)
    35  	failures = append(failures, falseFailures...)
    36  	return failures, nil
    37  }
    38  
    39  func runAssertions(devContext *DevContext, assertions []blocks.Assertion, expected v1.ResourceCheckResult_Membership, fmtString string) ([]*devinterface.DeveloperError, error) {
    40  	var failures []*devinterface.DeveloperError
    41  
    42  	for _, assertion := range assertions {
    43  		tpl := tuple.MustFromRelationship[*v1t.ObjectReference, *v1t.SubjectReference, *v1t.ContextualizedCaveat](assertion.Relationship)
    44  
    45  		if tpl.Caveat != nil {
    46  			failures = append(failures, &devinterface.DeveloperError{
    47  				Message: fmt.Sprintf("cannot specify a caveat on an assertion: `%s`", assertion.RelationshipWithContextString),
    48  				Source:  devinterface.DeveloperError_ASSERTION,
    49  				Kind:    devinterface.DeveloperError_UNKNOWN_RELATION,
    50  				Context: assertion.RelationshipWithContextString,
    51  				Line:    uint32(assertion.SourcePosition.LineNumber),
    52  				Column:  uint32(assertion.SourcePosition.ColumnPosition),
    53  			})
    54  			continue
    55  		}
    56  
    57  		cr, err := RunCheck(devContext, tpl.ResourceAndRelation, tpl.Subject, assertion.CaveatContext)
    58  		if err != nil {
    59  			devErr, wireErr := DistinguishGraphError(
    60  				devContext,
    61  				err,
    62  				devinterface.DeveloperError_ASSERTION,
    63  				uint32(assertion.SourcePosition.LineNumber),
    64  				uint32(assertion.SourcePosition.ColumnPosition),
    65  				assertion.RelationshipWithContextString,
    66  			)
    67  			if wireErr != nil {
    68  				return nil, wireErr
    69  			}
    70  			if devErr != nil {
    71  				failures = append(failures, devErr)
    72  			}
    73  		} else if cr.Permissionship != expected {
    74  			failures = append(failures, &devinterface.DeveloperError{
    75  				Message:                       fmt.Sprintf(fmtString, assertion.RelationshipWithContextString),
    76  				Source:                        devinterface.DeveloperError_ASSERTION,
    77  				Kind:                          devinterface.DeveloperError_ASSERTION_FAILED,
    78  				Context:                       assertion.RelationshipWithContextString,
    79  				Line:                          uint32(assertion.SourcePosition.LineNumber),
    80  				Column:                        uint32(assertion.SourcePosition.ColumnPosition),
    81  				CheckDebugInformation:         cr.DispatchDebugInfo,
    82  				CheckResolvedDebugInformation: cr.V1DebugInfo,
    83  			})
    84  		}
    85  	}
    86  
    87  	return failures, nil
    88  }