All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.biscuitsec.biscuit.datalog.SymbolTable Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
package org.biscuitsec.biscuit.datalog;

import org.biscuitsec.biscuit.crypto.PublicKey;
import org.biscuitsec.biscuit.crypto.TokenSignature;
import org.biscuitsec.biscuit.datalog.expressions.Expression;
import org.biscuitsec.biscuit.token.builder.Utils;
import io.vavr.control.Option;

import java.io.Serializable;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public final class SymbolTable implements Serializable {
    public final static short DEFAULT_SYMBOLS_OFFSET = 1024;

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_INSTANT;

    public String fromEpochIsoDate(long epochSec) {
        return Instant.ofEpochSecond(epochSec).atOffset(ZoneOffset.ofTotalSeconds(0)).format(dateTimeFormatter);
    }

    /**
     * According to the specification,
     * We need two symbols tables:
     * * one for the defaults symbols indexed from 0 et 1023 in defaultSymbols list
     * * one for the usages symbols indexed from 1024 in symbols list
     */
    public final static List defaultSymbols = List.of(
            "read",
            "write",
            "resource",
            "operation",
            "right",
            "time",
            "role",
            "owner",
            "tenant",
            "namespace",
            "user",
            "team",
            "service",
            "admin",
            "email",
            "group",
            "member",
            "ip_address",
            "client",
            "client_ip",
            "domain",
            "path",
            "version",
            "cluster",
            "node",
            "hostname",
            "nonce",
            "query"
    );
    public final List symbols;
    private final List publicKeys;

    public long insert(final String symbol) {
        int index = this.defaultSymbols.indexOf(symbol);
        if (index == -1) {
            index = this.symbols.indexOf(symbol);
            if (index == -1) {
                this.symbols.add(symbol);
                return this.symbols.size() - 1 + DEFAULT_SYMBOLS_OFFSET;
            } else {
                return index + DEFAULT_SYMBOLS_OFFSET;
            }
        } else {
            return index;
        }
    }

    public int currentOffset() {
        return this.symbols.size();
    }
    public int currentPublicKeyOffset() {
        return this.publicKeys.size();
    }

    public List publicKeys() {
        return publicKeys;
    }

    public long insert(final PublicKey publicKey) {
        int index = this.publicKeys.indexOf(publicKey);
        if (index == -1) {
            this.publicKeys.add(publicKey);
            return this.publicKeys.size() - 1;
        } else {
            return index;
        }
    }

    public Term add(final String symbol) {
        return new Term.Str(this.insert(symbol));
    }

    public Option get(final String symbol) {
        // looking for symbol in default symbols
        long index = this.defaultSymbols.indexOf(symbol);
        if (index == -1) {
            // looking for symbol in usages defined symbols
            index = this.symbols.indexOf(symbol);
            if (index == -1) {
                return Option.none();
            } else {
                return Option.some(index + DEFAULT_SYMBOLS_OFFSET);
            }
        } else {
            return Option.some(index);
        }
    }

    public Option get_s(int i) {
        if (i >= 0 && i < this.defaultSymbols.size() && i < DEFAULT_SYMBOLS_OFFSET) {
            return Option.some(this.defaultSymbols.get(i));
        } else if (i >= DEFAULT_SYMBOLS_OFFSET && i < this.symbols.size() + DEFAULT_SYMBOLS_OFFSET) {
            return Option.some(this.symbols.get(i - DEFAULT_SYMBOLS_OFFSET));
        } else {
            return Option.none();
        }
    }

    public Option get_pk(int i) {
        if (i >= 0 && i < this.publicKeys.size()) {
            return Option.some(this.publicKeys.get(i));
        } else {
            return Option.none();
        }
    }

    public String print_rule(final Rule r) {
        String res = this.print_predicate(r.head());
        res += " <- " + this.print_rule_body(r);

        return res;
    }

    public String print_rule_body(final Rule r) {
        final List preds = r.body().stream().map((p) -> this.print_predicate(p)).collect(Collectors.toList());
        final List expressions = r.expressions().stream().map((c) -> this.print_expression(c)).collect(Collectors.toList());

        String res = String.join(", ", preds);
        if (!expressions.isEmpty()) {
            if (!preds.isEmpty()) {
                res += ", ";
            }
            res += String.join(", ", expressions);
        }

        if(!r.scopes().isEmpty()) {
            res += " trusting ";
            final List scopes = r.scopes().stream().map((s) -> this.print_scope(s)).collect(Collectors.toList());
            res += String.join(", ", scopes);
        }
        return res;
    }

    public String print_expression(final Expression e) {
        return e.print(this).get();
    }

    public String print_scope(final Scope scope) {
        switch(scope.kind) {
            case Authority:
                return "authority";
            case Previous:
                return "previous";
            case PublicKey:
                Option pk = this.get_pk((int) scope.publicKey);
                if(pk.isDefined()) {
                    return pk.get().toString();
                }
        }
        return "<"+ scope.publicKey+"?>";
    }

    public String print_predicate(final Predicate p) {
        List ids = p.terms().stream().map((t) -> {
            return this.print_term(t);
        }).collect(Collectors.toList());
        return Optional.ofNullable(this.print_symbol((int) p.name())).orElse("") + "(" + String.join(", ", ids) + ")";
    }

    public String print_term(final Term i) {
        if (i instanceof Term.Variable) {
            return "$" + this.print_symbol((int) ((Term.Variable) i).value());
        } else if(i instanceof Term.Bool) {
            return i.toString();
        } else if (i instanceof Term.Date) {
            return fromEpochIsoDate(((Term.Date) i).value());
        } else if (i instanceof Term.Integer) {
            return "" + ((Term.Integer) i).value();
        } else if (i instanceof Term.Str) {
            return "\"" + this.print_symbol((int) ((Term.Str) i).value()) + "\"";
        } else if (i instanceof Term.Bytes) {
            return "hex:" + Utils.byteArrayToHexString(((Term.Bytes) i).value()).toLowerCase();
        } else if (i instanceof Term.Set) {
            final List values = ((Term.Set) i).value().stream().map((v) -> this.print_term(v)).collect(Collectors.toList());
            return "[" + String.join(", ", values) + "]";
        } else {
            return "???";
        }
    }

    public String print_fact(final Fact f) {
        return this.print_predicate(f.predicate());
    }

    public String print_check(final Check c) {
        String prefix;
        switch (c.kind()) {
            case One:
                prefix = "check if ";
                break;
            case All:
                prefix = "check all ";
                break;
            default:
                prefix = "check if ";
                break;
        }
        final List queries = c.queries().stream().map((q) -> this.print_rule_body(q)).collect(Collectors.toList());
        return prefix + String.join(" or ", queries);
    }

    public String print_world(final World w) {
        final List facts = w.facts().stream().map((f) -> this.print_fact(f)).collect(Collectors.toList());
        final List rules = w.rules().stream().map((r) -> this.print_rule(r)).collect(Collectors.toList());

        StringBuilder b = new StringBuilder();
        b.append("World {\n\tfacts: [\n\t\t");
        b.append(String.join(",\n\t\t", facts));
        b.append("\n\t],\n\trules: [\n\t\t");
        b.append(String.join(",\n\t\t", rules));
        b.append("\n\t]\n}");

        return b.toString();
    }

    public String print_symbol(int i) {
        return get_s(i).getOrElse("<" + i + "?>");
    }

    public SymbolTable() {
        this.symbols = new ArrayList<>();
        this.publicKeys = new ArrayList<>();
    }

    public SymbolTable(SymbolTable s) {
        this.symbols = new ArrayList<>();
        symbols.addAll(s.symbols);
        this.publicKeys = new ArrayList<>();
        publicKeys.addAll(s.publicKeys);
    }

    public SymbolTable(List symbols) {
        this.symbols = new ArrayList<>(symbols);
        this.publicKeys = new ArrayList<>();
    }

    public SymbolTable(List symbols, List publicKeys) {
        this.symbols = new ArrayList<>();
        this.symbols.addAll(symbols);
        this.publicKeys = new ArrayList<>();
        this.publicKeys.addAll(publicKeys);
    }

    public List getAllSymbols() {
        ArrayList allSymbols = new ArrayList<>();
        allSymbols.addAll(defaultSymbols);
        allSymbols.addAll(symbols);
        return allSymbols;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        SymbolTable that = (SymbolTable) o;

        if (!dateTimeFormatter.equals(that.dateTimeFormatter)) return false;
        if (!symbols.equals(that.symbols)) return false;
        return publicKeys.equals(that.publicKeys);
    }

    @Override
    public int hashCode() {
        int result = dateTimeFormatter.hashCode();
        result = 31 * result + symbols.hashCode();
        result = 31 * result + publicKeys.hashCode();
        return result;
    }

    @Override
    public String toString() {
        return "SymbolTable{" +
                "symbols=" + symbols +
                ", publicKeys=" + publicKeys +
                '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy