summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/development/assertions.go
blob: 7b08e6b8c24ec7f097805044cd1edc31befa8587 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package development

import (
	"fmt"

	"github.com/ccoveille/go-safecast"

	log "github.com/authzed/spicedb/internal/logging"
	devinterface "github.com/authzed/spicedb/pkg/proto/developer/v1"
	v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1"
	"github.com/authzed/spicedb/pkg/validationfile/blocks"
)

const maxDispatchDepth = 25

// RunAllAssertions runs all assertions found in the given assertions block against the
// developer context, returning whether any errors occurred.
func RunAllAssertions(devContext *DevContext, assertions *blocks.Assertions) ([]*devinterface.DeveloperError, error) {
	trueFailures, err := runAssertions(devContext, assertions.AssertTrue, v1.ResourceCheckResult_MEMBER, "Expected relation or permission %s to exist")
	if err != nil {
		return nil, err
	}

	caveatedFailures, err := runAssertions(devContext, assertions.AssertCaveated, v1.ResourceCheckResult_CAVEATED_MEMBER, "Expected relation or permission %s to be caveated")
	if err != nil {
		return nil, err
	}

	falseFailures, err := runAssertions(devContext, assertions.AssertFalse, v1.ResourceCheckResult_NOT_MEMBER, "Expected relation or permission %s to not exist")
	if err != nil {
		return nil, err
	}

	failures := append(trueFailures, caveatedFailures...)
	failures = append(failures, falseFailures...)
	return failures, nil
}

func runAssertions(devContext *DevContext, assertions []blocks.Assertion, expected v1.ResourceCheckResult_Membership, fmtString string) ([]*devinterface.DeveloperError, error) {
	var failures []*devinterface.DeveloperError

	for _, assertion := range assertions {
		// NOTE: zeroes are fine here to mean "unknown"
		lineNumber, err := safecast.ToUint32(assertion.SourcePosition.LineNumber)
		if err != nil {
			log.Err(err).Msg("could not cast lineNumber to uint32")
		}
		columnPosition, err := safecast.ToUint32(assertion.SourcePosition.ColumnPosition)
		if err != nil {
			log.Err(err).Msg("could not cast columnPosition to uint32")
		}

		rel := assertion.Relationship
		if rel.OptionalCaveat != nil {
			failures = append(failures, &devinterface.DeveloperError{
				Message: fmt.Sprintf("cannot specify a caveat on an assertion: `%s`", assertion.RelationshipWithContextString),
				Source:  devinterface.DeveloperError_ASSERTION,
				Kind:    devinterface.DeveloperError_UNKNOWN_RELATION,
				Context: assertion.RelationshipWithContextString,
				Line:    lineNumber,
				Column:  columnPosition,
			})
			continue
		}

		cr, err := RunCheck(devContext, rel.Resource, rel.Subject, assertion.CaveatContext)
		if err != nil {
			devErr, wireErr := DistinguishGraphError(
				devContext,
				err,
				devinterface.DeveloperError_ASSERTION,
				lineNumber,
				columnPosition,
				assertion.RelationshipWithContextString,
			)
			if wireErr != nil {
				return nil, wireErr
			}
			if devErr != nil {
				failures = append(failures, devErr)
			}
		} else if cr.Permissionship != expected {
			failures = append(failures, &devinterface.DeveloperError{
				Message:                       fmt.Sprintf(fmtString, assertion.RelationshipWithContextString),
				Source:                        devinterface.DeveloperError_ASSERTION,
				Kind:                          devinterface.DeveloperError_ASSERTION_FAILED,
				Context:                       assertion.RelationshipWithContextString,
				Line:                          lineNumber,
				Column:                        columnPosition,
				CheckDebugInformation:         cr.DispatchDebugInfo,
				CheckResolvedDebugInformation: cr.V1DebugInfo,
			})
		}
	}

	return failures, nil
}