Skip to content

Commit

Permalink
Merge pull request #165 from xmidt-org/update-validator
Browse files Browse the repository at this point in the history
Update validator code to use Locator.
  • Loading branch information
schmidtw authored Mar 12, 2024
2 parents 681c7b1 + 5d7a2a0 commit 5e79fc5
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 77 deletions.
14 changes: 12 additions & 2 deletions id.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
const (
hexDigits = "0123456789abcdefABCDEF"
macDelimiters = ":-.,"
macLength = 12

SchemeMAC = "mac"
SchemeUUID = "uuid"
Expand Down Expand Up @@ -99,6 +98,8 @@ func ParseDeviceID(deviceName string) (DeviceID, error) {
// The general format is:
//
// {scheme}:{authority}/{service}/{ignored}
//
// See https://xmidt.io/docs/wrp/basics/#locators for more details.
type Locator struct {
// Scheme is the scheme type of the locator. A CPE will have the forms
// `mac`, `uuid`, `serial`, `self`. A server or cloud service will have
Expand Down Expand Up @@ -151,6 +152,10 @@ func ParseLocator(locator string) (*Locator, error) {

// If the locator is a device identifier, then we need to parse it.
switch l.Scheme {
case SchemeDNS, SchemeEvent:
if l.Authority == "" {
return nil, ErrorInvalidLocator
}
case SchemeMAC, SchemeUUID, SchemeSerial, SchemeSelf:
id, err := makeDeviceID(l.Scheme, l.Authority)
if err != nil {
Expand Down Expand Up @@ -198,6 +203,10 @@ func makeDeviceID(prefix, idPart string) (DeviceID, error) {
if idPart != "" {
return invalidDeviceID, ErrorInvalidDeviceName
}
case SchemeUUID, SchemeSerial:
if idPart == "" {
return invalidDeviceID, ErrorInvalidDeviceName
}
case SchemeMAC:
var invalidCharacter rune = -1
idPart = strings.Map(
Expand All @@ -215,7 +224,8 @@ func makeDeviceID(prefix, idPart string) (DeviceID, error) {
idPart,
)

if invalidCharacter != -1 || len(idPart) != macLength {
if invalidCharacter != -1 ||
((len(idPart) != 12) && (len(idPart) != 16) && (len(idPart) != 40)) {
return invalidDeviceID, ErrorInvalidDeviceName
}
default:
Expand Down
2 changes: 1 addition & 1 deletion wrpvalidator/metaValidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func ExampleMetaValidator() {
}
}

// Output: source errors: validator `source`: Validator error [Source] err=invalid Source name 'external.com': value given doesn't match expected locator pattern: ^(?P<scheme>(?i)mac|uuid|event|dns|serial):(?P<authority>[^/]+)?
// Output: source errors: validator `source`: Validator error [Source] err=invalid Source name 'external.com': invalid locator
}

func TestMetaValidatorUnmarshal(t *testing.T) {
Expand Down
73 changes: 8 additions & 65 deletions wrpvalidator/specValidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ package wrpvalidator
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"unicode"

"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
Expand All @@ -19,18 +15,7 @@ import (
)

const (
serialPrefix = "serial"
uuidPrefix = "uuid"
eventPrefix = "event"
dnsPrefix = "dns"
macPrefix = "mac"
)

const (
hexDigits = "0123456789abcdefABCDEF"
macDelimiters = ":-.,"
macLength = 12
scheme = macPrefix + `|` + uuidPrefix + `|` + eventPrefix + `|` + dnsPrefix + `|` + serialPrefix
uuidPrefix = "uuid"
)

var (
Expand All @@ -39,16 +24,6 @@ var (
ErrorInvalidSource = NewValidatorError(errors.New("invalid Source name"), "", []string{"Source"})
ErrorInvalidDestination = NewValidatorError(errors.New("invalid Destination name"), "", []string{"Destination"})
errorInvalidUUID = errors.New("invalid UUID")
errorEmptyAuthority = errors.New("invalid empty authority (ID)")
errorInvalidMacLength = errors.New("invalid mac length")
errorInvalidCharacter = errors.New("invalid character")
errorInvalidLocatorPattern = errors.New("value given doesn't match expected locator pattern")
)

// locatorPattern is the precompiled regular expression that all source and dest locators must match.
// Matching is partial, as everything after the authority (ID) is ignored. https://xmidt.io/docs/wrp/basics/#locators
var locatorPattern = regexp.MustCompile(
`^(?P<scheme>(?i)` + scheme + `):(?P<authority>[^/]+)?`,
)

// SpecWithMetrics ensures messages are valid based on each spec validator in the list.
Expand Down Expand Up @@ -146,12 +121,7 @@ func UTF8(m wrp.Message) error {

// MessageType takes messages and validates their Type.
func MessageType(m wrp.Message) error {
if m.Type < wrp.Invalid0MessageType || m.Type > wrp.LastMessageType {
return ErrorInvalidMessageType
}

switch m.Type {
case wrp.Invalid0MessageType, wrp.Invalid1MessageType, wrp.LastMessageType:
if m.Type <= wrp.Invalid1MessageType || m.Type >= wrp.LastMessageType {
return ErrorInvalidMessageType
}

Expand Down Expand Up @@ -183,42 +153,15 @@ func Destination(m wrp.Message) error {
// validateLocator validates a given locator's scheme and authority (ID).
// Only mac and uuid schemes' IDs are validated. IDs from serial, event and dns schemes are
// not validated.
func validateLocator(l string) error {
match := locatorPattern.FindStringSubmatch(l)
if match == nil {
return fmt.Errorf("%w: %s", errorInvalidLocatorPattern, locatorPattern)
}

idPart := match[2]
if len(idPart) == 0 {
return errorEmptyAuthority
func validateLocator(s string) error {
l, err := wrp.ParseLocator(s)
if err != nil {
return err
}

switch strings.ToLower(match[1]) {
case macPrefix:
var invalidCharacter rune = -1
idPart = strings.Map(
func(r rune) rune {
switch {
case strings.ContainsRune(hexDigits, r):
return unicode.ToLower(r)
case strings.ContainsRune(macDelimiters, r):
return -1
default:
invalidCharacter = r
return -1
}
},
idPart,
)

if invalidCharacter != -1 {
return fmt.Errorf("%w: %v", errorInvalidCharacter, strconv.QuoteRune(invalidCharacter))
} else if len(idPart) != macLength {
return errorInvalidMacLength
}
switch l.Scheme {
case uuidPrefix:
if _, err := uuid.Parse(idPart); err != nil {
if _, err := uuid.Parse(l.Authority); err != nil {
return fmt.Errorf("%w: %v", errorInvalidUUID, err)
}
}
Expand Down
18 changes: 9 additions & 9 deletions wrpvalidator/specValidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,17 +615,17 @@ func testValidateLocator(t *testing.T) {
{
description: "Mac ID error, invalid mac ID character",
value: "MAC:invalid45566",
expectedErr: errorInvalidCharacter,
expectedErr: wrp.ErrorInvalidDeviceName,
},
{
description: "Mac ID error, invalid mac ID length",
value: "mac:11-aa-BB-44-55",
expectedErr: errorInvalidMacLength,
expectedErr: wrp.ErrorInvalidDeviceName,
},
{
description: "Mac ID error, no ID",
value: "mac:",
expectedErr: errorEmptyAuthority,
expectedErr: wrp.ErrorInvalidDeviceName,
},
// Serial success case
{
Expand All @@ -637,7 +637,7 @@ func testValidateLocator(t *testing.T) {
{
description: "Invalid serial ID error, no ID",
value: "serial:",
expectedErr: errorEmptyAuthority,
expectedErr: wrp.ErrorInvalidDeviceName,
},
// UUID success case
// The variant specified in RFC4122
Expand Down Expand Up @@ -698,7 +698,7 @@ func testValidateLocator(t *testing.T) {
{
description: "Invalid UUID ID error, no ID",
value: "uuid:",
expectedErr: errorEmptyAuthority,
expectedErr: wrp.ErrorInvalidDeviceName,
},
// Event success case
{
Expand All @@ -710,7 +710,7 @@ func testValidateLocator(t *testing.T) {
{
description: "Invalid event ID error, no ID",
value: "event:",
expectedErr: errorEmptyAuthority,
expectedErr: wrp.ErrorInvalidLocator,
},
// DNS success case
{
Expand All @@ -722,18 +722,18 @@ func testValidateLocator(t *testing.T) {
{
description: "Invalid DNS ID error, no ID",
value: "dns:",
expectedErr: errorEmptyAuthority,
expectedErr: wrp.ErrorInvalidLocator,
},
// Scheme failure case
{
description: "Invalid scheme error",
value: "invalid:a-BB-44-55",
expectedErr: errorInvalidLocatorPattern,
expectedErr: wrp.ErrorInvalidLocator,
},
{
description: "Invalid scheme error, empty string",
value: "",
expectedErr: errorInvalidLocatorPattern,
expectedErr: wrp.ErrorInvalidLocator,
},
}

Expand Down

0 comments on commit 5e79fc5

Please sign in to comment.