github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/development/warningdefs.go (about) 1 package development 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 corev1 "github.com/authzed/spicedb/pkg/proto/core/v1" 9 devinterface "github.com/authzed/spicedb/pkg/proto/developer/v1" 10 "github.com/authzed/spicedb/pkg/tuple" 11 "github.com/authzed/spicedb/pkg/typesystem" 12 ) 13 14 var lintRelationReferencesParentType = func( 15 ctx context.Context, 16 relation *corev1.Relation, 17 ts *typesystem.TypeSystem, 18 ) (*devinterface.DeveloperWarning, error) { 19 parentDef := ts.Namespace() 20 if strings.HasSuffix(relation.Name, parentDef.Name) { 21 if ts.IsPermission(relation.Name) { 22 return warningForMetadata( 23 fmt.Sprintf("Permission %q references parent type %q in its name; it is recommended to drop the suffix", relation.Name, parentDef.Name), 24 relation, 25 ), nil 26 } 27 28 return warningForMetadata( 29 fmt.Sprintf("Relation %q references parent type %q in its name; it is recommended to drop the suffix", relation.Name, parentDef.Name), 30 relation, 31 ), nil 32 } 33 34 return nil, nil 35 } 36 37 var lintPermissionReferencingItself = func( 38 ctx context.Context, 39 computedUserset *corev1.ComputedUserset, 40 sourcePosition *corev1.SourcePosition, 41 ts *typesystem.TypeSystem, 42 ) (*devinterface.DeveloperWarning, error) { 43 parentRelation := ctx.Value(relationKey).(*corev1.Relation) 44 permName := parentRelation.Name 45 if computedUserset.Relation == permName { 46 return warningForPosition( 47 fmt.Sprintf("Permission %q references itself, which will cause an error to be raised due to infinite recursion", permName), 48 sourcePosition, 49 ), nil 50 } 51 52 return nil, nil 53 } 54 55 var lintArrowReferencingUnreachable = func( 56 ctx context.Context, 57 ttu *corev1.TupleToUserset, 58 sourcePosition *corev1.SourcePosition, 59 ts *typesystem.TypeSystem, 60 ) (*devinterface.DeveloperWarning, error) { 61 parentRelation := ctx.Value(relationKey).(*corev1.Relation) 62 63 referencedRelation, ok := ts.GetRelation(ttu.Tupleset.Relation) 64 if !ok { 65 return nil, nil 66 } 67 68 allowedSubjectTypes, err := ts.AllowedSubjectRelations(referencedRelation.Name) 69 if err != nil { 70 return nil, err 71 } 72 73 wasFound := false 74 for _, subjectType := range allowedSubjectTypes { 75 nts, err := ts.TypeSystemForNamespace(ctx, subjectType.Namespace) 76 if err != nil { 77 return nil, err 78 } 79 80 _, ok := nts.GetRelation(ttu.ComputedUserset.Relation) 81 if ok { 82 wasFound = true 83 } 84 } 85 86 if !wasFound { 87 return warningForPosition( 88 fmt.Sprintf( 89 "Arrow `%s->%s` under permission %q references relation/permission %q that does not exist on any subject types of relation %q", 90 ttu.Tupleset.Relation, 91 ttu.ComputedUserset.Relation, 92 parentRelation.Name, 93 ttu.ComputedUserset.Relation, 94 ttu.Tupleset.Relation, 95 ), 96 sourcePosition, 97 ), nil 98 } 99 100 return nil, nil 101 } 102 103 var lintArrowOverSubRelation = func( 104 ctx context.Context, 105 ttu *corev1.TupleToUserset, 106 sourcePosition *corev1.SourcePosition, 107 ts *typesystem.TypeSystem, 108 ) (*devinterface.DeveloperWarning, error) { 109 parentRelation := ctx.Value(relationKey).(*corev1.Relation) 110 111 referencedRelation, ok := ts.GetRelation(ttu.Tupleset.Relation) 112 if !ok { 113 return nil, nil 114 } 115 116 allowedSubjectTypes, err := ts.AllowedSubjectRelations(referencedRelation.Name) 117 if err != nil { 118 return nil, err 119 } 120 121 for _, subjectType := range allowedSubjectTypes { 122 if subjectType.Relation != tuple.Ellipsis { 123 return warningForPosition( 124 fmt.Sprintf( 125 "Arrow `%s->%s` under permission %q references relation %q that has relation %q on subject %q: *the subject relation will be ignored for the arrow*", 126 ttu.Tupleset.Relation, 127 ttu.ComputedUserset.Relation, 128 parentRelation.Name, 129 ttu.Tupleset.Relation, 130 subjectType.Relation, 131 subjectType.Namespace, 132 ), 133 sourcePosition, 134 ), nil 135 } 136 } 137 138 return nil, nil 139 } 140 141 var lintArrowReferencingRelation = func( 142 ctx context.Context, 143 ttu *corev1.TupleToUserset, 144 sourcePosition *corev1.SourcePosition, 145 ts *typesystem.TypeSystem, 146 ) (*devinterface.DeveloperWarning, error) { 147 parentRelation := ctx.Value(relationKey).(*corev1.Relation) 148 149 referencedRelation, ok := ts.GetRelation(ttu.Tupleset.Relation) 150 if !ok { 151 return nil, nil 152 } 153 154 // For each subject type of the referenced relation, check if the referenced permission 155 // is, in fact, a relation. 156 allowedSubjectTypes, err := ts.AllowedSubjectRelations(referencedRelation.Name) 157 if err != nil { 158 return nil, err 159 } 160 161 for _, subjectType := range allowedSubjectTypes { 162 nts, err := ts.TypeSystemForNamespace(ctx, subjectType.Namespace) 163 if err != nil { 164 return nil, err 165 } 166 167 targetRelation, ok := nts.GetRelation(ttu.ComputedUserset.Relation) 168 if !ok { 169 continue 170 } 171 172 if !nts.IsPermission(targetRelation.Name) { 173 return warningForPosition( 174 fmt.Sprintf( 175 "Arrow `%s->%s` under permission %q references relation %q on definition %q; it is recommended to point to a permission", 176 ttu.Tupleset.Relation, 177 ttu.ComputedUserset.Relation, 178 parentRelation.Name, 179 targetRelation.Name, 180 subjectType.Namespace, 181 ), 182 sourcePosition, 183 ), nil 184 } 185 } 186 187 return nil, nil 188 }