github.com/openfga/openfga@v1.5.4-rc1/internal/condition/types/types.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/google/cel-go/cel"
     8  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
     9  )
    10  
    11  var CustomParamTypes = map[openfgav1.ConditionParamTypeRef_TypeName][]cel.EnvOption{}
    12  
    13  var paramTypeDefinitions = map[openfgav1.ConditionParamTypeRef_TypeName]paramTypeDefinition{}
    14  
    15  // typedParamValueConverter defines a signature that implementations can provide to enforce type enforcements
    16  // over any values provided.
    17  type typedParamValueConverter func(value any) (any, error)
    18  
    19  // paramTypeDefinition represents a parameter type definition included in a relationship condition.
    20  //
    21  // For example, the following condition defines two parameter type definitions (user and color), and
    22  // the 'user' parameter has a type that is a map<any> (a map with a generic type of any) and the 'color'
    23  // parameter has a type that is a string.
    24  //
    25  //	condition favorite_color(user map<any>, color string) {
    26  //	  user.favoriteColor == color
    27  //	}
    28  type paramTypeDefinition struct {
    29  	// name is the name/keyword for the type (e.g. 'string', 'timestamp', 'duration', 'map', 'list', 'ipaddress')
    30  	name openfgav1.ConditionParamTypeRef_TypeName
    31  
    32  	genericTypeCount uint
    33  
    34  	toParameterType func(genericType []ParameterType) (*ParameterType, error)
    35  }
    36  
    37  var paramTypeString = map[openfgav1.ConditionParamTypeRef_TypeName]string{
    38  	openfgav1.ConditionParamTypeRef_TYPE_NAME_ANY:       "any",
    39  	openfgav1.ConditionParamTypeRef_TYPE_NAME_BOOL:      "bool",
    40  	openfgav1.ConditionParamTypeRef_TYPE_NAME_STRING:    "string",
    41  	openfgav1.ConditionParamTypeRef_TYPE_NAME_INT:       "int",
    42  	openfgav1.ConditionParamTypeRef_TYPE_NAME_UINT:      "uint",
    43  	openfgav1.ConditionParamTypeRef_TYPE_NAME_DOUBLE:    "double",
    44  	openfgav1.ConditionParamTypeRef_TYPE_NAME_DURATION:  "duration",
    45  	openfgav1.ConditionParamTypeRef_TYPE_NAME_TIMESTAMP: "timestamp",
    46  	openfgav1.ConditionParamTypeRef_TYPE_NAME_MAP:       "map",
    47  	openfgav1.ConditionParamTypeRef_TYPE_NAME_LIST:      "list",
    48  	openfgav1.ConditionParamTypeRef_TYPE_NAME_IPADDRESS: "ipaddress",
    49  }
    50  
    51  func registerParamTypeWithGenerics(
    52  	paramTypeKeyword openfgav1.ConditionParamTypeRef_TypeName,
    53  	genericTypeCount uint,
    54  	toParameterType func(genericType []ParameterType) ParameterType,
    55  ) func(genericTypes ...ParameterType) (ParameterType, error) {
    56  	paramTypeDefinitions[paramTypeKeyword] = paramTypeDefinition{
    57  		name:             paramTypeKeyword,
    58  		genericTypeCount: genericTypeCount,
    59  		toParameterType: func(genericTypes []ParameterType) (*ParameterType, error) {
    60  			if uint(len(genericTypes)) != genericTypeCount {
    61  				return nil, fmt.Errorf("type `%s` requires %d generic types; found %d", paramTypeKeyword, genericTypeCount, len(genericTypes))
    62  			}
    63  
    64  			built := toParameterType(genericTypes)
    65  			return &built, nil
    66  		},
    67  	}
    68  
    69  	return func(genericTypes ...ParameterType) (ParameterType, error) {
    70  		if uint(len(genericTypes)) != genericTypeCount {
    71  			return ParameterType{}, fmt.Errorf("invalid number of parameters given to type constructor. expected: %d, found: %d", genericTypeCount, len(genericTypes))
    72  		}
    73  
    74  		return toParameterType(genericTypes), nil
    75  	}
    76  }
    77  
    78  func registerParamType(
    79  	paramTypeKeyword openfgav1.ConditionParamTypeRef_TypeName,
    80  	celType *cel.Type,
    81  	typedParamConverter typedParamValueConverter,
    82  ) ParameterType {
    83  	paramType := ParameterType{
    84  		name:                paramTypeKeyword,
    85  		celType:             celType,
    86  		genericTypes:        nil,
    87  		typedParamConverter: typedParamConverter,
    88  	}
    89  
    90  	paramTypeDefinitions[paramTypeKeyword] = paramTypeDefinition{
    91  		name:             paramTypeKeyword,
    92  		genericTypeCount: 0,
    93  		toParameterType: func(genericTypes []ParameterType) (*ParameterType, error) {
    94  			return &paramType, nil
    95  		},
    96  	}
    97  
    98  	return paramType
    99  }
   100  
   101  func registerCustomParamType(
   102  	paramTypeKeyword openfgav1.ConditionParamTypeRef_TypeName,
   103  	celType *cel.Type,
   104  	typeConverter typedParamValueConverter,
   105  	celOpts ...cel.EnvOption,
   106  ) ParameterType {
   107  	CustomParamTypes[paramTypeKeyword] = celOpts
   108  	return registerParamType(paramTypeKeyword, celType, typeConverter)
   109  }
   110  
   111  // ParameterType defines the canonical representation of parameter types supported in conditions.
   112  type ParameterType struct {
   113  	name                openfgav1.ConditionParamTypeRef_TypeName
   114  	celType             *cel.Type
   115  	genericTypes        []ParameterType
   116  	typedParamConverter typedParamValueConverter
   117  }
   118  
   119  func NewParameterType(
   120  	name openfgav1.ConditionParamTypeRef_TypeName,
   121  	celType *cel.Type,
   122  	generics []ParameterType,
   123  	typedParamConverter typedParamValueConverter,
   124  ) ParameterType {
   125  	return ParameterType{
   126  		name,
   127  		celType,
   128  		generics,
   129  		typedParamConverter,
   130  	}
   131  }
   132  
   133  // CelType returns the underlying Google CEL type for the variable type.
   134  func (pt ParameterType) CelType() *cel.Type {
   135  	return pt.celType
   136  }
   137  
   138  func (pt ParameterType) String() string {
   139  	if len(pt.genericTypes) > 0 {
   140  		genericTypeStrings := make([]string, 0, len(pt.genericTypes))
   141  
   142  		for _, genericType := range pt.genericTypes {
   143  			genericTypeStrings = append(genericTypeStrings, genericType.String())
   144  		}
   145  
   146  		// e.g. map<int>
   147  		return fmt.Sprintf("%s<%s>", pt.name, strings.Join(genericTypeStrings, ", "))
   148  	}
   149  
   150  	str, ok := paramTypeString[pt.name]
   151  	if !ok {
   152  		return "unknown"
   153  	}
   154  
   155  	return str
   156  }
   157  
   158  func (pt ParameterType) ConvertValue(value any) (any, error) {
   159  	converted, err := pt.typedParamConverter(value)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	return converted, nil
   165  }