Skip to content

Commit

Permalink
wip: add block comparison between sample definition and its serializa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
KannarFr committed Apr 9, 2024
1 parent 76fa45e commit fb4ae04
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 20 deletions.
8 changes: 7 additions & 1 deletion src/main/java/org/biscuitsec/biscuit/token/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,16 @@ public String print(SymbolTable symbol_table) {
s.append(this.symbols.symbols);
s.append("\n\t\tcontext: ");
s.append(this.context);
s.append("\n\t\tscopes: [");
for (Scope scope : this.scopes) {
s.append("\n\t\t\t");
s.append(symbol_table.print_scope(scope));
}
if(this.externalKey.isDefined()) {
s.append("\n\t\texternal key: ");
s.append(this.externalKey.get().toString());
}
s.append("\n\t\tfacts: [");
s.append("\n\t\t]\n\t\tfacts: [");
for (Fact f : this.facts) {
s.append("\n\t\t\t");
s.append(symbol_table.print_fact(f));
Expand Down Expand Up @@ -208,6 +213,7 @@ static public Either<Error.FormatError, Block> deserialize(Schema.Block b, Optio
}

ArrayList<Scope> scopes = new ArrayList<>();
System.out.println(b.getScopeList());
for (Schema.Scope scope: b.getScopeList()) {
Either<Error.FormatError, Scope> res = Scope.deserialize(scope);
if(res.isLeft()) {
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/biscuitsec/biscuit/token/builder/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ public org.biscuitsec.biscuit.token.Block build() {
publicKeys.add(this.symbols.publicKeys().get(i));
}

publicKeys.addAll(this.publicKeys);

SchemaVersion schemaVersion = new SchemaVersion(this.facts, this.rules, this.checks, this.scopes);

return new org.biscuitsec.biscuit.token.Block(symbols, this.context, this.facts, this.rules, this.checks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import java.util.function.Function;

public class Parser {
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, String s) {
return datalog(index, baseSymbols, null, s);
}

/**
* Takes a datalog string with <code>\n</code> as datalog line separator. It tries to parse
* each line using fact, rule, check and scope sequentially.
Expand All @@ -25,24 +29,37 @@ public class Parser {
*
* @param index block index
* @param baseSymbols symbols table
* @param blockSymbols block's custom symbols table (added to baseSymbols)
* @param s datalog string to parse
* @return Either<Map<Integer, List<Error>>, Block>
*/
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, String s) {
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, SymbolTable blockSymbols, String s) {
Block blockBuilder = new Block(index, baseSymbols);

// empty block code
if (s.isEmpty()) {
return Either.right(blockBuilder);
}

if (blockSymbols != null) {
blockSymbols.symbols.forEach(blockBuilder::addSymbol);
}

Map<Integer, List<Error>> errors = new HashMap<>();

Stream.of(s.split("\n")).zipWithIndex().forEach(indexedLine -> {
Stream.of(s.replace(";", "").replace("\\\"","\"").split("\n")).zipWithIndex().forEach(indexedLine -> {
Integer lineNumber = indexedLine._2;
String codeLine = indexedLine._1;
List<Error> lineErrors = new ArrayList<>();

fact(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_fact);
rule(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_rule);
check(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_check);
scope(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_scope);
scope(codeLine).bimap(lineErrors::add, r -> r._2).forEach(blockBuilder::add_scope);
fact(codeLine).bimap(lineErrors::add, r -> r._2).forEach(blockBuilder::add_fact);
rule(codeLine).bimap(lineErrors::add, r -> r._2).forEach(blockBuilder::add_rule);
check(codeLine).bimap(lineErrors::add, r -> r._2).forEach(blockBuilder::add_check);

if (lineErrors.size() > 3) {
System.out.println(codeLine);
System.out.println(lineErrors.get(lineErrors.size() - 1));
errors.put(lineNumber, lineErrors);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ void testParens() throws org.biscuitsec.biscuit.error.Error.Execution {
void testDatalogSucceeds() throws org.biscuitsec.biscuit.error.Error.Parser {
SymbolTable symbols = Biscuit.default_symbol_table();

String l1 = "fact1(1)";
String l1 = "fact1(1, 2)";
String l2 = "fact2(\"2\")";
String l3 = "rule1(2) <- fact2(\"2\")";
String l4 = "check if rule1(2)";
Expand Down
98 changes: 88 additions & 10 deletions src/test/java/org/biscuitsec/biscuit/token/SamplesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
import com.google.gson.*;
import com.google.protobuf.MapEntry;
import io.vavr.Tuple2;
import io.vavr.control.Option;
import org.biscuitsec.biscuit.crypto.KeyPair;
import org.biscuitsec.biscuit.crypto.PublicKey;
import org.biscuitsec.biscuit.datalog.Rule;
import org.biscuitsec.biscuit.datalog.RunLimits;
import org.biscuitsec.biscuit.datalog.SymbolTable;
import org.biscuitsec.biscuit.datalog.TrustedOrigins;
import org.biscuitsec.biscuit.error.Error;
import io.vavr.control.Either;
import io.vavr.control.Try;
import org.biscuitsec.biscuit.token.builder.Check;
import org.biscuitsec.biscuit.token.builder.Expression;
import org.biscuitsec.biscuit.token.builder.parser.ExpressionParser;
import org.biscuitsec.biscuit.token.builder.parser.Parser;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

Expand All @@ -22,6 +27,7 @@
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -39,6 +45,48 @@ Stream<DynamicTest> jsonTest() {
return sample.testcases.stream().map(t -> process_testcase(t, publicKey, keyPair));
}

void compareBlocks(List<Block> sampleBlocks, List<org.biscuitsec.biscuit.token.Block> blocks) {
assertEquals(sampleBlocks.size(), blocks.size());
List<Tuple2<Block, org.biscuitsec.biscuit.token.Block>> comparisonList = IntStream.range(0, sampleBlocks.size())
.mapToObj(i -> new Tuple2<>(sampleBlocks.get(i), blocks.get(i)))
.collect(Collectors.toList());

// for each token we start from the base symbol table
SymbolTable baseSymbols = new SymbolTable();

io.vavr.collection.Stream.ofAll(comparisonList).zipWithIndex().forEach(indexedItem -> {
compareBlock(baseSymbols, indexedItem._2, indexedItem._1._1, indexedItem._1._2);
});
}

void compareBlock(SymbolTable baseSymbols, long sampleBlockIndex, Block sampleBlock, org.biscuitsec.biscuit.token.Block block) {
Option<PublicKey> sampleExternalKey = sampleBlock.getExternalKey();
List<PublicKey> samplePublicKeys = sampleBlock.getPublicKeys();
String sampleDatalog = sampleBlock.getCode().replace("\"","\\\"");
SymbolTable sampleSymbols = new SymbolTable(sampleBlock.symbols);

Either<Map<Integer, List<org.biscuitsec.biscuit.token.builder.parser.Error>>, org.biscuitsec.biscuit.token.builder.Block> outputSample = Parser.datalog(sampleBlockIndex, baseSymbols, sampleDatalog);
assertTrue(outputSample.isRight());

if (!block.publicKeys.isEmpty()) {
outputSample.get().addPublicKeys(samplePublicKeys);
}

if (!block.externalKey.isDefined()) {
sampleSymbols.symbols.forEach(baseSymbols::add);
} else {
SymbolTable freshSymbols = new SymbolTable();
sampleSymbols.symbols.forEach(freshSymbols::add);
outputSample.get().setExternalKey(sampleExternalKey);
}

System.out.println("mdr");
System.out.println(outputSample.get().build().print(sampleSymbols));
System.out.println(block.symbols.symbols);
System.out.println(block.print(sampleSymbols));
assertArrayEquals(outputSample.get().build().to_bytes().get(), block.to_bytes().get());
}

DynamicTest process_testcase(final TestCase testCase, final PublicKey publicKey, final KeyPair privateKey) {
return DynamicTest.dynamicTest(testCase.title + ": "+testCase.filename, () -> {
System.out.println("Testcase name: \""+testCase.title+"\"");
Expand All @@ -57,6 +105,12 @@ DynamicTest process_testcase(final TestCase testCase, final PublicKey publicKey,
Biscuit token = Biscuit.from_bytes(data, publicKey);
assertArrayEquals(token.serialize(), data);

List<org.biscuitsec.biscuit.token.Block> allBlocks = new ArrayList<>();
allBlocks.add(token.authority);
allBlocks.addAll(token.blocks);

compareBlocks(testCase.token, allBlocks);

List<RevocationIdentifier> revocationIds = token.revocation_identifiers();
JsonArray validationRevocationIds = validation.getAsJsonArray("revocation_ids");
assertEquals(revocationIds.size(), validationRevocationIds.size());
Expand Down Expand Up @@ -142,19 +196,11 @@ DynamicTest process_testcase(final TestCase testCase, final PublicKey publicKey,
});
}


class Block {
List<String> symbols;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

String code;
List<String> public_keys;
String external_key;

public List<String> getSymbols() {
return symbols;
Expand All @@ -163,6 +209,38 @@ public List<String> getSymbols() {
public void setSymbols(List<String> symbols) {
this.symbols = symbols;
}

public String getCode() { return code; }

public void setCode(String code) { this.code = code; }

public List<PublicKey> getPublicKeys() {
return this.public_keys.stream()
.map(pk -> {
String[] parts = pk.split("/");
return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, parts[1]);
})
.collect(Collectors.toList());
}

public void setPublicKeys(List<PublicKey> publicKeys) {
this.public_keys = publicKeys.stream()
.map(PublicKey::toString)
.collect(Collectors.toList());
}

public Option<PublicKey> getExternalKey() {
if (this.external_key != null) {
String[] parts = this.external_key.split("/");
return Option.of(new PublicKey(Schema.PublicKey.Algorithm.Ed25519, parts[1]));
} else {
return Option.none();
}
}

public void setExternalKey(Option<PublicKey> externalKey) {
this.external_key = externalKey.map(PublicKey::toString).getOrElse((String) null);
}
}

class TestCase {
Expand Down

0 comments on commit fb4ae04

Please sign in to comment.