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

Origins + more test suite passing #7

Merged
merged 47 commits into from
Mar 29, 2024
Merged
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
190fd93
WIP
malcolmstill Mar 19, 2024
ec48aff
Working test001
malcolmstill Mar 19, 2024
bb0942f
Initial parser
malcolmstill Mar 19, 2024
525d888
Fix parameter name
malcolmstill Mar 20, 2024
40880e4
More illuminating name
malcolmstill Mar 21, 2024
d876d4c
WIP parser
malcolmstill Mar 23, 2024
10432f2
WIP parser
malcolmstill Mar 23, 2024
e7dc323
WIP parser
malcolmstill Mar 23, 2024
7e07bf5
Test 010 passes
malcolmstill Mar 23, 2024
185e678
Test 012 passes
malcolmstill Mar 23, 2024
521bbb9
Test 022 works
malcolmstill Mar 23, 2024
04da1de
Convert bytes and set
malcolmstill Mar 23, 2024
fad0623
Some RFC 3339 support
malcolmstill Mar 24, 2024
7acf870
WIP date
malcolmstill Mar 24, 2024
9d44302
WIP expressions
malcolmstill Mar 24, 2024
59011f7
WIP expression parser
malcolmstill Mar 24, 2024
f5c7dc1
Working expression parse
malcolmstill Mar 24, 2024
b17b962
Fix
malcolmstill Mar 24, 2024
9faa7d8
WIP
malcolmstill Mar 24, 2024
e36ccc6
WIP
malcolmstill Mar 25, 2024
0df1e74
WIP
malcolmstill Mar 25, 2024
e19e375
RuleSet / FactSet
malcolmstill Mar 25, 2024
3d061f8
Trusted facts compiles but does not work
malcolmstill Mar 25, 2024
2524056
Still not working
malcolmstill Mar 26, 2024
9f9fa9d
WIP
malcolmstill Mar 26, 2024
1959667
Fixed iterator (need to be pointers to FactSet, not FactSet by value)…
malcolmstill Mar 26, 2024
ec38967
WIP
malcolmstill Mar 26, 2024
2fd0b47
WIP
malcolmstill Mar 26, 2024
bb37f8c
I have a feeling we shouldn't think about TrustedOrigins as containin…
malcolmstill Mar 26, 2024
283109b
Fix Term.convert
malcolmstill Mar 26, 2024
710f96c
Rule.findMatch: evalute expressions where rule has no body predicates
malcolmstill Mar 26, 2024
9cb5ba4
Parse external key out of block
malcolmstill Mar 26, 2024
d1d7f3b
Verify external signature
malcolmstill Mar 26, 2024
a92c5b0
Test 027 passes
malcolmstill Mar 26, 2024
25ea0f0
More scope stuff
malcolmstill Mar 27, 2024
0aa6dd0
Test 024 third pary works
malcolmstill Mar 28, 2024
fd7aed5
Fix test 025
malcolmstill Mar 28, 2024
3b29527
Partial fix fo r test 026
malcolmstill Mar 28, 2024
286621b
Implement policies / builder expression conversion
malcolmstill Mar 28, 2024
b029049
Fix parser + working test 026...this required some additional mapping…
malcolmstill Mar 28, 2024
5ca2107
Depend on zig-regex + working test 014
malcolmstill Mar 28, 2024
270e63c
Concat
malcolmstill Mar 28, 2024
460f826
Update the samples...eveything now works apart from test 018
malcolmstill Mar 28, 2024
a5a06c0
Latest samples.json + required sample.zig changes
malcolmstill Mar 28, 2024
26028a7
Yay, test018 passes...all the tests pass!
malcolmstill Mar 28, 2024
f8bb0f3
Fix double free
malcolmstill Mar 28, 2024
58cfd30
Let's get all module tests running again
malcolmstill Mar 28, 2024
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
Prev Previous commit
Next Next commit
Test 010 passes
malcolmstill committed Mar 23, 2024
commit 7e07bf5526664f7ce49034a20441c38ca569aecd
31 changes: 29 additions & 2 deletions biscuit-builder/src/check.zig
Original file line number Diff line number Diff line change
@@ -8,7 +8,34 @@ pub const Check = struct {
kind: datalog.Check.Kind,
queries: std.ArrayList(Rule),

pub fn deinit(_: Check) void {
//
pub fn deinit(check: Check) void {
for (check.queries.items) |query| {
query.deinit();
}

check.queries.deinit();
}

pub fn convert(check: Check, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Check {
var queries = std.ArrayList(datalog.Rule).init(allocator);

for (check.queries.items) |query| {
try queries.append(try query.convert(allocator, symbols));
}

return .{ .kind = check.kind, .queries = queries };
}

pub fn format(check: Check, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
try writer.print("check ", .{});

switch (check.kind) {
.one => try writer.print("if", .{}),
.all => try writer.print("all", .{}),
}

for (check.queries.items) |query| {
try writer.print(" {any}", .{query});
}
}
};
2 changes: 1 addition & 1 deletion biscuit-builder/src/expression.zig
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ const Term = @import("term.zig").Term;

pub const Expression = struct {
/// convert to datalog fact
pub fn convert(_: Expression) datalog.Expression {
pub fn convert(_: Expression, _: std.mem.Allocator, _: *datalog.SymbolTable) !datalog.Expression {
unreachable;
}
};
35 changes: 33 additions & 2 deletions biscuit-builder/src/rule.zig
Original file line number Diff line number Diff line change
@@ -25,7 +25,38 @@ pub const Rule = struct {
}

/// convert to datalog predicate
pub fn convert(_: Rule) datalog.Rule {
unreachable;
pub fn convert(rule: Rule, allocator: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Rule {
const head = try rule.head.convert(allocator, symbols);

var body = std.ArrayList(datalog.Predicate).init(allocator);
var expressions = std.ArrayList(datalog.Expression).init(allocator);
var scopes = std.ArrayList(datalog.Scope).init(allocator);

for (rule.body.items) |predicate| {
try body.append(try predicate.convert(allocator, symbols));
}

for (rule.expressions.items) |expression| {
try expressions.append(try expression.convert(allocator, symbols));
}

for (rule.scopes.items) |scope| {
try scopes.append(try scope.convert(allocator, symbols));
}

return .{
.head = head,
.body = body,
.expressions = expressions,
.scopes = scopes,
};
}

pub fn format(rule: Rule, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
try writer.print("{any} <- ", .{rule.head});
for (rule.body.items, 0..) |*predicate, i| {
try writer.print("{any}", .{predicate.*});
if (i < rule.body.items.len - 1) try writer.print(", ", .{});
}
}
};
2 changes: 1 addition & 1 deletion biscuit-builder/src/scope.zig
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ pub const Scope = struct {
public_key: Ed25519.PublicKey,

/// convert to datalog fact
pub fn convert(_: Scope) datalog.Scope {
pub fn convert(_: Scope, _: std.mem.Allocator, _: *datalog.SymbolTable) !datalog.Scope {
unreachable;
}
};
11 changes: 11 additions & 0 deletions biscuit-builder/src/term.zig
Original file line number Diff line number Diff line change
@@ -2,20 +2,31 @@ const std = @import("std");
const datalog = @import("biscuit-datalog");

const TermTag = enum(u8) {
variable,
string,
bool,
};

pub const Term = union(TermTag) {
variable: []const u8,
string: []const u8,
bool: bool,

pub fn deinit(_: Term) void {}

pub fn convert(term: Term, _: std.mem.Allocator, symbols: *datalog.SymbolTable) !datalog.Term {
return switch (term) {
.variable => |s| .{ .variable = @truncate(try symbols.insert(s)) }, // FIXME: assert symbol fits in u32
.string => |s| .{ .string = try symbols.insert(s) },
.bool => |b| .{ .bool = b },
};
}

pub fn format(term: Term, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
switch (term) {
.variable => |v| try writer.print("${s}", .{v}),
.string => |s| try writer.print("\"{s}\"", .{s}),
.bool => |b| if (b) try writer.print("true", .{}) else try writer.print("false", .{}),
}
}
};
1 change: 1 addition & 0 deletions biscuit-datalog/src/check.zig
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ pub const Check = struct {
for (check.queries.items) |*query| {
query.deinit();
}

check.queries.deinit();
}

3 changes: 3 additions & 0 deletions biscuit-datalog/src/main.zig
Original file line number Diff line number Diff line change
@@ -2,7 +2,10 @@ pub const fact = @import("fact.zig");
pub const Fact = @import("fact.zig").Fact;
pub const predicate = @import("predicate.zig");
pub const Predicate = @import("predicate.zig").Predicate;
pub const Expression = @import("expression.zig").Expression;
pub const Scope = @import("scope.zig").Scope;
pub const rule = @import("rule.zig");
pub const Rule = @import("rule.zig").Rule;
pub const check = @import("check.zig");
pub const symbol_table = @import("symbol_table.zig");
pub const SymbolTable = @import("symbol_table.zig").SymbolTable;
10 changes: 6 additions & 4 deletions biscuit-datalog/src/rule.zig
Original file line number Diff line number Diff line change
@@ -22,16 +22,17 @@ pub const Rule = struct {
const head = try Predicate.fromSchema(allocator, schema_rule.head orelse return error.NoHeadInRuleSchema);

var body = std.ArrayList(Predicate).init(allocator);
var expressions = std.ArrayList(Expression).init(allocator);
var scopes = std.ArrayList(Scope).init(allocator);

for (schema_rule.body.items) |predicate| {
try body.append(try Predicate.fromSchema(allocator, predicate));
}

var expressions = std.ArrayList(Expression).init(allocator);
for (schema_rule.expressions.items) |expression| {
try expressions.append(try Expression.fromSchema(allocator, expression));
}

var scopes = std.ArrayList(Scope).init(allocator);
for (schema_rule.scope.items) |scope| {
try scopes.append(try Scope.fromSchema(scope));
}
@@ -41,16 +42,17 @@ pub const Rule = struct {

pub fn deinit(rule: *Rule) void {
rule.head.deinit();

for (rule.body.items) |*predicate| {
predicate.deinit();
}
rule.body.deinit();

for (rule.expressions.items) |*expression| {
expression.deinit();
}
rule.expressions.deinit();

rule.body.deinit();
rule.expressions.deinit();
rule.scopes.deinit();
}

1 change: 1 addition & 0 deletions biscuit-parser/build.zig
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ pub fn build(b: *std.Build) void {
lib_unit_tests.root_module.addImport("biscuit-schema", schema.module("biscuit-schema"));
lib_unit_tests.root_module.addImport("biscuit-format", format.module("biscuit-format"));
lib_unit_tests.root_module.addImport("biscuit-builder", builder.module("biscuit-builder"));
lib_unit_tests.root_module.addImport("biscuit-datalog", datalog.module("biscuit-datalog"));
lib_unit_tests.root_module.addImport("ziglyph", ziglyph.module("ziglyph"));

const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
134 changes: 114 additions & 20 deletions biscuit-parser/src/main.zig
Original file line number Diff line number Diff line change
@@ -81,6 +81,16 @@ pub const Parser = struct {
pub fn term(parser: *Parser) !Term {
const rst = parser.rest();

variable_blk: {
var term_parser = Parser.init(rst);

const value = term_parser.variable() catch break :variable_blk;

parser.offset += term_parser.offset;

return .{ .variable = value };
}

string_blk: {
var term_parser = Parser.init(rst);

@@ -148,17 +158,37 @@ pub const Parser = struct {
try terms.append(trm);

if (parser.peek()) |peeked| {
if (peeked != ',') break;
} else {
break;
if (peeked == ',') {
parser.offset += 1;
continue;
}
}

break;
}

try parser.expect(')');

return .{ .name = name, .terms = terms };
}

fn variable(parser: *Parser) ![]const u8 {
try parser.expect('$');

const start = parser.offset;

for (parser.rest()) |c| {
if (ziglyph.isAlphaNum(c) or c == '_') {
parser.offset += 1;
continue;
}

break;
}

return parser.input[start..parser.offset];
}

fn string(parser: *Parser) ![]const u8 {
try parser.expect('"');

@@ -188,7 +218,7 @@ pub const Parser = struct {
return error.ExpectedBooleanTerm;
}

pub fn check(parser: *Parser) !Check {
pub fn check(parser: *Parser, allocator: std.mem.Allocator) !Check {
var kind: datalog.Check.Kind = undefined;

if (std.mem.startsWith(u8, parser.rest(), "check if")) {
@@ -201,13 +231,41 @@ pub const Parser = struct {
return error.UnexpectedCheckKind;
}

const queries = try parser.checkBody();
const queries = try parser.checkBody(allocator);

return .{ .kind = kind, .queries = queries };
}

fn checkBody(_: *Parser) !std.ArrayList(Rule) {
unreachable;
fn checkBody(parser: *Parser, allocator: std.mem.Allocator) !std.ArrayList(Rule) {
var queries = std.ArrayList(Rule).init(allocator);

const required_body = try parser.ruleBody(allocator);

try queries.append(.{
.head = .{ .name = "query", .terms = std.ArrayList(Term).init(allocator) },
.body = required_body.predicates,
.expressions = required_body.expressions,
.scopes = required_body.scopes,
.variables = null,
});

while (true) {
parser.skipWhiteSpace();

if (!std.mem.startsWith(u8, parser.rest(), "or")) break;

const body = try parser.ruleBody(allocator);

try queries.append(.{
.head = .{ .name = "query", .terms = std.ArrayList(Term).init(allocator) },
.body = body.predicates,
.expressions = body.expressions,
.scopes = body.scopes,
.variables = null,
});
}

return queries;
}

pub fn rule(parser: *Parser, allocator: std.mem.Allocator) !Rule {
@@ -237,6 +295,7 @@ pub const Parser = struct {

while (true) {
parser.skipWhiteSpace();
std.debug.print("{s}: \"{s}\"\n", .{ @src().fn_name, parser.rest() });

// Try parsing a predicate
predicate_blk: {
@@ -251,7 +310,10 @@ pub const Parser = struct {
parser.skipWhiteSpace();

if (parser.peek()) |peeked| {
if (peeked == ',') continue;
if (peeked == ',') {
parser.offset += 1;
continue;
}
}
}

@@ -268,7 +330,10 @@ pub const Parser = struct {
parser.skipWhiteSpace();

if (parser.peek()) |peeked| {
if (peeked == ',') continue;
if (peeked == ',') {
parser.offset += 1;
continue;
}
}
}

@@ -349,29 +414,58 @@ pub const Parser = struct {
}
};

test "parse fact predicate" {
// test "parse fact predicate" {
// const testing = std.testing;
// const input: []const u8 =
// \\read(true)
// ;

// var parser = Parser.init(input);

// const r = try parser.factPredicate(testing.allocator);
// defer r.deinit();

// std.debug.print("{any}\n", .{r});
// }

test "parse rule body" {
const testing = std.testing;
const fact: []const u8 =
\\read(true)
const input: []const u8 =
\\query(false) <- read(true), write(false)
;

var parser = Parser.init(fact);
var parser = Parser.init(input);

const f = try parser.factPredicate(testing.allocator);
defer f.deinit();
std.debug.print("{any}\n", .{f});
const r = try parser.rule(testing.allocator);
defer r.deinit();

std.debug.print("{any}\n", .{r});
}

test "parse rule body" {
test "parse rule body with variables" {
const testing = std.testing;
const rule_body: []const u8 =
\\query(false) <- read(true), write(false)
const input: []const u8 =
\\query($0) <- read($0), write(false)
;

var parser = Parser.init(rule_body);
var parser = Parser.init(input);

const r = try parser.rule(testing.allocator);
defer r.deinit();

std.debug.print("{any}\n", .{r});
}

test "parse check" {
const testing = std.testing;
const input: []const u8 =
\\check if right($0, $1), resource($0), operation($1)
;

var parser = Parser.init(input);

const r = try parser.check(testing.allocator);
defer r.deinit();

std.debug.print("{any}\n", .{r});
}
14 changes: 13 additions & 1 deletion biscuit-samples/src/main.zig
Original file line number Diff line number Diff line change
@@ -90,7 +90,19 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.
}
}
},
.Authorizer => return error.NotImplemented,
.Authorizer => |expected_failed_authority_check| {
for (errors.items) |found_failed_check| {
switch (found_failed_check) {
.failed_block_check => return error.NotImplemented,
.failed_authority_check => |failed_block_check| {
if (failed_block_check.check_id == expected_failed_authority_check.check_id) {
// continue :blk;
check_accounted_for = true;
}
},
}
}
},
}

if (!check_accounted_for) return error.ExpectedFailedCheck;
14 changes: 12 additions & 2 deletions biscuit/src/authorizer.zig
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ pub const Authorizer = struct {
pub fn addCheck(authorizer: *Authorizer, input: []const u8) !void {
var parser = Parser.init(input);

const check = try parser.check();
const check = try parser.check(authorizer.allocator);

try authorizer.checks.append(check);
}
@@ -90,7 +90,17 @@ pub const Authorizer = struct {
// 2. Run the world to generate all facts
try authorizer.world.run(authorizer.symbols);

// TODO: 3. Run checks that have been added to this authorizer
// 3. Run checks that have been added to this authorizer
for (authorizer.checks.items) |c| {
const check = try c.convert(authorizer.allocator, &authorizer.symbols);

for (check.queries.items, 0..) |*query, check_id| {
const is_match = try authorizer.world.queryMatch(query, authorizer.symbols);

if (!is_match) try errors.append(.{ .failed_authority_check = .{ .check_id = check_id } });
std.debug.print("match {any} = {}\n", .{ query, is_match });
}
}

// 4. Run checks in the biscuit's authority block
if (authorizer.biscuit) |biscuit| {