diff --git a/baked_in.go b/baked_in.go index 51b65f4..2a086fd 100644 --- a/baked_in.go +++ b/baked_in.go @@ -5,6 +5,7 @@ import ( "context" "crypto/sha256" "fmt" + urn "github.com/leodido/go-urn" "net" "net/url" "os" @@ -98,6 +99,7 @@ var ( "email": isEmail, "url": isURL, "uri": isURI, + "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, "base64": isBase64, "base64url": isBase64URL, @@ -1077,6 +1079,24 @@ func isURL(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. +func isUrnRFC2141(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + str := field.String() + + _, match := urn.Parse([]byte(str)) + + return match + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // IsFile is the validation function for validating if the current field's value is a valid file path. func isFile(fl FieldLevel) bool { field := fl.Field() diff --git a/doc.go b/doc.go index 25bf2e7..dd4fde6 100644 --- a/doc.go +++ b/doc.go @@ -610,6 +610,13 @@ This will accept any uri the golang request uri accepts Usage: uri +Urn RFC 2141 String + +This validataes that a string value contains a valid URN +according to the RFC 2141 spec. + + Usage: urn_rfc2141 + Base64 String This validates that a string value contains a valid base64 value. diff --git a/validator_test.go b/validator_test.go index 3a4b8f0..7213d9b 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5748,6 +5748,83 @@ func TestIsLte(t *testing.T) { NotEqual(t, errs, nil) } +func TestUrnRFC2141(t *testing.T) { + + var tests = []struct { + param string + expected bool + }{ + {"urn:a:b", true}, + {"urn:a::", true}, + {"urn:a:-", true}, + {"URN:simple:simple", true}, + {"urn:urna:simple", true}, + {"urn:burnout:nss", true}, + {"urn:burn:nss", true}, + {"urn:urnurnurn:x", true}, + {"urn:abcdefghilmnopqrstuvzabcdefghilm:x", true}, + {"URN:123:x", true}, + {"URN:abcd-:x", true}, + {"URN:abcd-abcd:x", true}, + {"urn:urnx:urn", true}, + {"urn:ciao:a:b:c", true}, + {"urn:aaa:x:y:", true}, + {"urn:ciao:-", true}, + {"urn:colon:::::nss", true}, + {"urn:ciao:@!=%2C(xyz)+a,b.*@g=$_'", true}, + {"URN:hexes:%25", true}, + {"URN:x:abc%1Dz%2F%3az", true}, + {"URN:foo:a123,456", true}, + {"urn:foo:a123,456", true}, + {"urn:FOO:a123,456", true}, + {"urn:foo:A123,456", true}, + {"urn:foo:a123%2C456", true}, + {"URN:FOO:a123%2c456", true}, + {"URN:FOO:ABC%FFabc123%2c456", true}, + {"URN:FOO:ABC%FFabc123%2C456%9A", true}, + {"urn:ietf:params:scim:schemas:core:2.0:User", true}, + {"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified", true}, + {"URN:-xxx:x", false}, + {"urn::colon:nss", false}, + {"urn:abcdefghilmnopqrstuvzabcdefghilmn:specificstring", false}, + {"URN:a!?:x", false}, + {"URN:#,:x", false}, + {"urn:urn:NSS", false}, + {"urn:URN:NSS", false}, + {"urn:white space:NSS", false}, + {"urn:concat:no spaces", false}, + {"urn:a:%", false}, + {"urn:", false}, + } + + tag := "urn_rfc2141" + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, tag) + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d URN failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d URN failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != tag { + t.Fatalf("Index: %d URN failed Error: %s", i, errs) + } + } + } + } + + i := 1 + PanicMatches(t, func() { validate.Var(i, tag) }, "Bad field type int") +} + func TestUrl(t *testing.T) { var tests = []struct {