Skip to content

Commit

Permalink
Feat: add SpiceDB validations (#1125)
Browse files Browse the repository at this point in the history
## Fixes Or Enhances
Adds support for validating
[SpiceDB](https://github.com/authzed/spicedb) object ids, types, and
permissions
  • Loading branch information
wscalf committed Aug 6, 2023
1 parent 0f6b874 commit 4b53ea8
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -158,6 +158,7 @@ Baked-in Validations
| credit_card | Credit Card Number |
| mongodb | MongoDB ObjectID |
| cron | Cron |
| spicedb | SpiceDb ObjectID/Permission/Type |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
Expand Down
18 changes: 18 additions & 0 deletions baked_in.go
Expand Up @@ -230,6 +230,7 @@ var (
"luhn_checksum": hasLuhnChecksum,
"mongodb": isMongoDB,
"cron": isCron,
"spicedb": isSpiceDB,
}
)

Expand Down Expand Up @@ -2812,6 +2813,23 @@ func isMongoDB(fl FieldLevel) bool {
return mongodbRegex.MatchString(val)
}

// isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
func isSpiceDB(fl FieldLevel) bool {
val := fl.Field().String()
param := fl.Param()

switch param {
case "permission":
return spicedbPermissionRegex.MatchString(val)
case "type":
return spicedbTypeRegex.MatchString(val)
case "id", "":
return spicedbIDRegex.MatchString(val)
}

panic("Unrecognized parameter: " + param)
}

// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
func isCreditCard(fl FieldLevel) bool {
val := fl.Field().String()
Expand Down
6 changes: 6 additions & 0 deletions doc.go
Expand Up @@ -1382,6 +1382,12 @@ This validates that a string value contains a valid cron expression.
Usage: cron
# SpiceDb ObjectID/Permission/Object Type
This validates that a string is valid for use with SpiceDb for the indicated purpose. If no purpose is given, a purpose of 'id' is assumed.
Usage: spicedb=id|permission|type
# Alias Validators and Tags
Alias Validators and Tags
Expand Down
6 changes: 6 additions & 0 deletions regexes.go
Expand Up @@ -68,6 +68,9 @@ const (
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbRegexString = "^[a-f\\d]{24}$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"
)

var (
Expand Down Expand Up @@ -134,4 +137,7 @@ var (
cveRegex = regexp.MustCompile(cveRegexString)
mongodbRegex = regexp.MustCompile(mongodbRegexString)
cronRegex = regexp.MustCompile(cronRegexString)
spicedbIDRegex = regexp.MustCompile(spicedbIDRegexString)
spicedbPermissionRegex = regexp.MustCompile(spicedbPermissionRegexString)
spicedbTypeRegex = regexp.MustCompile(spicedbTypeRegexString)
)
74 changes: 74 additions & 0 deletions validator_test.go
Expand Up @@ -13094,6 +13094,80 @@ func TestMongoDBObjectIDFormatValidation(t *testing.T) {
}
}

func TestSpiceDBValueFormatValidation(t *testing.T) {
tests := []struct {
value string
tag string
expected bool
}{
//Must be an asterisk OR a string containing alphanumeric characters and a restricted set a special symbols: _ | / - = +
{"*", "spicedb=id", true},
{`azAZ09_|/-=+`, "spicedb=id", true},
{`a*`, "spicedb=id", false},
{`/`, "spicedb=id", true},
{"*", "spicedb", true},

//Must begin and end with a lowercase letter, may also contain numbers and underscores between, min length 3, max length 64
{"a", "spicedb=permission", false},
{"1", "spicedb=permission", false},
{"a1", "spicedb=permission", false},
{"a_b", "spicedb=permission", true},
{"A_b", "spicedb=permission", false},
{"a_B", "spicedb=permission", false},
{"abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxyz", "spicedb=permission", true},
{"abcdefghijklmnopqrstuvwxyz_01234_56789_abcdefghijklmnopqrstuvwxyz", "spicedb=permission", false},

//Object types follow the same rules as permissions for the type name plus an optional prefix up to 63 characters with a /
{"a", "spicedb=type", false},
{"1", "spicedb=type", false},
{"a1", "spicedb=type", false},
{"a_b", "spicedb=type", true},
{"A_b", "spicedb=type", false},
{"a_B", "spicedb=type", false},
{"abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxyz", "spicedb=type", true},
{"abcdefghijklmnopqrstuvwxyz_01234_56789_abcdefghijklmnopqrstuvwxyz", "spicedb=type", false},

{`a_b/a`, "spicedb=type", false},
{`a_b/1`, "spicedb=type", false},
{`a_b/a1`, "spicedb=type", false},
{`a_b/a_b`, "spicedb=type", true},
{`a_b/A_b`, "spicedb=type", false},
{`a_b/a_B`, "spicedb=type", false},
{`a_b/abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxyz`, "spicedb=type", true},
{`a_b/abcdefghijklmnopqrstuvwxyz_01234_56789_abcdefghijklmnopqrstuvwxyz`, "spicedb=type", false},

{`a/a_b`, "spicedb=type", false},
{`1/a_b`, "spicedb=type", false},
{`a1/a_b`, "spicedb=type", false},
{`a_b/a_b`, "spicedb=type", true},
{`A_b/a_b`, "spicedb=type", false},
{`a_B/a_b`, "spicedb=type", false},
{`abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxy/a_b`, "spicedb=type", true},
{`abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxyz/a_b`, "spicedb=type", false},
}

validate := New()

for i, test := range tests {
errs := validate.Var(test.value, test.tag)

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d spicedb failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d spicedb - expected error but there was none.", i)
} else {
val := getError(errs, "", "")
if val.Tag() != "spicedb" {
t.Fatalf("Index: %d spicedb failed Error: %s", i, errs)
}
}
}
}
}

func TestCreditCardFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"credit_card"`
Expand Down

0 comments on commit 4b53ea8

Please sign in to comment.