mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-14 17:28:56 +02:00
[management] Parse resource addr before db write (#3061)
This commit is contained in:
@ -1272,18 +1272,17 @@ components:
|
|||||||
description: Network resource description
|
description: Network resource description
|
||||||
type: string
|
type: string
|
||||||
example: A remote resource inside network 1
|
example: A remote resource inside network 1
|
||||||
address:
|
|
||||||
description: Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
|
|
||||||
type: string
|
|
||||||
example: "1.1.1.1"
|
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
- address
|
|
||||||
NetworkResourceRequest:
|
NetworkResourceRequest:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
|
address:
|
||||||
|
description: Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
|
||||||
|
type: string
|
||||||
|
example: "1.1.1.1"
|
||||||
groups:
|
groups:
|
||||||
description: Group IDs containing the resource
|
description: Group IDs containing the resource
|
||||||
type: array
|
type: array
|
||||||
@ -1292,6 +1291,7 @@ components:
|
|||||||
example: "chacdk86lnnboviihd70"
|
example: "chacdk86lnnboviihd70"
|
||||||
required:
|
required:
|
||||||
- groups
|
- groups
|
||||||
|
- address
|
||||||
NetworkResource:
|
NetworkResource:
|
||||||
allOf:
|
allOf:
|
||||||
- type: object
|
- type: object
|
||||||
@ -1307,10 +1307,20 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GroupMinimum'
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
|
domain:
|
||||||
|
description: Domain name of the resource
|
||||||
|
type: string
|
||||||
|
example: example.com
|
||||||
|
prefix:
|
||||||
|
description: Prefix of the resource
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- type
|
- type
|
||||||
- groups
|
- groups
|
||||||
|
- domain
|
||||||
|
- prefix
|
||||||
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||||
NetworkResourceType:
|
NetworkResourceType:
|
||||||
description: Network resource type based of the address
|
description: Network resource type based of the address
|
||||||
|
@ -551,12 +551,12 @@ type NetworkRequest struct {
|
|||||||
|
|
||||||
// NetworkResource defines model for NetworkResource.
|
// NetworkResource defines model for NetworkResource.
|
||||||
type NetworkResource struct {
|
type NetworkResource struct {
|
||||||
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
|
|
||||||
Address string `json:"address"`
|
|
||||||
|
|
||||||
// Description Network resource description
|
// Description Network resource description
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Domain Domain name of the resource
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
|
||||||
// Groups Groups that the resource belongs to
|
// Groups Groups that the resource belongs to
|
||||||
Groups []GroupMinimum `json:"groups"`
|
Groups []GroupMinimum `json:"groups"`
|
||||||
|
|
||||||
@ -566,15 +566,15 @@ type NetworkResource struct {
|
|||||||
// Name Network resource name
|
// Name Network resource name
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Prefix Prefix of the resource
|
||||||
|
Prefix string `json:"prefix"`
|
||||||
|
|
||||||
// Type Network resource type based of the address
|
// Type Network resource type based of the address
|
||||||
Type NetworkResourceType `json:"type"`
|
Type NetworkResourceType `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkResourceMinimum defines model for NetworkResourceMinimum.
|
// NetworkResourceMinimum defines model for NetworkResourceMinimum.
|
||||||
type NetworkResourceMinimum struct {
|
type NetworkResourceMinimum struct {
|
||||||
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
|
|
||||||
Address string `json:"address"`
|
|
||||||
|
|
||||||
// Description Network resource description
|
// Description Network resource description
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
@ -142,13 +142,14 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc
|
|||||||
return nil, status.NewPermissionDeniedError()
|
return nil, status.NewPermissionDeniedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceType, addr, err := types.GetResourceType(resource.Address)
|
resourceType, domain, prefix, err := types.GetResourceType(resource.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get resource type: %w", err)
|
return nil, fmt.Errorf("failed to get resource type: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resource.Type = resourceType
|
resource.Type = resourceType
|
||||||
resource.Address = addr
|
resource.Domain = domain
|
||||||
|
resource.Prefix = prefix
|
||||||
|
|
||||||
_, err = m.store.GetNetworkResourceByID(ctx, store.LockingStrengthShare, resource.AccountID, resource.ID)
|
_, err = m.store.GetNetworkResourceByID(ctx, store.LockingStrengthShare, resource.AccountID, resource.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,9 +3,8 @@ package types
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
|
|
||||||
@ -31,11 +30,13 @@ type NetworkResource struct {
|
|||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
Type NetworkResourceType
|
Type NetworkResourceType
|
||||||
Address string
|
Address string `gorm:"-"`
|
||||||
|
Domain string
|
||||||
|
Prefix netip.Prefix `gorm:"serializer:json"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetworkResource(accountID, networkID, name, description, address string) (*NetworkResource, error) {
|
func NewNetworkResource(accountID, networkID, name, description, address string) (*NetworkResource, error) {
|
||||||
resourceType, address, err := GetResourceType(address)
|
resourceType, domain, prefix, err := GetResourceType(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid address: %w", err)
|
return nil, fmt.Errorf("invalid address: %w", err)
|
||||||
}
|
}
|
||||||
@ -48,6 +49,8 @@ func NewNetworkResource(accountID, networkID, name, description, address string)
|
|||||||
Description: description,
|
Description: description,
|
||||||
Type: resourceType,
|
Type: resourceType,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
Domain: domain,
|
||||||
|
Prefix: prefix,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +60,8 @@ func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkR
|
|||||||
Name: n.Name,
|
Name: n.Name,
|
||||||
Description: &n.Description,
|
Description: &n.Description,
|
||||||
Type: api.NetworkResourceType(n.Type.String()),
|
Type: api.NetworkResourceType(n.Type.String()),
|
||||||
Address: n.Address,
|
Domain: n.Domain,
|
||||||
|
Prefix: n.Prefix.String(),
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,26 +88,22 @@ func (n *NetworkResource) Copy() *NetworkResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetResourceType returns the type of the resource based on the address
|
// GetResourceType returns the type of the resource based on the address
|
||||||
func GetResourceType(address string) (NetworkResourceType, string, error) {
|
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
|
||||||
if ip, cidr, err := net.ParseCIDR(address); err == nil {
|
if prefix, err := netip.ParsePrefix(address); err == nil {
|
||||||
ones, _ := cidr.Mask.Size()
|
if prefix.Bits() == 32 || prefix.Bits() == 128 {
|
||||||
if strings.HasSuffix(address, "/32") {
|
return host, "", prefix, nil
|
||||||
return host, address, nil
|
|
||||||
}
|
}
|
||||||
if ip != nil && ones == 32 {
|
return subnet, "", prefix, nil
|
||||||
return host, address + "/32", nil
|
|
||||||
}
|
|
||||||
return subnet, address, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if net.ParseIP(address) != nil {
|
if ip, err := netip.ParseAddr(address); err == nil {
|
||||||
return host, address + "/32", nil
|
return host, "", netip.PrefixFrom(ip, ip.BitLen()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
|
domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
|
||||||
if domainRegex.MatchString(address) {
|
if domainRegex.MatchString(address) {
|
||||||
return domain, address, nil
|
return domain, address, netip.Prefix{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", "", errors.New("not a host, subnet, or domain")
|
return "", "", netip.Prefix{}, errors.New("not a valid host, subnet, or domain")
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,38 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetResourceType(t *testing.T) {
|
func TestGetResourceType(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expectedType NetworkResourceType
|
expectedType NetworkResourceType
|
||||||
expectedErr bool
|
expectedErr bool
|
||||||
expectedAddr string
|
expectedDomain string
|
||||||
|
expectedPrefix netip.Prefix
|
||||||
}{
|
}{
|
||||||
// Valid host IPs
|
// Valid host IPs
|
||||||
{"1.1.1.1", host, false, "1.1.1.1/32"},
|
{"1.1.1.1", host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
|
||||||
{"1.1.1.1/32", host, false, "1.1.1.1/32"},
|
{"1.1.1.1/32", host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
|
||||||
// Valid subnets
|
// Valid subnets
|
||||||
{"192.168.1.0/24", subnet, false, "192.168.1.0/24"},
|
{"192.168.1.0/24", subnet, false, "", netip.MustParsePrefix("192.168.1.0/24")},
|
||||||
{"10.0.0.0/16", subnet, false, "10.0.0.0/16"},
|
{"10.0.0.0/16", subnet, false, "", netip.MustParsePrefix("10.0.0.0/16")},
|
||||||
// Valid domains
|
// Valid domains
|
||||||
{"example.com", domain, false, "example.com"},
|
{"example.com", domain, false, "example.com", netip.Prefix{}},
|
||||||
{"*.example.com", domain, false, "*.example.com"},
|
{"*.example.com", domain, false, "*.example.com", netip.Prefix{}},
|
||||||
{"sub.example.com", domain, false, "sub.example.com"},
|
{"sub.example.com", domain, false, "sub.example.com", netip.Prefix{}},
|
||||||
// Invalid inputs
|
// Invalid inputs
|
||||||
{"invalid", "", true, ""},
|
{"invalid", "", true, "", netip.Prefix{}},
|
||||||
{"1.1.1.1/abc", "", true, ""},
|
{"1.1.1.1/abc", "", true, "", netip.Prefix{}},
|
||||||
{"1234", "", true, ""},
|
{"1234", "", true, "", netip.Prefix{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.input, func(t *testing.T) {
|
t.Run(tt.input, func(t *testing.T) {
|
||||||
result, addr, err := GetResourceType(tt.input)
|
result, domain, prefix, err := GetResourceType(tt.input)
|
||||||
|
|
||||||
if result != tt.expectedType {
|
if result != tt.expectedType {
|
||||||
t.Errorf("Expected type %v, got %v", tt.expectedType, result)
|
t.Errorf("Expected type %v, got %v", tt.expectedType, result)
|
||||||
}
|
}
|
||||||
@ -38,8 +41,12 @@ func TestGetResourceType(t *testing.T) {
|
|||||||
t.Errorf("Expected error, got nil")
|
t.Errorf("Expected error, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr != tt.expectedAddr {
|
if prefix != tt.expectedPrefix {
|
||||||
t.Errorf("Expected address %v, got %v", tt.expectedAddr, addr)
|
t.Errorf("Expected address %v, got %v", tt.expectedPrefix, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain != tt.expectedDomain {
|
||||||
|
t.Errorf("Expected domain %v, got %v", tt.expectedDomain, domain)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2502,7 +2502,13 @@ func TestSqlStore_SaveNetworkResource(t *testing.T) {
|
|||||||
|
|
||||||
savedNetResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, netResource.ID)
|
savedNetResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, netResource.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, netResource, savedNetResource)
|
require.Equal(t, netResource.ID, savedNetResource.ID)
|
||||||
|
require.Equal(t, netResource.Name, savedNetResource.Name)
|
||||||
|
require.Equal(t, netResource.NetworkID, savedNetResource.NetworkID)
|
||||||
|
require.Equal(t, netResource.Type, resourceTypes.NetworkResourceType("domain"))
|
||||||
|
require.Equal(t, netResource.Domain, "example.com")
|
||||||
|
require.Equal(t, netResource.AccountID, savedNetResource.AccountID)
|
||||||
|
require.Equal(t, netResource.Prefix, netip.Prefix{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlStore_DeleteNetworkResource(t *testing.T) {
|
func TestSqlStore_DeleteNetworkResource(t *testing.T) {
|
||||||
|
Reference in New Issue
Block a user