Skip to content

Commit 2091f7e

Browse files
committed
feat: adding validation for java & go
1 parent 8dd93a9 commit 2091f7e

File tree

7 files changed

+429
-40
lines changed

7 files changed

+429
-40
lines changed

pkg/go/validation/validation-rules.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package validation
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
)
7+
8+
type Rule string
9+
10+
const (
11+
RuleType Rule = "[^:#@\\*\\s]{1,254}"
12+
RuleRelation Rule = "[^:#@\\*\\s]{1,50}"
13+
RuleCondition Rule = "[^\\*\\s]{2,256}"
14+
RuleId Rule = "[^#:\\*\\s]+"
15+
RuleObject Rule = "[^\\s]{2,256}"
16+
)
17+
18+
func ValidateObject(object string) bool {
19+
typeMatch, _ := regexp.MatchString(fmt.Sprintf("^%s:%s$", RuleType, RuleId), object)
20+
objectMatch, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleObject), object)
21+
22+
return typeMatch && objectMatch
23+
}
24+
25+
func ValidateRelation(relation string) bool {
26+
match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleRelation), relation)
27+
28+
return match
29+
}
30+
31+
func ValidateUserSet(userSet string) bool {
32+
match, _ := regexp.MatchString(fmt.Sprintf("^%s:%s#%s$", RuleType, RuleId, RuleRelation), userSet)
33+
34+
return match
35+
}
36+
37+
func ValidateUserObject(userObject string) bool {
38+
typeMatch, _ := regexp.MatchString(fmt.Sprintf("^%s:%s$", RuleType, RuleId), userObject)
39+
objectMatch, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleObject), userObject)
40+
41+
return typeMatch && objectMatch
42+
}
43+
44+
func ValidateUserWildcard(userWildcard string) bool {
45+
match, _ := regexp.MatchString(fmt.Sprintf("^%s:\\*$", RuleType), userWildcard)
46+
47+
return match
48+
}
49+
50+
func ValidateUser(user string) bool {
51+
return ValidateUserSet(user) || ValidateObject(user) || ValidateUserWildcard(user)
52+
}
53+
54+
func ValidateRelationshipCondition(condition string) bool {
55+
match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleCondition), condition)
56+
57+
return match
58+
}
59+
60+
func ValidateType(typeString string) bool {
61+
match, _ := regexp.MatchString(fmt.Sprintf("^%s$", RuleType), typeString)
62+
63+
return match
64+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package validation
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func validateBadStructure(t *testing.T, validator func(string) bool) {
10+
assert.False(t, validator("item::1"))
11+
assert.False(t, validator(":item:1"))
12+
assert.False(t, validator("item:1:"))
13+
assert.False(t, validator("item#relation"))
14+
assert.False(t, validator("item:1##relation"))
15+
assert.False(t, validator("#item:1"))
16+
assert.False(t, validator("item:1#"))
17+
assert.False(t, validator("ite#m:1"))
18+
assert.False(t, validator("it*em:1"))
19+
assert.False(t, validator("*:1"))
20+
assert.False(t, validator("item*thing"))
21+
assert.False(t, validator("item:*thing"))
22+
assert.False(t, validator("item:**"))
23+
}
24+
25+
func TestValidateObject(t *testing.T) {
26+
27+
// Should pass '<type>:<id>'
28+
assert.True(t, ValidateObject("document:1"))
29+
30+
// Should fail if no ':' delimiter
31+
assert.False(t, ValidateObject("document1"))
32+
33+
// Should fail if includes relation
34+
assert.False(t, ValidateObject("document:1#relation"))
35+
36+
validateBadStructure(t, ValidateObject)
37+
}
38+
39+
func TestValidateUserTest(t *testing.T) {
40+
41+
// Should pass if UserSet
42+
assert.True(t, ValidateUser("group:engineering#member"))
43+
44+
// Should pass if UserObject
45+
assert.True(t, ValidateUser("group:engineering"))
46+
47+
// Should pass if UserWildcard
48+
assert.True(t, ValidateUser("group:*"))
49+
50+
// Should fail when missing <id>
51+
assert.False(t, ValidateUser("group"))
52+
53+
validateBadStructure(t, ValidateUser)
54+
}
55+
56+
func TestValidatorUserSet(t *testing.T) {
57+
58+
// Should pass if '<type>:<id>#<relation>'
59+
assert.True(t, ValidateUserSet("group:engineering#member"))
60+
61+
// Shoud fail for 'UserObject'
62+
assert.False(t, ValidateUserSet("group:engineering"))
63+
64+
// Shoud fail for 'UserWildcard'
65+
assert.False(t, ValidateUserSet("group:*"))
66+
67+
// Shoud fail if missing '<id>'
68+
assert.False(t, ValidateUserSet("group"))
69+
70+
validateBadStructure(t, ValidateUserSet)
71+
}
72+
73+
func TestValidatorUserObject(t *testing.T) {
74+
75+
// Should pass for '<type>:<id>'
76+
assert.True(t, ValidateUserObject("group:engineering"))
77+
78+
// Should fail if contains '#'
79+
assert.False(t, ValidateUserObject("group:engineering#member"))
80+
81+
// Should fail if 'Wildcard' is present
82+
assert.False(t, ValidateUserObject("group:*"))
83+
84+
// Should fail if missing '<id>'
85+
assert.False(t, ValidateUserObject("group"))
86+
87+
validateBadStructure(t, ValidateUserObject)
88+
}
89+
90+
func TestValidatorUserWildcard(t *testing.T) {
91+
92+
// Should pass for '<type>:*'
93+
assert.True(t, ValidateUserWildcard("group:*"))
94+
95+
// Should fail for '<type>:<id>'
96+
assert.False(t, ValidateUserWildcard("group:engineering"))
97+
98+
// Should fail if contains '#'
99+
assert.False(t, ValidateUserWildcard("group:engineering#member"))
100+
101+
// Should fail if missing '*'
102+
assert.False(t, ValidateUserObject("group"))
103+
104+
validateBadStructure(t, ValidateUserWildcard)
105+
}
106+
107+
func TestValidatorType(t *testing.T) {
108+
109+
// Should pass '<types>'
110+
assert.True(t, ValidateType("folder"))
111+
112+
// Should ail 'UserObject'
113+
assert.False(t, ValidateType("folder:1"))
114+
115+
// Should fail UserSet
116+
assert.False(t, ValidateType("folder:1#relation"))
117+
118+
validateBadStructure(t, ValidateType)
119+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package dev.openfga.language.validation;
2+
3+
public class Validator {
4+
5+
public class Rules {
6+
public static final String TYPE = "[^:#@\\*\\s]{1,254}";
7+
public static final String RELATION = "[^:#@\\*\\s]{1,50}";
8+
public static final String CONDITION = "[^\\*\\s]{2,256}";
9+
public static final String ID = "[^#:\\*\\s]+";
10+
public static final String OBJECT = "[^\\s]{2,256}";
11+
}
12+
13+
public static class Regexes {
14+
public static final ValidationRegex object = ValidationRegex.build("object",
15+
String.format("^%s$", Rules.OBJECT));
16+
17+
public static final ValidationRegex typeId = ValidationRegex.build("object",
18+
String.format("^%s:%s$", Rules.TYPE, Rules.ID));
19+
20+
public static final ValidationRegex relation = ValidationRegex.build("relation",
21+
String.format("^%s$", Rules.RELATION));
22+
23+
public static final ValidationRegex userSet = ValidationRegex.build("userSet",
24+
String.format("^%s:%s#%s$", Rules.TYPE, Rules.ID, Rules.RELATION));
25+
26+
public static final ValidationRegex userObject = ValidationRegex.build("userObject",
27+
String.format("^%s:%s$", Rules.TYPE, Rules.ID));
28+
29+
public static final ValidationRegex userWildcard = ValidationRegex.build("userWildcard",
30+
String.format("^%s:\\*$", Rules.TYPE));
31+
32+
public static final ValidationRegex condition = ValidationRegex.build("condition",
33+
String.format("^%s$", Rules.CONDITION));
34+
35+
public static final ValidationRegex type = ValidationRegex.build("condition",
36+
String.format("^%s$", Rules.TYPE));
37+
}
38+
39+
public static boolean validateObject(String object) {
40+
return Regexes.typeId.matches(object) && Regexes.object.matches(object);
41+
}
42+
43+
public static boolean validateRelation(String relation) {
44+
return Regexes.relation.matches(relation);
45+
}
46+
47+
public static boolean validateUserSet(String userset) {
48+
return Regexes.userSet.matches(userset);
49+
}
50+
51+
public static boolean validateUserObject(String userObject) {
52+
return Regexes.userObject.matches(userObject);
53+
}
54+
55+
public static boolean validateUserWildcard(String userWildcard) {
56+
return Regexes.userWildcard.matches(userWildcard);
57+
}
58+
59+
public static boolean validateUser(String user) {
60+
return Regexes.userSet.matches(user) || Regexes.userObject.matches(user) || Regexes.userWildcard.matches(user);
61+
}
62+
63+
public static boolean validateConditionName(String condition) {
64+
return Regexes.condition.matches(condition);
65+
}
66+
67+
public static boolean validateType(String type) {
68+
return Regexes.type.matches(type);
69+
}
70+
71+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package dev.openfga.language.validation;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.function.Function;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
public class ValidationRules {
11+
12+
public void validatedBadStructure(Function<String, Boolean> validate) {
13+
assertFalse(validate.apply("item::1"));
14+
assertFalse(validate.apply(":item:1"));
15+
assertFalse(validate.apply("item:1:"));
16+
assertFalse(validate.apply("item#relation"));
17+
assertFalse(validate.apply("item:1##relation"));
18+
assertFalse(validate.apply("#item:1"));
19+
assertFalse(validate.apply("item:1#"));
20+
assertFalse(validate.apply("ite#m:1"));
21+
assertFalse(validate.apply("it*em:1"));
22+
assertFalse(validate.apply("*:1"));
23+
assertFalse(validate.apply("item*thing"));
24+
assertFalse(validate.apply("item:*thing"));
25+
assertFalse(validate.apply("item:**"));
26+
}
27+
28+
@Test
29+
public void ruleObjectTest() {
30+
31+
// Should pass '<type>:<id>'
32+
assertTrue(Validator.validateObject("document:1"));
33+
34+
// Should fail if no ':' delimiter
35+
assertFalse(Validator.validateObject("document1"));
36+
37+
// Should fail if includes relation
38+
assertFalse(Validator.validateObject("document:1#relation"));
39+
40+
// Validate against bad formats
41+
validatedBadStructure(Validator::validateObject);
42+
}
43+
44+
@Test
45+
public void ruleUserTest() {
46+
47+
// Should pass if UserSet
48+
assertTrue(Validator.validateUser("group:engineering#member"));
49+
50+
// Should pass if UserObject
51+
assertTrue(Validator.validateUser("group:engineering"));
52+
53+
// Should pass if UserWildcard
54+
assertTrue(Validator.validateUser("group:*"));
55+
56+
// Should fail when missing <id>
57+
assertFalse(Validator.validateUser("group"));
58+
59+
// Validate against bad formats
60+
validatedBadStructure(Validator::validateUser);
61+
}
62+
63+
@Test
64+
public void ruleUserSetTest() {
65+
66+
// Should pass if '<type>:<id>#<relation>'
67+
assertTrue(Validator.validateUserSet("group:engineering#member"));
68+
69+
// Shoud fail for 'UserObject'
70+
assertFalse(Validator.validateUserSet("group:engineering"));
71+
72+
// Shoud fail for 'UserWildcard'
73+
assertFalse(Validator.validateUserSet("group:*"));
74+
75+
// Shoud fail if missing '<id>'
76+
assertFalse(Validator.validateUserSet("group"));
77+
78+
validatedBadStructure(Validator::validateUserSet);
79+
}
80+
81+
@Test
82+
public void ruleUserObjectTest() {
83+
84+
// Should pass for '<type>:<id>'
85+
assertTrue(Validator.validateUserObject("group:engineering"));
86+
87+
// Should fail if contains '#'
88+
assertFalse(Validator.validateUserObject("group:engineering#member"));
89+
90+
// Should fail if 'Wildcard' is present
91+
assertFalse(Validator.validateUserObject("group:*"));
92+
93+
// Should fail if missing '<id>'
94+
assertFalse(Validator.validateUserObject("group"));
95+
96+
validatedBadStructure(Validator::validateUserObject);
97+
}
98+
99+
@Test
100+
public void ruleUserWildcardTest() {
101+
102+
// Should pass for '<type>:*'
103+
assertTrue(Validator.validateUserWildcard("group:*"));
104+
105+
// Should fail for '<type>:<id>'
106+
assertFalse(Validator.validateUserWildcard("group:engineering"));
107+
108+
// Should fail if contains '#'
109+
assertFalse(Validator.validateUserWildcard("group:engineering#member"));
110+
111+
// Should fail if missing '*'
112+
assertFalse(Validator.validateUserObject("group"));
113+
114+
validatedBadStructure(Validator::validateUserWildcard);
115+
}
116+
117+
@Test
118+
public void ruleTypeTest() {
119+
120+
// Should pass '<types>'
121+
assertTrue(Validator.validateType("folder"));
122+
123+
// Should ail 'UserObject'
124+
assertFalse(Validator.validateType("folder:1"));
125+
126+
// Should fail UserSet
127+
assertFalse(Validator.validateType("folder:1#relation"));
128+
129+
validatedBadStructure(Validator::validateType);
130+
}
131+
}

0 commit comments

Comments
 (0)