github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/caveats/types/ipaddress.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "net/netip" 6 "reflect" 7 8 "github.com/authzed/cel-go/cel" 9 "github.com/authzed/cel-go/common/types" 10 "github.com/authzed/cel-go/common/types/ref" 11 ) 12 13 // ParseIPAddress parses the string form of an IP Address into an IPAddress object type. 14 func ParseIPAddress(ip string) (IPAddress, error) { 15 parsed, err := netip.ParseAddr(ip) 16 return IPAddress{parsed}, err 17 } 18 19 // MustParseIPAddress parses the string form of an IP Address into an IPAddress object type. 20 func MustParseIPAddress(ip string) IPAddress { 21 ipAddress, err := ParseIPAddress(ip) 22 if err != nil { 23 panic(err) 24 } 25 return ipAddress 26 } 27 28 var ipaddressCelType = cel.OpaqueType("IPAddress") 29 30 // IPAddress defines a custom type for representing an IP Address in caveats. 31 type IPAddress struct { 32 ip netip.Addr 33 } 34 35 func (ipa IPAddress) SerializedString() string { 36 return ipa.ip.String() 37 } 38 39 func (ipa IPAddress) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { 40 switch typeDesc { 41 case reflect.TypeOf(""): 42 return ipa.ip.String(), nil 43 } 44 return nil, fmt.Errorf("type conversion error from 'IPAddress' to '%v'", typeDesc) 45 } 46 47 func (ipa IPAddress) ConvertToType(typeVal ref.Type) ref.Val { 48 switch typeVal { 49 case types.StringType: 50 return types.String(ipa.ip.String()) 51 case types.TypeType: 52 return ipaddressCelType 53 } 54 return types.NewErr("type conversion error from '%s' to '%s'", ipaddressCelType, typeVal) 55 } 56 57 func (ipa IPAddress) Equal(other ref.Val) ref.Val { 58 o2, ok := other.(IPAddress) 59 if !ok { 60 return types.ValOrErr(other, "no such overload") 61 } 62 return types.Bool(ipa == o2) 63 } 64 65 func (ipa IPAddress) Type() ref.Type { 66 return ipaddressCelType 67 } 68 69 func (ipa IPAddress) Value() interface{} { 70 return ipa 71 } 72 73 var IPAddressType = registerCustomType[IPAddress]( 74 "ipaddress", 75 cel.ObjectType("IPAddress"), 76 func(value any) (any, error) { 77 ipvalue, ok := value.(IPAddress) 78 if ok { 79 return ipvalue, nil 80 } 81 82 vle, ok := value.(string) 83 if !ok { 84 return nil, fmt.Errorf("ipaddress requires an ipaddress string, found: %T `%v`", value, value) 85 } 86 87 d, err := ParseIPAddress(vle) 88 if err != nil { 89 return nil, fmt.Errorf("could not parse ip address string `%s`: %w", vle, err) 90 } 91 92 return d, nil 93 }, 94 cel.Function("in_cidr", 95 cel.MemberOverload("ipaddress_in_cidr_string", 96 []*cel.Type{cel.ObjectType("IPAddress"), cel.StringType}, 97 cel.BoolType, 98 cel.BinaryBinding(func(lhs, rhs ref.Val) ref.Val { 99 cidr, ok := rhs.Value().(string) 100 if !ok { 101 return types.NewErr("expected CIDR string") 102 } 103 104 network, err := netip.ParsePrefix(cidr) 105 if err != nil { 106 return types.NewErr("invalid CIDR string: `%s`", cidr) 107 } 108 109 return types.Bool(network.Contains(lhs.(IPAddress).ip)) 110 }), 111 ), 112 ))