Skip to content

Commit 2664308

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

File tree

8 files changed

+449
-45
lines changed

8 files changed

+449
-45
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: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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+
t.Helper()
11+
12+
assert.False(t, validator("item::1"))
13+
assert.False(t, validator(":item:1"))
14+
assert.False(t, validator("item:1:"))
15+
assert.False(t, validator("item#relation"))
16+
assert.False(t, validator("item:1##relation"))
17+
assert.False(t, validator("#item:1"))
18+
assert.False(t, validator("item:1#"))
19+
assert.False(t, validator("ite#m:1"))
20+
assert.False(t, validator("it*em:1"))
21+
assert.False(t, validator("*:1"))
22+
assert.False(t, validator("item*thing"))
23+
assert.False(t, validator("item:*thing"))
24+
assert.False(t, validator("item:**"))
25+
}
26+
27+
func TestValidateObject(t *testing.T) {
28+
t.Parallel()
29+
30+
// Should pass '<type>:<id>'
31+
assert.True(t, ValidateObject("document:1"))
32+
33+
// Should fail if no ':' delimiter
34+
assert.False(t, ValidateObject("document1"))
35+
36+
// Should fail if includes relation
37+
assert.False(t, ValidateObject("document:1#relation"))
38+
39+
// Should fail if the id is '*'
40+
assert.False(t, ValidateObject("document:*"))
41+
42+
validateBadStructure(t, ValidateObject)
43+
}
44+
45+
func TestValidateUserTest(t *testing.T) {
46+
t.Parallel()
47+
48+
// Should pass if UserSet
49+
assert.True(t, ValidateUser("group:engineering#member"))
50+
51+
// Should pass if UserObject
52+
assert.True(t, ValidateUser("group:engineering"))
53+
54+
// Should pass if UserWildcard
55+
assert.True(t, ValidateUser("group:*"))
56+
57+
// Should fail when missing <id>
58+
assert.False(t, ValidateUser("group"))
59+
60+
validateBadStructure(t, ValidateUser)
61+
}
62+
63+
func TestValidatorUserSet(t *testing.T) {
64+
t.Parallel()
65+
66+
// Should pass if '<type>:<id>#<relation>'
67+
assert.True(t, ValidateUserSet("group:engineering#member"))
68+
69+
// Shoud fail for 'UserObject'
70+
assert.False(t, ValidateUserSet("group:engineering"))
71+
72+
// Shoud fail for 'UserWildcard'
73+
assert.False(t, ValidateUserSet("group:*"))
74+
75+
// Shoud fail if missing '<id>'
76+
assert.False(t, ValidateUserSet("group"))
77+
78+
validateBadStructure(t, ValidateUserSet)
79+
}
80+
81+
func TestValidatorUserObject(t *testing.T) {
82+
t.Parallel()
83+
84+
// Should pass for '<type>:<id>'
85+
assert.True(t, ValidateUserObject("group:engineering"))
86+
87+
// Should fail if contains '#'
88+
assert.False(t, ValidateUserObject("group:engineering#member"))
89+
90+
// Should fail if 'Wildcard' is present
91+
assert.False(t, ValidateUserObject("group:*"))
92+
93+
// Should fail if missing '<id>'
94+
assert.False(t, ValidateUserObject("group"))
95+
96+
validateBadStructure(t, ValidateUserObject)
97+
}
98+
99+
func TestValidatorUserWildcard(t *testing.T) {
100+
t.Parallel()
101+
102+
// Should pass for '<type>:*'
103+
assert.True(t, ValidateUserWildcard("group:*"))
104+
105+
// Should fail for '<type>:<id>'
106+
assert.False(t, ValidateUserWildcard("group:engineering"))
107+
108+
// Should fail if contains '#'
109+
assert.False(t, ValidateUserWildcard("group:engineering#member"))
110+
111+
// Should fail if missing '*'
112+
assert.False(t, ValidateUserObject("group"))
113+
114+
validateBadStructure(t, ValidateUserWildcard)
115+
}
116+
117+
func TestValidatorType(t *testing.T) {
118+
t.Parallel()
119+
120+
// Should pass '<types>'
121+
assert.True(t, ValidateType("folder"))
122+
123+
// Should ail 'UserObject'
124+
assert.False(t, ValidateType("folder:1"))
125+
126+
// Should fail UserSet
127+
assert.False(t, ValidateType("folder:1#relation"))
128+
129+
validateBadStructure(t, ValidateType)
130+
}

pkg/java/src/main/java/dev/openfga/language/validation/ValidationOptions.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package dev.openfga.language.validation;
22

3-
public class ValidationOptions {
3+
import dev.openfga.language.validation.Validator.Rules;
44

5-
private static final String DEFAULT_TYPE_PATTERN = "^[^:#@\\*\\s]{1,254}$";
6-
private static final String DEFAULT_RELATION_PATTERN = "^[^:#@\\*\\s]{1,50}$";
5+
public class ValidationOptions {
76

8-
private String typePattern = DEFAULT_TYPE_PATTERN;
9-
private String relationPattern = DEFAULT_RELATION_PATTERN;
7+
private String typePattern = String.format("^%s$", Rules.TYPE);
8+
private String relationPattern = String.format("^%s$", Rules.RELATION);
109

1110
public String getTypePattern() {
1211
return typePattern;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 =
15+
ValidationRegex.build("object", String.format("^%s$", Rules.OBJECT));
16+
17+
public static final ValidationRegex typeId =
18+
ValidationRegex.build("object", String.format("^%s:%s$", Rules.TYPE, Rules.ID));
19+
20+
public static final ValidationRegex relation =
21+
ValidationRegex.build("relation", String.format("^%s$", Rules.RELATION));
22+
23+
public static final ValidationRegex userSet =
24+
ValidationRegex.build("userSet", String.format("^%s:%s#%s$", Rules.TYPE, Rules.ID, Rules.RELATION));
25+
26+
public static final ValidationRegex userObject =
27+
ValidationRegex.build("userObject", String.format("^%s:%s$", Rules.TYPE, Rules.ID));
28+
29+
public static final ValidationRegex userWildcard =
30+
ValidationRegex.build("userWildcard", String.format("^%s:\\*$", Rules.TYPE));
31+
32+
public static final ValidationRegex condition =
33+
ValidationRegex.build("condition", String.format("^%s$", Rules.CONDITION));
34+
35+
public static final ValidationRegex type =
36+
ValidationRegex.build("condition", 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+
}

0 commit comments

Comments
 (0)