Skip to content

Commit 5f4bb07

Browse files
authored
Merge pull request #137 from preuss-adam/apreuss/datalog-3.3
Update to support Datalog version 3.3.
2 parents 8675b8a + 01dc4c2 commit 5f4bb07

39 files changed

Lines changed: 4291 additions & 1011 deletions

src/main/java/org/eclipse/biscuit/datalog/Check.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.eclipse.biscuit.datalog;
77

88
import static biscuit.format.schema.Schema.CheckV2.Kind.All;
9+
import static biscuit.format.schema.Schema.CheckV2.Kind.Reject;
910

1011
import biscuit.format.schema.Schema;
1112
import java.util.ArrayList;
@@ -17,7 +18,8 @@
1718
public final class Check {
1819
public enum Kind {
1920
ONE,
20-
ALL
21+
ALL,
22+
REJECT
2123
}
2224

2325
private static final int HASH_CODE_SEED = 31;
@@ -47,6 +49,9 @@ public Schema.CheckV2 serialize() {
4749
case ALL:
4850
b.setKind(All);
4951
break;
52+
case REJECT:
53+
b.setKind(Reject);
54+
break;
5055
default:
5156
}
5257

@@ -68,6 +73,9 @@ public static Result<Check, Error.FormatError> deserializeV2(Schema.CheckV2 chec
6873
case All:
6974
kind = Kind.ALL;
7075
break;
76+
case Reject:
77+
kind = Kind.REJECT;
78+
break;
7179
default:
7280
kind = Kind.ONE;
7381
break;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.eclipse.biscuit.datalog;
2+
3+
import biscuit.format.schema.Schema;
4+
import org.eclipse.biscuit.error.Error;
5+
import org.eclipse.biscuit.error.Result;
6+
7+
public abstract class MapKey extends Term {
8+
public abstract Schema.MapKey serializeMapKey();
9+
10+
public abstract org.eclipse.biscuit.token.builder.MapKey toMapKey(SymbolTable symbolTable);
11+
12+
public static Result<MapKey, Error.FormatError> deserializeMapKeyEnum(Schema.MapKey mapKey) {
13+
if (mapKey.hasInteger()) {
14+
return Term.Integer.deserializeMapKey(mapKey);
15+
} else if (mapKey.hasString()) {
16+
return Term.Str.deserializeMapKey(mapKey);
17+
} else {
18+
return Result.err(new Error.FormatError.DeserializationError("invalid MapKey kind"));
19+
}
20+
}
21+
}

src/main/java/org/eclipse/biscuit/datalog/Predicate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public Schema.PredicateV2 serialize() {
9090
Schema.PredicateV2.Builder builder = Schema.PredicateV2.newBuilder().setName(this.name);
9191

9292
for (int i = 0; i < this.terms.size(); i++) {
93-
builder.addTerms(this.terms.get(i).serialize());
93+
builder.addTerms(this.terms.get(i).serializeTerm());
9494
}
9595

9696
return builder.build();

src/main/java/org/eclipse/biscuit/datalog/SchemaVersion.java

Lines changed: 124 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,87 +6,107 @@
66
package org.eclipse.biscuit.datalog;
77

88
import java.util.List;
9+
import java.util.Optional;
10+
import org.eclipse.biscuit.crypto.PublicKey;
911
import org.eclipse.biscuit.datalog.expressions.Expression;
1012
import org.eclipse.biscuit.datalog.expressions.Op;
1113
import org.eclipse.biscuit.error.Error;
1214
import org.eclipse.biscuit.error.Result;
1315
import org.eclipse.biscuit.token.format.SerializedBiscuit;
1416

1517
public final class SchemaVersion {
16-
private boolean containsScopes;
17-
private boolean containsCheckAll;
18-
private boolean containsV4;
18+
private final boolean containsScopes;
19+
private final boolean containsV31;
20+
private final boolean containsCheckAll;
21+
private final boolean containsV33;
22+
private final boolean containsExternalKey;
1923

20-
public SchemaVersion(List<Fact> facts, List<Rule> rules, List<Check> checks, List<Scope> scopes) {
21-
containsScopes = !scopes.isEmpty();
24+
public SchemaVersion(
25+
List<Fact> facts,
26+
List<Rule> rules,
27+
List<Check> checks,
28+
List<Scope> scopes,
29+
Optional<PublicKey> externalKey) {
30+
containsScopes =
31+
!scopes.isEmpty()
32+
|| rules.stream().anyMatch(r -> !r.scopes().isEmpty())
33+
|| checks.stream()
34+
.anyMatch(c -> c.queries().stream().anyMatch(q -> !q.scopes().isEmpty()));
2235

23-
if (!containsScopes) {
24-
for (Rule r : rules) {
25-
if (!r.scopes().isEmpty()) {
26-
containsScopes = true;
27-
break;
28-
}
29-
}
30-
}
31-
if (!containsScopes) {
32-
for (Check check : checks) {
33-
for (Rule query : check.queries()) {
34-
if (!query.scopes().isEmpty()) {
35-
containsScopes = true;
36-
break;
37-
}
38-
}
39-
}
40-
}
36+
containsCheckAll = checks.stream().anyMatch(c -> c.kind() == Check.Kind.ALL);
4137

42-
containsCheckAll = false;
43-
for (Check check : checks) {
44-
if (check.kind() == Check.Kind.ALL) {
45-
containsCheckAll = true;
46-
break;
47-
}
48-
}
38+
containsV31 =
39+
rules.stream().anyMatch(r -> containsV31Op(r.expressions()))
40+
|| checks.stream()
41+
.anyMatch(c -> c.queries().stream().anyMatch(q -> containsV31Op(q.expressions())));
4942

50-
containsV4 = false;
51-
for (Check check : checks) {
52-
for (Rule query : check.queries()) {
53-
if (containsV4Ops(query.expressions())) {
54-
containsV4 = true;
55-
break;
56-
}
57-
}
58-
}
43+
containsV33 =
44+
checks.stream().anyMatch(c -> c.kind() == Check.Kind.REJECT)
45+
|| rules.stream()
46+
.anyMatch(
47+
r ->
48+
containsV33Predicate(r.head())
49+
|| r.body().stream().anyMatch(SchemaVersion::containsV33Predicate)
50+
|| containsV33Op(r.expressions()))
51+
|| checks.stream()
52+
.anyMatch(
53+
c ->
54+
c.queries().stream()
55+
.anyMatch(
56+
q ->
57+
q.body().stream().anyMatch(SchemaVersion::containsV33Predicate)
58+
|| containsV33Op(q.expressions())))
59+
|| facts.stream().anyMatch(f -> containsV33Predicate(f.predicate()));
60+
61+
containsExternalKey = externalKey.isPresent();
5962
}
6063

6164
public int version() {
62-
if (containsScopes || containsV4 || containsCheckAll) {
63-
return 4;
64-
} else {
65-
return SerializedBiscuit.MIN_SCHEMA_VERSION;
65+
if (containsV33) {
66+
return SerializedBiscuit.DATALOG_3_3;
67+
}
68+
if (containsExternalKey) {
69+
return SerializedBiscuit.DATALOG_3_2;
6670
}
71+
if (containsScopes || containsV31 || containsCheckAll) {
72+
return SerializedBiscuit.DATALOG_3_1;
73+
}
74+
return SerializedBiscuit.MIN_SCHEMA_VERSION;
6775
}
6876

6977
public Result<Void, Error.FormatError> checkCompatibility(int version) {
70-
if (version < 4) {
78+
if (version < SerializedBiscuit.DATALOG_3_1) {
7179
if (containsScopes) {
7280
return Result.err(
73-
new Error.FormatError.DeserializationError("v3 blocks must not have scopes"));
81+
new Error.FormatError.DeserializationError(
82+
"scopes are only supported in datalog v3.1+"));
7483
}
75-
if (containsV4) {
84+
if (containsV31) {
7685
return Result.err(
7786
new Error.FormatError.DeserializationError(
78-
"v3 blocks must not have v4 operators (bitwise operators or !="));
87+
"bitwise operators and != are only supported in datalog v3.1+"));
7988
}
8089
if (containsCheckAll) {
8190
return Result.err(
82-
new Error.FormatError.DeserializationError("v3 blocks must not use check all"));
91+
new Error.FormatError.DeserializationError(
92+
"check all is only supported in datalog v3.1+"));
8393
}
8494
}
95+
if (version < SerializedBiscuit.DATALOG_3_2 && containsExternalKey) {
96+
return Result.err(
97+
new Error.FormatError.DeserializationError(
98+
"third-party blocks are only supported in datalog v3.2+"));
99+
}
85100

101+
if (version < SerializedBiscuit.DATALOG_3_3 && containsV33) {
102+
return Result.err(
103+
new Error.FormatError.DeserializationError(
104+
"maps, arrays, null, closures are only supported in datalog v3.3+"));
105+
}
86106
return Result.ok(null);
87107
}
88108

89-
public static boolean containsV4Ops(List<Expression> expressions) {
109+
private static boolean containsV31Op(List<Expression> expressions) {
90110
for (Expression e : expressions) {
91111
for (Op op : e.getOps()) {
92112
if (op instanceof Op.Binary) {
@@ -104,4 +124,59 @@ public static boolean containsV4Ops(List<Expression> expressions) {
104124
}
105125
return false;
106126
}
127+
128+
private static boolean containsV33Op(List<Expression> expressions) {
129+
for (Expression e : expressions) {
130+
for (Op op : e.getOps()) {
131+
if (op instanceof Op.Unary) {
132+
Op.Unary b = (Op.Unary) op;
133+
switch (b.getOp()) {
134+
case TypeOf:
135+
return true;
136+
default:
137+
}
138+
} else if (op instanceof Op.Binary) {
139+
Op.Binary b = (Op.Binary) op;
140+
switch (b.getOp()) {
141+
case HeterogeneousEqual:
142+
case HeterogeneousNotEqual:
143+
case LazyAnd:
144+
case LazyOr:
145+
case Get:
146+
case Any:
147+
case All:
148+
case TryOr:
149+
return true;
150+
default:
151+
}
152+
} else if (op instanceof Op.Closure) {
153+
return true;
154+
} else if (op instanceof Term.Null) {
155+
return true;
156+
} else if (op instanceof Term.Array) {
157+
return true;
158+
} else if (op instanceof Term.Map) {
159+
return true;
160+
}
161+
}
162+
}
163+
return false;
164+
}
165+
166+
private static boolean containsV33Predicate(Predicate predicate) {
167+
return predicate.terms().stream().anyMatch(SchemaVersion::containsV33Term);
168+
}
169+
170+
private static boolean containsV33Term(Term term) {
171+
if (term instanceof Term.Null) {
172+
return true;
173+
}
174+
if (term instanceof Term.Array) {
175+
return true;
176+
}
177+
if (term instanceof Term.Map) {
178+
return true;
179+
}
180+
return false;
181+
}
107182
}

src/main/java/org/eclipse/biscuit/datalog/SymbolTable.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.ArrayList;
1313
import java.util.Collections;
1414
import java.util.List;
15+
import java.util.Map;
1516
import java.util.Optional;
1617
import java.util.stream.Collectors;
1718
import org.eclipse.biscuit.crypto.PublicKey;
@@ -228,7 +229,26 @@ public String formatTerm(final Term i) {
228229
final List<String> values =
229230
((Term.Set) i)
230231
.value().stream().map((v) -> this.formatTerm(v)).collect(Collectors.toList());
232+
if (values.isEmpty()) {
233+
return "{,}";
234+
} else {
235+
Collections.sort(values);
236+
return "{" + String.join(", ", values) + "}";
237+
}
238+
} else if (i instanceof Term.Null) {
239+
return "null";
240+
} else if (i instanceof Term.Array) {
241+
final List<String> values =
242+
((Term.Array) i)
243+
.value().stream().map((v) -> this.formatTerm(v)).collect(Collectors.toList());
231244
return "[" + String.join(", ", values) + "]";
245+
} else if (i instanceof Term.Map) {
246+
ArrayList<String> entries = new ArrayList();
247+
for (Map.Entry<MapKey, Term> entry : ((Term.Map) i).value().entrySet()) {
248+
entries.add(formatTerm(entry.getKey()) + ": " + formatTerm(entry.getValue()));
249+
}
250+
Collections.sort(entries);
251+
return "{" + String.join(", ", entries) + "}";
232252
} else {
233253
return "???";
234254
}
@@ -247,6 +267,9 @@ public String formatCheck(final Check c) {
247267
case ALL:
248268
prefix = "check all ";
249269
break;
270+
case REJECT:
271+
prefix = "reject if ";
272+
break;
250273
default:
251274
prefix = "check if ";
252275
break;

0 commit comments

Comments
 (0)