From c646b7627b940054ae18da97047b5e0059df9b28 Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 1/6] fix(openfga): fix Rule.id regex validation --- pkg/js/tests/{valdiate-store.test.ts => validate-store.test.ts} | 2 +- pkg/js/validator/validate-rules.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename pkg/js/tests/{valdiate-store.test.ts => validate-store.test.ts} (95%) diff --git a/pkg/js/tests/valdiate-store.test.ts b/pkg/js/tests/validate-store.test.ts similarity index 95% rename from pkg/js/tests/valdiate-store.test.ts rename to pkg/js/tests/validate-store.test.ts index f5070b3f..8c27fa92 100644 --- a/pkg/js/tests/valdiate-store.test.ts +++ b/pkg/js/tests/validate-store.test.ts @@ -13,7 +13,7 @@ describe("validate valid store file", () => { testCases.forEach((testCase) => { const testFn = testCase.skip ? it.skip : it; - testFn(`should valdiate ${testCase.name} `, () => { + testFn(`should validate ${testCase.name} `, () => { const schemaValidator: ValidateFunction = YamlStoreValidator(); const yaml = YAML.parseDocument(fs.readFileSync(testCase.store).toString()); diff --git a/pkg/js/validator/validate-rules.ts b/pkg/js/validator/validate-rules.ts index 5f84ad45..1364478e 100644 --- a/pkg/js/validator/validate-rules.ts +++ b/pkg/js/validator/validate-rules.ts @@ -2,7 +2,7 @@ export const Rules = { type: "[^:#@\\*\\s]{1,254}", relation: "[^:#@\\*\\s]{1,50}", condition: "[^\\*\\s]{1,50}", - id: "[^#:\\*\\s]+", + id: "(?![#:\\s*])[a-zA-Z0-9_|*@.+]+", object: "[^\\s]{2,256}", }; From 11f374c96a604a1239d801d9e47fad904a7835e0 Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 2/6] fix(openfga): added unit tests for new regex for Rules.id --- pkg/js/tests/validate-rules.test.ts | 74 +++++++++++++++++++++++++++++ pkg/js/validator/validate-rules.ts | 4 ++ 2 files changed, 78 insertions(+) diff --git a/pkg/js/tests/validate-rules.test.ts b/pkg/js/tests/validate-rules.test.ts index cd9570ed..943da442 100644 --- a/pkg/js/tests/validate-rules.test.ts +++ b/pkg/js/tests/validate-rules.test.ts @@ -172,4 +172,78 @@ describe("Validation Rules", () => { validatedBadStructure(Validator.type); }); + + describe("Rule 'id'", () => { + it("should pass 'document1'", () => { + expect(Validator.objectId("document1")).toBeTruthy(); + }); + + it("should pass 'doc_123'", () => { + expect(Validator.objectId("doc_123")).toBeTruthy(); + }); + + it("should pass 'user@domain.com'", () => { + expect(Validator.objectId("user@domain.com")).toBeTruthy(); + }); + + it("should pass 'file.name'", () => { + expect(Validator.objectId("file.name")).toBeTruthy(); + }); + + it("should pass 'data+set'", () => { + expect(Validator.objectId("data+set")).toBeTruthy(); + }); + + it("should pass 'pipe|char'", () => { + expect(Validator.objectId("pipe|char")).toBeTruthy(); + }); + + it("should pass 'star*char'", () => { + expect(Validator.objectId("star*char")).toBeTruthy(); + }); + + it("should pass 'underscore_'", () => { + expect(Validator.objectId("underscore_")).toBeTruthy(); + }); + + it("should pass 'pipe|underscore_@domain.com'", () => { + expect(Validator.objectId("pipe|underscore_@domain.com")).toBeTruthy(); + }); + + it("should fail '#document1'", () => { + expect(Validator.objectId("#document1")).toBeFalsy(); + }); + + it("should fail ':doc123'", () => { + expect(Validator.objectId(":doc123")).toBeFalsy(); + }); + + it("should fail ' doc123'", () => { + expect(Validator.objectId(" doc123")).toBeFalsy(); + }); + + it("should fail 'doc*123'", () => { + expect(Validator.objectId("doc*123")).toBeTruthy(); + }); + + it("should fail 'doc:123'", () => { + expect(Validator.objectId("doc:123")).toBeFalsy(); + }); + + it("should fail 'doc#123'", () => { + expect(Validator.objectId("doc#123")).toBeFalsy(); + }); + + it("should fail 'doc 123'", () => { + expect(Validator.objectId("doc 123")).toBeFalsy(); + }); + + it("should fail 'doc*'", () => { + expect(Validator.objectId("doc*")).toBeTruthy(); + }); + + it("should fail 'doc:'", () => { + expect(Validator.objectId("doc:")).toBeFalsy(); + }); + }); }); diff --git a/pkg/js/validator/validate-rules.ts b/pkg/js/validator/validate-rules.ts index 1364478e..1ccecaa9 100644 --- a/pkg/js/validator/validate-rules.ts +++ b/pkg/js/validator/validate-rules.ts @@ -42,6 +42,10 @@ export const Validator = { type: (type: string): boolean => { return validateFieldValue(`^${Rules.type}$`, type); }, + // ObjectId name + objectId: (id: string): boolean => { + return validateFieldValue(`^${Rules.id}$`, id); + }, }; const validateFieldValue = (rule: string, value: string): boolean => { From 6bb8f13568e42cd98d473b924a747537e5dc32cd Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 3/6] feat(lanuage): added objectId regex for go and java --- pkg/go/validation/validation-rules.go | 8 ++++- pkg/go/validation/validation-rules_test.go | 36 +++++++++++++++++++ .../language/validation/Validator.java | 9 ++++- pkg/js/validator/validate-rules.ts | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/pkg/go/validation/validation-rules.go b/pkg/go/validation/validation-rules.go index c92e586a..811eae26 100644 --- a/pkg/go/validation/validation-rules.go +++ b/pkg/go/validation/validation-rules.go @@ -11,7 +11,7 @@ const ( RuleType Rule = "[^:#@\\*\\s]{1,254}" RuleRelation Rule = "[^:#@\\*\\s]{1,50}" RuleCondition Rule = "[^\\*\\s]{1,50}" - RuleID Rule = "[^#:\\*\\s]+" + RuleID Rule = "[^#:\\s*][a-zA-Z0-9_|*@.+]*" RuleObject Rule = "[^\\s]{2,256}" ) @@ -22,6 +22,12 @@ func ValidateObject(object string) bool { return typeMatch && objectMatch } +func ValidateObjectId(relation string) bool { + match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleID), relation) + + return match +} + func ValidateRelation(relation string) bool { match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleRelation), relation) diff --git a/pkg/go/validation/validation-rules_test.go b/pkg/go/validation/validation-rules_test.go index ff6e11af..fa9b6272 100644 --- a/pkg/go/validation/validation-rules_test.go +++ b/pkg/go/validation/validation-rules_test.go @@ -24,6 +24,42 @@ func validateBadStructure(t *testing.T, validator func(string) bool) { assert.False(t, validator("item:**")) } +func TestValidateObjectId(t *testing.T) { + t.Parallel() + + tests := []struct { + value string + expected bool + }{ + {"document1", true}, // Should pass valid ID + {"doc_123", true}, // Should pass valid ID with underscore + {"user@domain.com", true}, // Should pass valid email-like ID + {"file.name", true}, // Should pass valid ID with dot + {"data+set", true}, // Should pass valid ID with plus + {"pipe|char", true}, // Should pass valid ID with pipe + {"star*char", true}, // Should pass valid ID with star + {"underscore_", true}, // Should pass valid ID with underscore + {"pipe|underscore_@domain.com", true}, // Should pass valid complex ID + {"#document1", false}, // Should fail if starts with # + {":doc123", false}, // Should fail if starts with : + {" doc123", false}, // Should fail if starts with space + {"doc*123", true}, // Should pass valid ID with star + {"doc:123", false}, // Should fail if contains : + {"doc#123", false}, // Should fail if contains # + {"doc 123", false}, // Should fail if contains space + {"doc*", true}, // Should pass valid ID with star + {"doc:", false}, // Should fail if ends with : + {" doc", false}, // Should fail if starts with space + } + + for _, test := range tests { + t.Run(test.value, func(t *testing.T) { + assert.Equal(t, test.expected, ValidateObjectId(test.value)) + }) + } + +} + func TestValidateObject(t *testing.T) { t.Parallel() diff --git a/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java b/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java index e4afc0c8..56ccb9b9 100644 --- a/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java +++ b/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java @@ -6,7 +6,7 @@ public class Rules { public static final String TYPE = "[^:#@\\*\\s]{1,254}"; public static final String RELATION = "[^:#@\\*\\s]{1,50}"; public static final String CONDITION = "[^\\*\\s]{1,50}"; - public static final String ID = "[^#:\\*\\s]+"; + public static final String ID = "[^#:\\s*][a-zA-Z0-9_|*@.+]*"; public static final String OBJECT = "[^\\s]{2,256}"; } @@ -14,6 +14,9 @@ public static class Regexes { public static final ValidationRegex object = ValidationRegex.build("object", String.format("^%s$", Rules.OBJECT)); + public static final ValidationRegex objectId = + ValidationRegex.build("object", String.format("^%s$", Rules.ID)); + public static final ValidationRegex typeId = ValidationRegex.build("object", String.format("^%s:%s$", Rules.TYPE, Rules.ID)); @@ -40,6 +43,10 @@ public static boolean validateObject(String object) { return Regexes.typeId.matches(object) && Regexes.object.matches(object); } + public static boolean validateObjectId(String objectId) { + return Regexes.objectId.matches(objectId); + } + public static boolean validateRelation(String relation) { return Regexes.relation.matches(relation); } diff --git a/pkg/js/validator/validate-rules.ts b/pkg/js/validator/validate-rules.ts index 1ccecaa9..7c3af74d 100644 --- a/pkg/js/validator/validate-rules.ts +++ b/pkg/js/validator/validate-rules.ts @@ -2,7 +2,7 @@ export const Rules = { type: "[^:#@\\*\\s]{1,254}", relation: "[^:#@\\*\\s]{1,50}", condition: "[^\\*\\s]{1,50}", - id: "(?![#:\\s*])[a-zA-Z0-9_|*@.+]+", + id: "[^#:\\s*][a-zA-Z0-9_|*@.+]*", object: "[^\\s]{2,256}", }; From 2e0fb5d18ee3d66911ff33ac7ae3329e870507fb Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 4/6] feat(language): added unit test for java --- .../language/validation/ValidationRules.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkg/java/src/test/java/dev/openfga/language/validation/ValidationRules.java b/pkg/java/src/test/java/dev/openfga/language/validation/ValidationRules.java index f89ea39f..c676407b 100644 --- a/pkg/java/src/test/java/dev/openfga/language/validation/ValidationRules.java +++ b/pkg/java/src/test/java/dev/openfga/language/validation/ValidationRules.java @@ -43,6 +43,30 @@ public void ruleObjectTest() { validatedBadStructure(Validator::validateObject); } + @Test + public void testValidateObjectId() { + // Valid cases + assertTrue(Validator.Regexes.objectId.matches("document1")); + assertTrue(Validator.Regexes.objectId.matches("doc_123")); + assertTrue(Validator.Regexes.objectId.matches("user@domain.com")); + assertTrue(Validator.Regexes.objectId.matches("file.name")); + assertTrue(Validator.Regexes.objectId.matches("data+set")); + assertTrue(Validator.Regexes.objectId.matches("pipe|char")); + assertTrue(Validator.Regexes.objectId.matches("star*char")); + assertTrue(Validator.Regexes.objectId.matches("underscore_")); + assertTrue(Validator.Regexes.objectId.matches("pipe|underscore_@domain.com")); + + // Invalid cases + assertFalse(Validator.Regexes.objectId.matches("#document1")); + assertFalse(Validator.Regexes.objectId.matches(":doc123")); + assertFalse(Validator.Regexes.objectId.matches(" doc123")); + assertFalse(Validator.Regexes.objectId.matches("doc:123")); + assertFalse(Validator.Regexes.objectId.matches("doc#123")); + assertFalse(Validator.Regexes.objectId.matches("doc 123")); + assertFalse(Validator.Regexes.objectId.matches("doc:")); + assertFalse(Validator.Regexes.objectId.matches(" doc")); + } + @Test public void ruleUserTest() { From 23f369d63fddc45653f67177d8d42c9168e4f8d8 Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 5/6] fix(language): fix java lint issue --- .../main/java/dev/openfga/language/validation/Validator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java b/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java index 56ccb9b9..f4443aaf 100644 --- a/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java +++ b/pkg/java/src/main/java/dev/openfga/language/validation/Validator.java @@ -14,8 +14,7 @@ public static class Regexes { public static final ValidationRegex object = ValidationRegex.build("object", String.format("^%s$", Rules.OBJECT)); - public static final ValidationRegex objectId = - ValidationRegex.build("object", String.format("^%s$", Rules.ID)); + public static final ValidationRegex objectId = ValidationRegex.build("object", String.format("^%s$", Rules.ID)); public static final ValidationRegex typeId = ValidationRegex.build("object", String.format("^%s:%s$", Rules.TYPE, Rules.ID)); From 1dfc01970fc74b31b42470bb7cf70c3984beb0d8 Mon Sep 17 00:00:00 2001 From: Talent Zeng Date: Wed, 25 Sep 2024 09:50:52 -0400 Subject: [PATCH 6/6] feat(language): fix linter for go --- pkg/go/validation/validation-rules.go | 2 +- pkg/go/validation/validation-rules_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/go/validation/validation-rules.go b/pkg/go/validation/validation-rules.go index 811eae26..cf810561 100644 --- a/pkg/go/validation/validation-rules.go +++ b/pkg/go/validation/validation-rules.go @@ -22,7 +22,7 @@ func ValidateObject(object string) bool { return typeMatch && objectMatch } -func ValidateObjectId(relation string) bool { +func ValidateObjectID(relation string) bool { match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleID), relation) return match diff --git a/pkg/go/validation/validation-rules_test.go b/pkg/go/validation/validation-rules_test.go index fa9b6272..eeea0aec 100644 --- a/pkg/go/validation/validation-rules_test.go +++ b/pkg/go/validation/validation-rules_test.go @@ -24,7 +24,7 @@ func validateBadStructure(t *testing.T, validator func(string) bool) { assert.False(t, validator("item:**")) } -func TestValidateObjectId(t *testing.T) { +func TestValidateObjectID(t *testing.T) { t.Parallel() tests := []struct { @@ -54,7 +54,8 @@ func TestValidateObjectId(t *testing.T) { for _, test := range tests { t.Run(test.value, func(t *testing.T) { - assert.Equal(t, test.expected, ValidateObjectId(test.value)) + t.Parallel() + assert.Equal(t, test.expected, ValidateObjectID(test.value)) }) }