github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/validationfile/blocks/assertions.go (about) 1 package blocks 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" 9 yamlv3 "gopkg.in/yaml.v3" 10 11 "github.com/authzed/spicedb/pkg/spiceerrors" 12 "github.com/authzed/spicedb/pkg/tuple" 13 ) 14 15 // Assertions represents assertions defined in the validation file. 16 type Assertions struct { 17 // AssertTrue is the set of relationships to assert true. 18 AssertTrue []Assertion `yaml:"assertTrue"` 19 20 // AssertCaveated is the set of relationships to assert that are caveated. 21 AssertCaveated []Assertion `yaml:"assertCaveated"` 22 23 // AssertFalse is the set of relationships to assert false. 24 AssertFalse []Assertion `yaml:"assertFalse"` 25 26 // SourcePosition is the position of the assertions in the file. 27 SourcePosition spiceerrors.SourcePosition 28 } 29 30 // Assertion is a parsed assertion. 31 type Assertion struct { 32 // RelationshipWithContextString is the string form of the assertion, including optional context. 33 // Forms: 34 // `document:firstdoc#view@user:tom` 35 // `document:seconddoc#view@user:sarah with {"some":"contexthere"}` 36 RelationshipWithContextString string 37 38 // Relationship is the parsed relationship on which the assertion is being 39 // run. 40 Relationship *v1.Relationship 41 42 // CaveatContext is the caveat context for the assertion, if any. 43 CaveatContext map[string]any 44 45 // SourcePosition is the position of the assertion in the file. 46 SourcePosition spiceerrors.SourcePosition 47 } 48 49 type internalAssertions struct { 50 // AssertTrue is the set of relationships to assert true. 51 AssertTrue []Assertion `yaml:"assertTrue"` 52 53 // AssertCaveated is the set of relationships to assert that are caveated. 54 AssertCaveated []Assertion `yaml:"assertCaveated"` 55 56 // AssertFalse is the set of relationships to assert false. 57 AssertFalse []Assertion `yaml:"assertFalse"` 58 } 59 60 // UnmarshalYAML is a custom unmarshaller. 61 func (a *Assertions) UnmarshalYAML(node *yamlv3.Node) error { 62 ia := internalAssertions{} 63 if err := node.Decode(&ia); err != nil { 64 return convertYamlError(err) 65 } 66 67 a.AssertTrue = ia.AssertTrue 68 a.AssertFalse = ia.AssertFalse 69 a.AssertCaveated = ia.AssertCaveated 70 a.SourcePosition = spiceerrors.SourcePosition{LineNumber: node.Line, ColumnPosition: node.Column} 71 return nil 72 } 73 74 // UnmarshalYAML is a custom unmarshaller. 75 func (a *Assertion) UnmarshalYAML(node *yamlv3.Node) error { 76 relationshipWithContextString := "" 77 78 if err := node.Decode(&relationshipWithContextString); err != nil { 79 return convertYamlError(err) 80 } 81 82 trimmed := strings.TrimSpace(relationshipWithContextString) 83 84 // Check for caveat context. 85 parts := strings.SplitN(trimmed, " with ", 2) 86 if len(parts) == 0 { 87 return spiceerrors.NewErrorWithSource( 88 fmt.Errorf("error parsing assertion `%s`", trimmed), 89 trimmed, 90 uint64(node.Line), 91 uint64(node.Column), 92 ) 93 } 94 95 tpl := tuple.Parse(strings.TrimSpace(parts[0])) 96 if tpl == nil { 97 return spiceerrors.NewErrorWithSource( 98 fmt.Errorf("error parsing relationship in assertion `%s`", trimmed), 99 trimmed, 100 uint64(node.Line), 101 uint64(node.Column), 102 ) 103 } 104 105 a.Relationship = tuple.MustToRelationship(tpl) 106 107 if len(parts) == 2 { 108 caveatContextMap := make(map[string]any, 0) 109 err := json.Unmarshal([]byte(parts[1]), &caveatContextMap) 110 if err != nil { 111 return spiceerrors.NewErrorWithSource( 112 fmt.Errorf("error parsing caveat context in assertion `%s`: %w", trimmed, err), 113 trimmed, 114 uint64(node.Line), 115 uint64(node.Column), 116 ) 117 } 118 119 a.CaveatContext = caveatContextMap 120 } 121 122 a.RelationshipWithContextString = relationshipWithContextString 123 a.SourcePosition = spiceerrors.SourcePosition{LineNumber: node.Line, ColumnPosition: node.Column} 124 return nil 125 } 126 127 // ParseAssertionsBlock parses the given contents as an assertions block. 128 func ParseAssertionsBlock(contents []byte) (*Assertions, error) { 129 a := internalAssertions{} 130 if err := yamlv3.Unmarshal(contents, &a); err != nil { 131 return nil, convertYamlError(err) 132 } 133 return &Assertions{ 134 AssertTrue: a.AssertTrue, 135 AssertCaveated: a.AssertCaveated, 136 AssertFalse: a.AssertFalse, 137 }, nil 138 }