github.com/openfga/openfga@v1.5.4-rc1/internal/condition/types/ipaddress.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "net/netip" 6 "reflect" 7 8 "github.com/google/cel-go/cel" 9 "github.com/google/cel-go/common/types" 10 "github.com/google/cel-go/common/types/ref" 11 "github.com/google/cel-go/common/types/traits" 12 openfgav1 "github.com/openfga/api/proto/openfga/v1" 13 ) 14 15 var ipaddrLibraryDecls = map[string][]cel.FunctionOpt{ 16 "ipaddress": { 17 cel.Overload("string_to_ipaddress", []*cel.Type{cel.StringType}, ipaddrCelType, 18 cel.UnaryBinding(stringToIPAddress))}, 19 } 20 21 var ipaddrLib = &IPAddress{} 22 23 func IPAddressEnvOption() cel.EnvOption { 24 return cel.Lib(ipaddrLib) 25 } 26 27 func (ip *IPAddress) CompileOptions() []cel.EnvOption { 28 options := []cel.EnvOption{} 29 for name, overloads := range ipaddrLibraryDecls { 30 options = append(options, cel.Function(name, overloads...)) 31 } 32 return options 33 } 34 35 func (ip *IPAddress) ProgramOptions() []cel.ProgramOption { 36 return []cel.ProgramOption{} 37 } 38 39 // IPAddressType defines a ParameterType that is used to represent IP addresses in CEL expressions. 40 var IPAddressType = registerCustomParamType( 41 openfgav1.ConditionParamTypeRef_TYPE_NAME_IPADDRESS, 42 cel.ObjectType("IPAddress"), 43 ipaddressTypeConverterFunc, 44 cel.Function("in_cidr", 45 cel.MemberOverload("ipaddr_in_cidr", 46 []*cel.Type{cel.ObjectType("IPAddress"), cel.StringType}, 47 cel.BoolType, 48 cel.BinaryBinding(ipaddressCELBinaryBinding), 49 ), 50 ), 51 ) 52 53 // IPAddress represents a network IP address. 54 type IPAddress struct { 55 addr netip.Addr 56 } 57 58 // ParseIPAddress attempts to parse the provided ip string. If the provided string does 59 // not define a well-formed IP address, then an error is returned. 60 func ParseIPAddress(ip string) (IPAddress, error) { 61 addr, err := netip.ParseAddr(ip) 62 if err != nil { 63 return IPAddress{}, err 64 } 65 66 return IPAddress{addr}, nil 67 } 68 69 // ipaddrCelType defines a CEL type for the IPAddress and registers it as a receiver type. 70 var ipaddrCelType = cel.ObjectType("IPAddress", traits.ReceiverType) 71 72 // ConvertToNative implements the CEL ref.Val.ConvertToNative. 73 // 74 // See https://pkg.go.dev/github.com/google/cel-go/common/types/ref#Val 75 func (ip IPAddress) ConvertToNative(typeDesc reflect.Type) (any, error) { 76 if reflect.TypeOf(ip).AssignableTo(typeDesc) { 77 return ip, nil 78 } 79 80 switch typeDesc { 81 case reflect.TypeOf(""): 82 return ip.addr.String(), nil 83 default: 84 return nil, fmt.Errorf("failed to convert from type '%s' to native Go type 'IPAddress'", typeDesc) 85 } 86 } 87 88 // ConvertToType implements the CEL ref.Val.ConvertToType. 89 // 90 // See https://pkg.go.dev/github.com/google/cel-go/common/types/ref#Val 91 func (ip IPAddress) ConvertToType(typeValue ref.Type) ref.Val { 92 switch typeValue { 93 case types.StringType: 94 return types.String(ip.addr.String()) 95 case types.TypeType: 96 return ipaddrCelType 97 default: 98 return types.NewErr("failed to convert from CEL type '%s' to '%s'", ipaddrCelType, typeValue) 99 } 100 } 101 102 // Equal implements the CEL ref.Val.Equal. 103 // 104 // See https://pkg.go.dev/github.com/google/cel-go/common/types/ref#Val 105 func (ip IPAddress) Equal(other ref.Val) ref.Val { 106 otherip, ok := other.(IPAddress) 107 if !ok { 108 return types.NoSuchOverloadErr() 109 } 110 111 return types.Bool(ip.addr.Compare(otherip.addr) == 0) 112 } 113 114 // Type implements the CEL ref.Val.Type. 115 // 116 // See https://pkg.go.dev/github.com/google/cel-go/common/types/ref#Val 117 func (ip IPAddress) Type() ref.Type { 118 return ipaddrCelType 119 } 120 121 // Value implements ref.Val.Value. 122 // 123 // See https://pkg.go.dev/github.com/google/cel-go/common/types/ref#Val 124 func (ip IPAddress) Value() any { 125 return ip 126 } 127 128 // ipaddressBinaryBinding implements a cel.BinaryBinding that is used as a receiver overload for 129 // comparing an ipaddress value against a network CIDR defined as a string. If the ipaddress is 130 // within the CIDR range this binding will return true, otherwise it will return false or an error. 131 // 132 // See https://pkg.go.dev/github.com/google/cel-go/cel#BinaryBinding 133 func ipaddressCELBinaryBinding(lhs, rhs ref.Val) ref.Val { 134 cidr, ok := rhs.Value().(string) 135 if !ok { 136 return types.NewErr("a CIDR string is required for comparison") 137 } 138 139 network, err := netip.ParsePrefix(cidr) 140 if err != nil { 141 return types.NewErr("'%s' is a malformed CIDR string", cidr) 142 } 143 144 ipaddr, ok := lhs.(IPAddress) 145 if !ok { 146 return types.NewErr("an IPAddress parameter value is required for comparison") 147 } 148 149 return types.Bool(network.Contains(ipaddr.addr)) 150 } 151 152 func stringToIPAddress(arg ref.Val) ref.Val { 153 ipStr, ok := arg.Value().(string) 154 if !ok { 155 return types.MaybeNoSuchOverloadErr(arg) 156 } 157 158 ipaddr, err := ParseIPAddress(ipStr) 159 if err != nil { 160 return types.NewErr(err.Error()) 161 } 162 163 return ipaddr 164 }