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  	))