Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3rd party block support #76

Merged
merged 6 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/main/java/com/clevercloud/biscuit/crypto/PublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

import biscuit.format.schema.Schema;
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import com.clevercloud.biscuit.datalog.Scope;
import com.clevercloud.biscuit.error.Error;
import com.clevercloud.biscuit.token.builder.Utils;
import com.google.protobuf.ByteString;
import io.vavr.control.Either;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;

import static com.clevercloud.biscuit.crypto.KeyPair.ed25519;
import static io.vavr.API.Left;
import static io.vavr.API.Right;

public class PublicKey {

Expand Down Expand Up @@ -41,6 +47,21 @@ public PublicKey(Algorithm algorithm, String hex) {
this.algorithm = algorithm;
}

public Schema.PublicKey serialize() {
Schema.PublicKey.Builder publicKey = Schema.PublicKey.newBuilder();
publicKey.setKey(ByteString.copyFrom(this.toBytes()));
publicKey.setAlgorithm(this.algorithm);
return publicKey.build();
}

static public PublicKey deserialize(Schema.PublicKey pk) throws Error.FormatError.DeserializationError {
if(!pk.hasAlgorithm() || !pk.hasKey() || pk.getAlgorithm() != Algorithm.Ed25519) {
throw new Error.FormatError.DeserializationError("Invalid public key");
}

return new PublicKey(pk.getAlgorithm(), pk.getKey().toByteArray());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -55,4 +76,9 @@ public boolean equals(Object o) {
public int hashCode() {
return key.hashCode();
}

@Override
public String toString() {
return "ed25519/" + toHex();
}
}
14 changes: 0 additions & 14 deletions src/main/java/com/clevercloud/biscuit/datalog/AuthorizedWorld.java

This file was deleted.

200 changes: 120 additions & 80 deletions src/main/java/com/clevercloud/biscuit/datalog/Combinator.java
Original file line number Diff line number Diff line change
@@ -1,112 +1,152 @@
package com.clevercloud.biscuit.datalog;

import com.clevercloud.biscuit.datalog.expressions.Expression;
import io.vavr.Tuple2;
import io.vavr.control.Option;

import java.io.Serializable;
import java.util.*;

public final class Combinator implements Serializable {
private final MatchedVariables variables;
private final List<Predicate> next_predicates;
private final Set<Fact> all_facts;
private final Predicate pred;
private final Iterator<Fact> fit;
private Combinator current_it;
import java.util.function.Supplier;
import java.util.stream.Stream;

public final class Combinator implements Serializable, Iterator<Tuple2<Origin, Map<Long, Term>>> {
private MatchedVariables variables;
private final Supplier<Stream<Tuple2<Origin, Fact>>> allFacts;
private final List<Predicate> predicates;
private final Iterator<Tuple2<Origin, Fact>> currentFacts;
private Combinator currentIt;
private final SymbolTable symbols;

public Option<MatchedVariables> next() {
while(true) {
if (this.current_it != null) {
Option<MatchedVariables> next_vars_opt = this.current_it.next();
// the iterator is empty, try with the next fact
if(next_vars_opt.isEmpty()) {
this.current_it = null;
continue;
}
return next_vars_opt;
}
private Origin currentOrigin;

// we iterate over the facts that match the current predicate
if (this.fit.hasNext()) {
final Fact current_fact = this.fit.next();
private Option<Tuple2<Origin, Map<Long, Term>>> nextElement;

// create a new MatchedVariables in which we fix variables we could unify from our first predicate and the current fact
MatchedVariables vars = this.variables.clone();
boolean match_ids = true;
@Override
public boolean hasNext() {
if (this.nextElement != null && this.nextElement.isDefined()) {
return true;
}
this.nextElement = getNext();
return this.nextElement.isDefined();
}

@Override
public Tuple2<Origin, Map<Long, Term>> next() {
if (this.nextElement == null || !this.nextElement.isDefined()) {
this.nextElement = getNext();
}
if (this.nextElement == null || !this.nextElement.isDefined()) {
throw new NoSuchElementException();
} else {
Tuple2<Origin, Map<Long, Term>> t = this.nextElement.get();
this.nextElement = Option.none();
return t;
}
}

// we know the fact matches the predicate's format so they have the same number of terms
// fill the MatchedVariables before creating the next combinator
for (int i = 0; i < pred.terms().size(); ++i) {
final Term id = pred.terms().get(i);
if (id instanceof Term.Variable) {
final long key = ((Term.Variable) id).value();
final Term value = current_fact.predicate().terms().get(i);
public Option<Tuple2<Origin, Map<Long, Term>>> getNext() {
if (this.predicates.isEmpty()) {
final Option<Map<Long, Term>> v_opt = this.variables.complete();
if (v_opt.isEmpty()) {
return Option.none();
} else {
Map<Long, Term> variables = v_opt.get();
// if there were no predicates,
// we should return a value, but only once. To prevent further
// successful calls, we create a set of variables that cannot
// possibly be completed, so the next call will fail
Set<Long> set = new HashSet<>();
set.add((long) 0);

this.variables = new MatchedVariables(set);
return Option.some(new Tuple2<>(new Origin(), variables));
}
}

if (!vars.insert(key, value)) {
match_ids = false;
while (true) {
if (this.currentIt == null) {
Predicate predicate = this.predicates.get(0);

while (true) {
// we iterate over the facts that match the current predicate
if (this.currentFacts.hasNext()) {
final Tuple2<Origin, Fact> t = this.currentFacts.next();
Origin currentOrigin = t._1.clone();
Fact fact = t._2;

// create a new MatchedVariables in which we fix variables we could unify from our first predicate and the current fact
MatchedVariables vars = this.variables.clone();
boolean matchTerms = true;

// we know the fact matches the predicate's format so they have the same number of terms
// fill the MatchedVariables before creating the next combinator
for (int i = 0; i < predicate.terms().size(); ++i) {
final Term term = predicate.terms().get(i);
if (term instanceof Term.Variable) {
final long key = ((Term.Variable) term).value();
final Term value = fact.predicate().terms().get(i);

if (!vars.insert(key, value)) {
matchTerms = false;
}
if (!matchTerms) {
break;
}
}
}
if (!match_ids) {
break;

// the fact did not match the predicate, try the next one
if (!matchTerms) {
continue;
}
}
}

if (!match_ids) {
continue;
}
// there are no more predicates to check
if (this.predicates.size() == 1) {
final Option<Map<Long, Term>> v_opt = vars.complete();
if (v_opt.isEmpty()) {
continue;
} else {
return Option.some(new Tuple2<>(currentOrigin, v_opt.get()));
}
} else {
this.currentOrigin = currentOrigin;
// we found a matching fact, we create a new combinator over the rest of the predicates
// no need to copy all the expressions at all levels
this.currentIt = new Combinator(vars, predicates.subList(1, predicates.size()), this.allFacts, this.symbols);
}
break;

// there are no more predicates to check
if (next_predicates.isEmpty()) {
final Option<Map<Long, Term>> v_opt = vars.complete();
if(v_opt.isEmpty()) {
continue;
} else {
return Option.some(vars);
return Option.none();
}
} else {
// we found a matching fact, we create a new combinator over the rest of the predicates
// no need to copy all of the expressions at all levels
this.current_it = new Combinator(vars, next_predicates, this.all_facts, this.symbols);
}
} else {
break;
}
}

return Option.none();
}

public List<Map<Long, Term>> combine() {
final List<Map<Long, Term>> variables = new ArrayList<>();

while(true) {
Option<MatchedVariables> res = this.next();

if(res.isEmpty()) {
return variables;
if (this.currentIt == null) {
return Option.none();
}

Option<Map<Long, Term>> vars = res.get().complete();
if(vars.isDefined()) {
variables.add(vars.get());
Option<Tuple2<Origin, Map<Long, Term>>> opt = this.currentIt.getNext();

if (opt.isDefined()) {
Tuple2<Origin, Map<Long, Term>> t = opt.get();
return Option.some(new Tuple2<>(t._1.union(currentOrigin), t._2));
} else {
currentOrigin = null;
currentIt = null;
}
}
}


public Combinator(final MatchedVariables variables, final List<Predicate> predicates,
final Set<Fact> all_facts, final SymbolTable symbols) {
Supplier<Stream<Tuple2<Origin, Fact>>> all_facts, final SymbolTable symbols) {
this.variables = variables;
this.all_facts = all_facts;
this.current_it = null;
this.pred = predicates.get(0);
this.fit = all_facts.stream().filter((fact) -> fact.match_predicate(predicates.get(0))).iterator();
this.allFacts = all_facts;
this.currentIt = null;
this.predicates = predicates;
this.currentFacts = all_facts.get().filter((tuple) -> tuple._2.match_predicate(predicates.get(0))).iterator();
this.symbols = symbols;

final List<Predicate> next_predicates = new ArrayList<>();
for (int i = 1; i < predicates.size(); ++i) {
next_predicates.add(predicates.get(i));
}
this.next_predicates = next_predicates;
this.currentOrigin = null;
this.nextElement = null;
}
}
Loading
Loading