From bc8cb5beca3410b50bdbc0ae4cf3f14354738c0e Mon Sep 17 00:00:00 2001 From: Anya Krupp Date: Wed, 4 Oct 2023 04:29:05 -0700 Subject: [PATCH 1/3] Add method to check if IP Address is inside Block range Signed-off-by: Anya Krupp --- internal/ent/schema/validator/validate.go | 30 ++++++++++ .../ent/schema/validator/validate_test.go | 58 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 internal/ent/schema/validator/validate_test.go diff --git a/internal/ent/schema/validator/validate.go b/internal/ent/schema/validator/validate.go index 45aa4d97..564b18ac 100644 --- a/internal/ent/schema/validator/validate.go +++ b/internal/ent/schema/validator/validate.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "net" + + "github.com/3th1nk/cidr" ) // IPAddr returns error if IP address is NOT valid @@ -15,6 +17,26 @@ func IPAddr(ip string) error { return InvalidIPAddrError(ip) } +// PartOfBlock returns error if IP address is NOT part of the block given block's prefix +func PartOfBlock(ipBlockPref string, ipAdrr string) error { + c, _ := cidr.Parse(ipBlockPref) + belongsToBlock := false + + c.Each(func(ip string) bool { + if ip == ipAdrr { + belongsToBlock = true + } + + return true + }) + + if belongsToBlock { + return nil + } + + return IPAddrOutsideBlockError(ipBlockPref, ipAdrr) +} + // ErrInvalidIPAddr is an error raised when provided IP Address is invalid var ErrInvalidIPAddr = errors.New("provided IP Address is invalid") @@ -22,3 +44,11 @@ var ErrInvalidIPAddr = errors.New("provided IP Address is invalid") func InvalidIPAddrError(ip string) error { return fmt.Errorf("error %w: %s", ErrInvalidIPAddr, ip) } + +// ErrIPAddrOutsideBlock is an error raised when provided IP Address is not part of the IP Block +var ErrIPAddrOutsideBlock = errors.New("provided IP Address is not part of the IP Block - Prefix") + +// IPAddrOutsideBlockError returns Error IP Address doesn't belong to the IP Block +func IPAddrOutsideBlockError(block string, ip string) error { + return fmt.Errorf("error %w: %s; IP Address: %s", ErrIPAddrOutsideBlock, block, ip) +} diff --git a/internal/ent/schema/validator/validate_test.go b/internal/ent/schema/validator/validate_test.go new file mode 100644 index 00000000..e01dbead --- /dev/null +++ b/internal/ent/schema/validator/validate_test.go @@ -0,0 +1,58 @@ +package validator + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPartOfBlock(t *testing.T) { + type args struct { + ipBl string + ipAdrr string + } + + tests := []struct { + name string + args args + wantErr bool + errMss string + }{ + { + + name: "happy path", + args: args{ + ipBl: "192.168.1.0/28", + ipAdrr: "192.168.1.13"}, + wantErr: false, + }, + { + name: "outside block", + args: args{ + ipBl: "192.168.1.0/28", + ipAdrr: "192.168.1.25"}, + wantErr: true, + errMss: "error provided IP Address is not part of the IP Block - Prefix: 192.168.1.0/28; IP Address: 192.168.1.25", + }, + { + name: "far from block", + args: args{ + ipBl: "108.1.80.128/30", + ipAdrr: "192.168.10.12"}, + wantErr: true, + errMss: "error provided IP Address is not part of the IP Block - Prefix: 108.1.80.128/30; IP Address: 192.168.10.12", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := PartOfBlock(tt.args.ipBl, tt.args.ipAdrr) + if tt.wantErr { + assert.Error(t, err) + t.Logf("error: %+v", err) + } else { + assert.Nil(t, err) + } + }) + } +} From 959e7c34118d3b5ed7976b30b111f3bb78925111 Mon Sep 17 00:00:00 2001 From: Anya Krupp Date: Mon, 23 Oct 2023 13:17:27 -0700 Subject: [PATCH 2/3] Update error messages to validation of ips --- internal/ent/schema/validator/errors.go | 30 +++++++++++++++++++ internal/ent/schema/validator/validate.go | 26 ---------------- .../ent/schema/validator/validate_test.go | 5 +--- 3 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 internal/ent/schema/validator/errors.go diff --git a/internal/ent/schema/validator/errors.go b/internal/ent/schema/validator/errors.go new file mode 100644 index 00000000..5f27c2fb --- /dev/null +++ b/internal/ent/schema/validator/errors.go @@ -0,0 +1,30 @@ +package validator + +import ( + "errors" + "fmt" +) + +// ErrInvalidIPAddr is an error raised when provided IP Address is invalid +var ErrInvalidIPAddr = errors.New("ip Address is invalid") + +// InvalidIPAddrError returns Error Invalid IP Address +func InvalidIPAddrError(ip string) error { + return fmt.Errorf("%w: %s", ErrInvalidIPAddr, ip) +} + +// ErrInvalidIPPref is an error raised when provided IP Block Prefix is invalid +var ErrInvalidIPPref = errors.New("ip block prefix is invalid") + +// InvalidIPPrefError returns Error Invalid IP Block Prefix +func InvalidIPPrefError(prefix string) error { + return fmt.Errorf("%w: %s", ErrInvalidIPPref, prefix) +} + +// ErrIPAddrOutsideBlock is an error raised when provided IP Address is not part of the IP Block +var ErrIPAddrOutsideBlock = errors.New("ip address is invalid for IP Block Prefix") + +// IPAddrOutsideBlockError returns Error IP Address doesn't belong to the IP Block +func IPAddrOutsideBlockError(block string, ip string) error { + return fmt.Errorf("%w: ip address: %s, ip block %s", ErrIPAddrOutsideBlock, ip, block) +} diff --git a/internal/ent/schema/validator/validate.go b/internal/ent/schema/validator/validate.go index 357e2c00..562f6917 100644 --- a/internal/ent/schema/validator/validate.go +++ b/internal/ent/schema/validator/validate.go @@ -1,8 +1,6 @@ package validator import ( - "errors" - "fmt" "net" "net/netip" @@ -48,27 +46,3 @@ func PartOfBlock(ipBlockPref string, ipAdrr string) error { return IPAddrOutsideBlockError(ipBlockPref, ipAdrr) } - -// ErrInvalidIPAddr is an error raised when provided IP Address is invalid -var ErrInvalidIPAddr = errors.New("provided IP Address is invalid") - -// InvalidIPAddrError returns Error Invalid IP Address -func InvalidIPAddrError(ip string) error { - return fmt.Errorf("error %w: %s", ErrInvalidIPAddr, ip) -} - -// ErrInvalidIPPref is an error raised when provided IP Block Prefix is invalid -var ErrInvalidIPPref = errors.New("provided IP Block Prefix is invalid") - -// InvalidIPPrefError returns Error Invalid IP Block Prefix -func InvalidIPPrefError(prefix string) error { - return fmt.Errorf("error %w: %s", ErrInvalidIPPref, prefix) -} - -// ErrIPAddrOutsideBlock is an error raised when provided IP Address is not part of the IP Block -var ErrIPAddrOutsideBlock = errors.New("provided IP Address is not part of the IP Block - Prefix") - -// IPAddrOutsideBlockError returns Error IP Address doesn't belong to the IP Block -func IPAddrOutsideBlockError(block string, ip string) error { - return fmt.Errorf("error %w: %s; IP Address: %s", ErrIPAddrOutsideBlock, block, ip) -} diff --git a/internal/ent/schema/validator/validate_test.go b/internal/ent/schema/validator/validate_test.go index e01dbead..0f421955 100644 --- a/internal/ent/schema/validator/validate_test.go +++ b/internal/ent/schema/validator/validate_test.go @@ -16,7 +16,6 @@ func TestPartOfBlock(t *testing.T) { name string args args wantErr bool - errMss string }{ { @@ -32,7 +31,6 @@ func TestPartOfBlock(t *testing.T) { ipBl: "192.168.1.0/28", ipAdrr: "192.168.1.25"}, wantErr: true, - errMss: "error provided IP Address is not part of the IP Block - Prefix: 192.168.1.0/28; IP Address: 192.168.1.25", }, { name: "far from block", @@ -40,7 +38,6 @@ func TestPartOfBlock(t *testing.T) { ipBl: "108.1.80.128/30", ipAdrr: "192.168.10.12"}, wantErr: true, - errMss: "error provided IP Address is not part of the IP Block - Prefix: 108.1.80.128/30; IP Address: 192.168.10.12", }, } @@ -49,7 +46,7 @@ func TestPartOfBlock(t *testing.T) { err := PartOfBlock(tt.args.ipBl, tt.args.ipAdrr) if tt.wantErr { assert.Error(t, err) - t.Logf("error: %+v", err) + assert.ErrorContainsf(t, err, tt.args.ipAdrr, tt.args.ipBl) } else { assert.Nil(t, err) } From 26dfce6be4377fbbee5e898bee3de461af6355d9 Mon Sep 17 00:00:00 2001 From: Anya Krupp Date: Mon, 23 Oct 2023 14:09:24 -0700 Subject: [PATCH 3/3] Add resolver tests --- internal/graphapi/ipaddress.resolvers.go | 14 +++++ internal/graphapi/ipaddress_test.go | 66 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/internal/graphapi/ipaddress.resolvers.go b/internal/graphapi/ipaddress.resolvers.go index 85055c7a..4e4060e6 100644 --- a/internal/graphapi/ipaddress.resolvers.go +++ b/internal/graphapi/ipaddress.resolvers.go @@ -9,6 +9,7 @@ import ( "go.infratographer.com/ipam-api/internal/ent/generated" "go.infratographer.com/ipam-api/internal/ent/generated/ipaddress" + "go.infratographer.com/ipam-api/internal/ent/schema/validator" "go.infratographer.com/permissions-api/pkg/permissions" "go.infratographer.com/x/gidx" ) @@ -34,6 +35,10 @@ func (r *mutationResolver) CreateIPAddress(ctx context.Context, input generated. return nil, err } + // if err := validator.PartOfBlock(input.IP, input.NodeID.Prefix()); err != nil { + // return nil, err + // } + t, err := r.client.IPAddress.Create().SetInput(input).Save(ctx) if err != nil { return nil, err @@ -53,6 +58,15 @@ func (r *mutationResolver) UpdateIPAddress(ctx context.Context, id gidx.Prefixed return nil, err } + bl, err := r.client.IPBlock.Get(ctx, t.BlockID) + if err != nil { + return nil, err + } + + if err := validator.PartOfBlock(t.IP, bl.Prefix); err != nil { + return nil, err + } + t, err = t.Update().SetInput(input).Save(ctx) if err != nil { return nil, err diff --git a/internal/graphapi/ipaddress_test.go b/internal/graphapi/ipaddress_test.go index 81faded0..572b0f24 100644 --- a/internal/graphapi/ipaddress_test.go +++ b/internal/graphapi/ipaddress_test.go @@ -68,11 +68,11 @@ func Test_IPAddress_Lifecycle(t *testing.T) { ctx = context.WithValue(ctx, permissions.CheckerCtxKey, permissions.DefaultAllowChecker) ipbt := (&IPBlockTypeBuilder{}).MustNew(ctx) - ipb := (&IPBlockBuilder{IPBlockTypeID: ipbt.ID}).MustNew(ctx) + ipb := (&IPBlockBuilder{IPBlockTypeID: ipbt.ID, Prefix: "192.168.1.0/28"}).MustNew(ctx) t.Run("Create", func(t *testing.T) { ipa, err := client.CreateIPAddress(ctx, testclient.CreateIPAddressInput{ - IP: gofakeit.IPv4Address(), + IP: "192.168.1.13", NodeID: gidx.MustNewID(nodePrefix), NodeOwnerID: gidx.MustNewID(ownerPrefix), Reserved: newBool(true), @@ -163,3 +163,65 @@ func Test_IPAddressable(t *testing.T) { require.NoError(t, err) assert.Len(t, addrs.Entities[0].IPAddresses, 0) } + +func Test_IPAddress_PartOfBlock_Succeess(t *testing.T) { + client := graphTestClient() + ctx := context.Background() + + // Permit request + ctx = context.WithValue(ctx, permissions.CheckerCtxKey, permissions.DefaultAllowChecker) + + ipbt := (&IPBlockTypeBuilder{}).MustNew(ctx) + ipb := (&IPBlockBuilder{IPBlockTypeID: ipbt.ID, Prefix: "192.168.1.0/28"}).MustNew(ctx) + + t.Run("Create", func(t *testing.T) { + ipa, err := client.CreateIPAddress(ctx, testclient.CreateIPAddressInput{ + IP: "192.168.1.13", + NodeID: gidx.MustNewID(nodePrefix), + NodeOwnerID: gidx.MustNewID(ownerPrefix), + Reserved: newBool(true), + IPBlockID: ipb.ID, + }) + + require.NoError(t, err) + require.NotNil(t, ipa) + }) +} + +func Test_IPAddress_PartOfBlock_Failure(t *testing.T) { + client := graphTestClient() + ctx := context.Background() + + // Permit request + ctx = context.WithValue(ctx, permissions.CheckerCtxKey, permissions.DefaultAllowChecker) + + ipbt := (&IPBlockTypeBuilder{}).MustNew(ctx) + ipb := (&IPBlockBuilder{IPBlockTypeID: ipbt.ID, Prefix: "192.168.1.0/28"}).MustNew(ctx) + ipb2 := (&IPBlockBuilder{IPBlockTypeID: ipbt.ID, Prefix: "108.1.80.128/30"}).MustNew(ctx) + + t.Run("Create", func(t *testing.T) { + ipa, err := client.CreateIPAddress(ctx, testclient.CreateIPAddressInput{ + IP: "192.168.1.25", + NodeID: gidx.MustNewID(nodePrefix), + NodeOwnerID: gidx.MustNewID(ownerPrefix), + Reserved: newBool(true), + IPBlockID: ipb.ID, + }) + + require.Error(t, err) + require.Nil(t, ipa) + }) + + t.Run("Create", func(t *testing.T) { + ipa, err := client.CreateIPAddress(ctx, testclient.CreateIPAddressInput{ + IP: "192.168.10.12", + NodeID: gidx.MustNewID(nodePrefix), + NodeOwnerID: gidx.MustNewID(ownerPrefix), + Reserved: newBool(true), + IPBlockID: ipb2.ID, + }) + + require.Error(t, err) + require.Nil(t, ipa) + }) +}