From e40bece34258ded65ca46f4e66272d73d0c8a9f3 Mon Sep 17 00:00:00 2001 From: steveorourke Date: Mon, 26 Jul 2021 14:10:57 +1000 Subject: [PATCH] Add support for jwt validation (#783) --- README.md | 1 + baked_in.go | 6 ++++++ doc.go | 6 ++++++ regexes.go | 2 ++ translations/en/en.go | 5 +++++ translations/en/en_test.go | 5 +++++ validator_test.go | 36 ++++++++++++++++++++++++++++++++++++ 7 files changed, 61 insertions(+) diff --git a/README.md b/README.md index 7dcc5af3a..cdbb80529 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Baked-in Validations | isbn10 | International Standard Book Number 10 | | isbn13 | International Standard Book Number 13 | | json | JSON | +| jwt | JSON Web Token (JWT) | | latitude | Latitude | | longitude | Longitude | | rgb | RGB String | diff --git a/baked_in.go b/baked_in.go index 224a1c22d..b22c01aba 100644 --- a/baked_in.go +++ b/baked_in.go @@ -181,6 +181,7 @@ var ( "url_encoded": isURLEncoded, "dir": isDir, "json": isJSON, + "jwt": isJWT, "hostname_port": isHostnamePort, "lowercase": isLowercase, "uppercase": isUppercase, @@ -2235,6 +2236,11 @@ func isJSON(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isJWT is the validation function for validating if the current field's value is a valid JWT string. +func isJWT(fl FieldLevel) bool { + return jWTRegex.MatchString(fl.Field().String()) +} + // isHostnamePort validates a : combination for fields typically used for socket address. func isHostnamePort(fl FieldLevel) bool { val := fl.Field().String() diff --git a/doc.go b/doc.go index eafad0db4..cf1376bfa 100644 --- a/doc.go +++ b/doc.go @@ -811,6 +811,12 @@ This validates that a string value is valid JSON Usage: json +JWT String + +This validates that a string value is a valid JWT + + Usage: jwt + File path This validates that a string value contains a valid file path and that diff --git a/regexes.go b/regexes.go index ddcf785a4..df00c4ebc 100644 --- a/regexes.go +++ b/regexes.go @@ -48,6 +48,7 @@ const ( uRLEncodedRegexString = `^(?:[^%]|%[0-9A-Fa-f]{2})*$` hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?` hTMLRegexString = `<[/]?([a-zA-Z]+).*?>` + jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$" splitParamsRegexString = `'[^']*'|\S+` bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$` ) @@ -98,6 +99,7 @@ var ( uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) hTMLRegex = regexp.MustCompile(hTMLRegexString) + jWTRegex = regexp.MustCompile(jWTRegexString) splitParamsRegex = regexp.MustCompile(splitParamsRegexString) bicRegex = regexp.MustCompile(bicRegexString) ) diff --git a/translations/en/en.go b/translations/en/en.go index 3bbca51a0..b95f7ddb6 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1284,6 +1284,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must be a valid json string", override: false, }, + { + tag: "jwt", + translation: "{0} must be a valid jwt string", + override: false, + }, { tag: "lowercase", translation: "{0} must be a lowercase string", diff --git a/translations/en/en_test.go b/translations/en/en_test.go index fd8df77ec..0f207685f 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -141,6 +141,7 @@ func TestTranslations(t *testing.T) { UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` JSONString string `validate:"json"` + JWTString string `validate:"jwt"` LowercaseString string `validate:"lowercase"` UppercaseString string `validate:"uppercase"` Datetime string `validate:"datetime=2006-01-02"` @@ -646,6 +647,10 @@ func TestTranslations(t *testing.T) { ns: "Test.JSONString", expected: "JSONString must be a valid json string", }, + { + ns: "Test.JWTString", + expected: "JWTString must be a valid jwt string", + }, { ns: "Test.LowercaseString", expected: "LowercaseString must be a lowercase string", diff --git a/validator_test.go b/validator_test.go index 3f0ea8d59..8e9dc037c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -10787,6 +10787,42 @@ func TestJSONValidation(t *testing.T) { }, "Bad field type int") } +func TestJWTValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiZ29waGVyIn0.O_bROM_szPq9qBql-XDHMranHwP48ODdoLICWzqBr_U", true}, + {"acb123-_.def456-_.ghi789-_", true}, + {"eyJhbGciOiJOT05FIn0.e30.", true}, + {"eyJhbGciOiJOT05FIn0.e30.\n", false}, + {"\x00.\x00.\x00", false}, + {"", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "jwt") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d jwt failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d jwt failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "jwt" { + t.Fatalf("Index: %d jwt failed Error: %s", i, errs) + } + } + } + } +} + func Test_hostnameport_validator(t *testing.T) { type Host struct { Addr string `validate:"hostname_port"`