From 2714da39b9e3443e094db6838da73490fad8659d Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Mon, 12 Feb 2024 13:25:46 +0100 Subject: [PATCH 1/8] docs: update URN RFC 8141 documentation Signed-off-by: Leonardo Di Donato --- doc.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc.go b/doc.go index b47409188..708739103 100644 --- a/doc.go +++ b/doc.go @@ -909,6 +909,13 @@ This will accept any uri the golang request uri accepts Usage: uri +# Urn RFC 8141 String + +This validataes that a string value contains a valid URN +according to the RFC 8141 spec. + + Usage: urn + # Urn RFC 2141 String This validataes that a string value contains a valid URN From eecc538c92db4969b4bbe351ad8fdf2e42a4e1ed Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Mon, 12 Feb 2024 13:26:08 +0100 Subject: [PATCH 2/8] feat: URN (RFC 8141) validator Signed-off-by: Leonardo Di Donato --- baked_in.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/baked_in.go b/baked_in.go index 95f56e008..3aad048a6 100644 --- a/baked_in.go +++ b/baked_in.go @@ -130,6 +130,7 @@ var ( "url": isURL, "http_url": isHttpURL, "uri": isURI, + "urn": isUrnRFC8141, // RFC 8141 "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, "filepath": isFilePath, @@ -1505,6 +1506,23 @@ func isHttpURL(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isUrnRFC8141 is the validation function for validating if the current field's value is a valid URN as per RFC 8141. +func isUrnRFC8141(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + case reflect.String: + + str := field.String() + + _, match := urn.Parse([]byte(str), urn.WithParsingMode(urn.RFC8141Only)) + + return match + } + + 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() From 4cbde7628da7c535a5f7725d64a1263e3562b320 Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Mon, 12 Feb 2024 13:26:30 +0100 Subject: [PATCH 3/8] test: check URN (RFC 8141) validates as meant to Signed-off-by: Leonardo Di Donato --- validator_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/validator_test.go b/validator_test.go index 2826ae70e..e4a3fad73 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8134,6 +8134,83 @@ func TestUrnRFC2141(t *testing.T) { PanicMatches(t, func() { _ = validate.Var(i, tag) }, "Bad field type int") } +func TestUrnRFC8141(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"urn:lex:it:ministero.giustizia:decreto:1992-07-24;358~art5", true}, + {"urn:nid:nss/", true}, + {"urn:nid:nss&", true}, + {"urn:example:1/406/47452/2", true}, + {"urn:example:foo-bar-baz-qux?+CCResolve:cc=uk", true}, + {"urn:example:foo-bar-baz-qux?+&", true}, + {"urn:example:foo-bar-baz-qux?+%16CCResolve:cc=uk", true}, + {"urn:example:weather?=op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z", true}, + {"urn:example:CamelCase1/406/47452/2?=lat=41.22255&long=16.06596#frag?some/slash/~%D0", true}, + {"urn:example:CamelCase1/406/47452/2?=lat=41.22255&long=16.06596#frag", true}, + {"URN:signs:()+,-.:=@;$_!*alnum123456789", 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:xyz: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:urn-7:informal", true}, + {"urn:aa:", false}, + {"URN:x:abc%1Dz%2F%3az", false}, + {"urn:123456789-1234567890-abcdefghilmn:o", false}, + {"urn:ex:ex?+a?+", false}, + {"urn:xn--:nss", false}, + {"urn:a:nss", false}, + {"URN:trailing-:w", false}, + {"URN:-leading:w", false}, + {"urn:urn-s:nss", false}, + {"urn:urn-0:nss", false}, + {"urn:", false}, + } + + tag := "urn" + + 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) { tests := []struct { param string From 755e75ee66ed7ae6b8ac45df31f8283442054e3d Mon Sep 17 00:00:00 2001 From: Leo Di Donato Date: Tue, 4 Jun 2024 05:02:13 +0700 Subject: [PATCH 4/8] fix: urn_rfc8141 as per RFC name Co-authored-by: Dean Karn --- baked_in.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baked_in.go b/baked_in.go index 3aad048a6..ee955fff1 100644 --- a/baked_in.go +++ b/baked_in.go @@ -130,7 +130,7 @@ var ( "url": isURL, "http_url": isHttpURL, "uri": isURI, - "urn": isUrnRFC8141, // RFC 8141 + "urn_rfc8141": isUrnRFC8141, // RFC 8141 "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, "filepath": isFilePath, From 8090251e8aba0ecfb6e7c065e0fea4e87678044b Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Tue, 4 Jun 2024 00:08:54 +0200 Subject: [PATCH 5/8] tests: fixup urn RFC8141 validator name Signed-off-by: Leonardo Di Donato --- baked_in.go | 2 +- doc.go | 2 +- validator_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/baked_in.go b/baked_in.go index ee955fff1..f9c6602f7 100644 --- a/baked_in.go +++ b/baked_in.go @@ -130,7 +130,7 @@ var ( "url": isURL, "http_url": isHttpURL, "uri": isURI, - "urn_rfc8141": isUrnRFC8141, // RFC 8141 + "urn_rfc8141": isUrnRFC8141, // RFC 8141 "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, "filepath": isFilePath, diff --git a/doc.go b/doc.go index 708739103..19ea28e4c 100644 --- a/doc.go +++ b/doc.go @@ -914,7 +914,7 @@ This will accept any uri the golang request uri accepts This validataes that a string value contains a valid URN according to the RFC 8141 spec. - Usage: urn + Usage: urn_rfc8141 # Urn RFC 2141 String diff --git a/validator_test.go b/validator_test.go index e4a3fad73..42e29ced2 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8183,7 +8183,7 @@ func TestUrnRFC8141(t *testing.T) { {"urn:", false}, } - tag := "urn" + tag := "urn_rfc8141" validate := New() From 3eaab96e66c73f5d1eaefad910f9ec5e795fbff6 Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Mon, 7 Apr 2025 14:31:38 +0200 Subject: [PATCH 6/8] docs: mention urn_rfc8141 in README Signed-off-by: Leonardo Di Donato --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b3e113a19..25e22dd67 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ validate := validator.New(validator.WithRequiredStructEnabled()) | http_url | HTTP URL String | | url_encoded | URL Encoded | | urn_rfc2141 | Urn RFC 2141 String | +| urn_rfc8141 | Urn RFC 8141 String | ### Strings: From 84648b94919b652dc1d5cdc1565a0f05017d870b Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato <120051+leodido@users.noreply.github.com> Date: Fri, 17 Jul 2026 09:33:27 +0200 Subject: [PATCH 7/8] fix: reject empty RFC 8141 URNs --- baked_in.go | 3 +++ validator_test.go | 1 + 2 files changed, 4 insertions(+) diff --git a/baked_in.go b/baked_in.go index f9c6602f7..1ea6acd2a 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1514,6 +1514,9 @@ func isUrnRFC8141(fl FieldLevel) bool { case reflect.String: str := field.String() + if str == "" { + return false + } _, match := urn.Parse([]byte(str), urn.WithParsingMode(urn.RFC8141Only)) diff --git a/validator_test.go b/validator_test.go index 42e29ced2..647363513 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8170,6 +8170,7 @@ func TestUrnRFC8141(t *testing.T) { {"urn:ietf:params:scim:schemas:core:2.0:User", true}, {"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:meta.lastModified", true}, {"urn:urn-7:informal", true}, + {"", false}, {"urn:aa:", false}, {"URN:x:abc%1Dz%2F%3az", false}, {"urn:123456789-1234567890-abcdefghilmn:o", false}, From 89fbe428ebe454a39e4c3f45a2af12fe2c096f99 Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato <120051+leodido@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:19:06 +0200 Subject: [PATCH 8/8] style: remove leading newline in URN test --- validator_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index ec3ec0f3f..ac0cf3137 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8747,7 +8747,6 @@ func TestUrnRFC8141(t *testing.T) { validate := New() for i, test := range tests { - errs := validate.Var(test.param, tag) if test.expected {