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
}
|