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

     1  package namespace
     2  
     3  import (
     4  	"github.com/authzed/spicedb/pkg/caveats"
     5  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
     6  	iv1 "github.com/authzed/spicedb/pkg/proto/impl/v1"
     7  	"github.com/authzed/spicedb/pkg/spiceerrors"
     8  )
     9  
    10  // Namespace creates a namespace definition with one or more defined relations.
    11  func Namespace(name string, relations ...*core.Relation) *core.NamespaceDefinition {
    12  	return &core.NamespaceDefinition{
    13  		Name:     name,
    14  		Relation: relations,
    15  	}
    16  }
    17  
    18  // WithComment creates a namespace definition with one or more defined relations.
    19  func WithComment(name string, comment string, relations ...*core.Relation) *core.NamespaceDefinition {
    20  	nd := Namespace(name, relations...)
    21  	nd.Metadata, _ = AddComment(nd.Metadata, comment)
    22  	return nd
    23  }
    24  
    25  // MustRelation creates a relation definition with an optional rewrite definition.
    26  func MustRelation(name string, rewrite *core.UsersetRewrite, allowedDirectRelations ...*core.AllowedRelation) *core.Relation {
    27  	r, err := Relation(name, rewrite, allowedDirectRelations...)
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  	return r
    32  }
    33  
    34  // Relation creates a relation definition with an optional rewrite definition.
    35  func Relation(name string, rewrite *core.UsersetRewrite, allowedDirectRelations ...*core.AllowedRelation) (*core.Relation, error) {
    36  	var typeInfo *core.TypeInformation
    37  	if len(allowedDirectRelations) > 0 {
    38  		typeInfo = &core.TypeInformation{
    39  			AllowedDirectRelations: allowedDirectRelations,
    40  		}
    41  	}
    42  
    43  	rel := &core.Relation{
    44  		Name:            name,
    45  		UsersetRewrite:  rewrite,
    46  		TypeInformation: typeInfo,
    47  	}
    48  
    49  	switch {
    50  	case rewrite != nil && len(allowedDirectRelations) == 0:
    51  		if err := SetRelationKind(rel, iv1.RelationMetadata_PERMISSION); err != nil {
    52  			return nil, spiceerrors.MustBugf("failed to set relation kind: %s", err.Error())
    53  		}
    54  
    55  	case rewrite == nil && len(allowedDirectRelations) > 0:
    56  		if err := SetRelationKind(rel, iv1.RelationMetadata_RELATION); err != nil {
    57  			return nil, spiceerrors.MustBugf("failed to set relation kind: %s", err.Error())
    58  		}
    59  
    60  	default:
    61  		// By default we do not set a relation kind on the relation. Relations without any
    62  		// information, or relations with both rewrites and types are legacy relations from
    63  		// before the DSL schema and, as such, do not have a defined "kind".
    64  	}
    65  
    66  	return rel, nil
    67  }
    68  
    69  // MustRelationWithComment creates a relation definition with an optional rewrite definition.
    70  func MustRelationWithComment(name string, comment string, rewrite *core.UsersetRewrite, allowedDirectRelations ...*core.AllowedRelation) *core.Relation {
    71  	rel := MustRelation(name, rewrite, allowedDirectRelations...)
    72  	rel.Metadata, _ = AddComment(rel.Metadata, comment)
    73  	return rel
    74  }
    75  
    76  // AllowedRelation creates a relation reference to an allowed relation.
    77  func AllowedRelation(namespaceName string, relationName string) *core.AllowedRelation {
    78  	return &core.AllowedRelation{
    79  		Namespace: namespaceName,
    80  		RelationOrWildcard: &core.AllowedRelation_Relation{
    81  			Relation: relationName,
    82  		},
    83  	}
    84  }
    85  
    86  // AllowedRelationWithCaveat creates a relation reference to an allowed relation.
    87  func AllowedRelationWithCaveat(namespaceName string, relationName string, withCaveat *core.AllowedCaveat) *core.AllowedRelation {
    88  	return &core.AllowedRelation{
    89  		Namespace: namespaceName,
    90  		RelationOrWildcard: &core.AllowedRelation_Relation{
    91  			Relation: relationName,
    92  		},
    93  		RequiredCaveat: withCaveat,
    94  	}
    95  }
    96  
    97  // AllowedPublicNamespace creates a relation reference to an allowed public namespace.
    98  func AllowedPublicNamespace(namespaceName string) *core.AllowedRelation {
    99  	return &core.AllowedRelation{
   100  		Namespace: namespaceName,
   101  		RelationOrWildcard: &core.AllowedRelation_PublicWildcard_{
   102  			PublicWildcard: &core.AllowedRelation_PublicWildcard{},
   103  		},
   104  	}
   105  }
   106  
   107  // AllowedCaveat creates a caveat reference.
   108  func AllowedCaveat(name string) *core.AllowedCaveat {
   109  	return &core.AllowedCaveat{
   110  		CaveatName: name,
   111  	}
   112  }
   113  
   114  // CaveatDefinition returns a new caveat definition.
   115  func CaveatDefinition(env *caveats.Environment, name string, expr string) (*core.CaveatDefinition, error) {
   116  	compiled, err := caveats.CompileCaveatWithName(env, expr, name)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return CompiledCaveatDefinition(env, name, compiled)
   121  }
   122  
   123  // CompiledCaveatDefinition returns a new caveat definition.
   124  func CompiledCaveatDefinition(env *caveats.Environment, name string, compiled *caveats.CompiledCaveat) (*core.CaveatDefinition, error) {
   125  	if compiled == nil {
   126  		return nil, spiceerrors.MustBugf("compiled caveat is nil")
   127  	}
   128  
   129  	serialized, err := compiled.Serialize()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return &core.CaveatDefinition{
   134  		Name:                 name,
   135  		SerializedExpression: serialized,
   136  		ParameterTypes:       env.EncodedParametersTypes(),
   137  	}, nil
   138  }
   139  
   140  // MustCaveatDefinition returns a new caveat definition.
   141  func MustCaveatDefinition(env *caveats.Environment, name string, expr string) *core.CaveatDefinition {
   142  	cd, err := CaveatDefinition(env, name, expr)
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  	return cd
   147  }
   148  
   149  // MustCaveatDefinitionWithComment returns a new caveat definition.
   150  func MustCaveatDefinitionWithComment(env *caveats.Environment, name string, comment string, expr string) *core.CaveatDefinition {
   151  	cd, err := CaveatDefinition(env, name, expr)
   152  	if err != nil {
   153  		panic(err)
   154  	}
   155  	cd.Metadata, _ = AddComment(cd.Metadata, comment)
   156  	return cd
   157  }
   158  
   159  // AllowedPublicNamespaceWithCaveat creates a relation reference to an allowed public namespace.
   160  func AllowedPublicNamespaceWithCaveat(namespaceName string, withCaveat *core.AllowedCaveat) *core.AllowedRelation {
   161  	return &core.AllowedRelation{
   162  		Namespace: namespaceName,
   163  		RelationOrWildcard: &core.AllowedRelation_PublicWildcard_{
   164  			PublicWildcard: &core.AllowedRelation_PublicWildcard{},
   165  		},
   166  		RequiredCaveat: withCaveat,
   167  	}
   168  }
   169  
   170  // RelationReference creates a relation reference.
   171  func RelationReference(namespaceName string, relationName string) *core.RelationReference {
   172  	return &core.RelationReference{
   173  		Namespace: namespaceName,
   174  		Relation:  relationName,
   175  	}
   176  }
   177  
   178  // Union creates a rewrite definition that combines/considers usersets in all children.
   179  func Union(firstChild *core.SetOperation_Child, rest ...*core.SetOperation_Child) *core.UsersetRewrite {
   180  	return &core.UsersetRewrite{
   181  		RewriteOperation: &core.UsersetRewrite_Union{
   182  			Union: setOperation(firstChild, rest),
   183  		},
   184  	}
   185  }
   186  
   187  // Intersection creates a rewrite definition that returns/considers only usersets present in all children.
   188  func Intersection(firstChild *core.SetOperation_Child, rest ...*core.SetOperation_Child) *core.UsersetRewrite {
   189  	return &core.UsersetRewrite{
   190  		RewriteOperation: &core.UsersetRewrite_Intersection{
   191  			Intersection: setOperation(firstChild, rest),
   192  		},
   193  	}
   194  }
   195  
   196  // Exclusion creates a rewrite definition that starts with the usersets of the first child
   197  // and iteratively removes usersets that appear in the remaining children.
   198  func Exclusion(firstChild *core.SetOperation_Child, rest ...*core.SetOperation_Child) *core.UsersetRewrite {
   199  	return &core.UsersetRewrite{
   200  		RewriteOperation: &core.UsersetRewrite_Exclusion{
   201  			Exclusion: setOperation(firstChild, rest),
   202  		},
   203  	}
   204  }
   205  
   206  func setOperation(firstChild *core.SetOperation_Child, rest []*core.SetOperation_Child) *core.SetOperation {
   207  	children := append([]*core.SetOperation_Child{firstChild}, rest...)
   208  	return &core.SetOperation{
   209  		Child: children,
   210  	}
   211  }
   212  
   213  // Nil creates a child for a set operation that references the empty set.
   214  func Nil() *core.SetOperation_Child {
   215  	return &core.SetOperation_Child{
   216  		ChildType: &core.SetOperation_Child_XNil{},
   217  	}
   218  }
   219  
   220  // ComputesUserset creates a child for a set operation that follows a relation on the given starting object.
   221  func ComputedUserset(relation string) *core.SetOperation_Child {
   222  	return &core.SetOperation_Child{
   223  		ChildType: &core.SetOperation_Child_ComputedUserset{
   224  			ComputedUserset: &core.ComputedUserset{
   225  				Relation: relation,
   226  			},
   227  		},
   228  	}
   229  }
   230  
   231  // MustComputesUsersetWithSourcePosition creates a child for a set operation that follows a relation on the given starting object.
   232  func MustComputesUsersetWithSourcePosition(relation string, lineNumber uint64) *core.SetOperation_Child {
   233  	cu := &core.ComputedUserset{
   234  		Relation: relation,
   235  	}
   236  	cu.SourcePosition = &core.SourcePosition{
   237  		ZeroIndexedLineNumber:     lineNumber,
   238  		ZeroIndexedColumnPosition: 0,
   239  	}
   240  
   241  	return &core.SetOperation_Child{
   242  		ChildType: &core.SetOperation_Child_ComputedUserset{
   243  			ComputedUserset: cu,
   244  		},
   245  	}
   246  }
   247  
   248  // TupleToUserset creates a child which first loads all tuples with the specific relation,
   249  // and then unions all children on the usersets found by following a relation on those loaded
   250  // tuples.
   251  func TupleToUserset(tuplesetRelation, usersetRelation string) *core.SetOperation_Child {
   252  	return &core.SetOperation_Child{
   253  		ChildType: &core.SetOperation_Child_TupleToUserset{
   254  			TupleToUserset: &core.TupleToUserset{
   255  				Tupleset: &core.TupleToUserset_Tupleset{
   256  					Relation: tuplesetRelation,
   257  				},
   258  				ComputedUserset: &core.ComputedUserset{
   259  					Relation: usersetRelation,
   260  					Object:   core.ComputedUserset_TUPLE_USERSET_OBJECT,
   261  				},
   262  			},
   263  		},
   264  	}
   265  }
   266  
   267  // Rewrite wraps a rewrite as a set operation child of another rewrite.
   268  func Rewrite(rewrite *core.UsersetRewrite) *core.SetOperation_Child {
   269  	return &core.SetOperation_Child{
   270  		ChildType: &core.SetOperation_Child_UsersetRewrite{
   271  			UsersetRewrite: rewrite,
   272  		},
   273  	}
   274  }