diff --git a/biscuit-datalog/src/combinator.zig b/biscuit-datalog/src/combinator.zig index 689b734..d351609 100644 --- a/biscuit-datalog/src/combinator.zig +++ b/biscuit-datalog/src/combinator.zig @@ -103,7 +103,7 @@ pub const Combinator = struct { // Lookup the next (trusted) fact const origin_fact = combinator.trusted_fact_iterator.next() orelse return null; - std.debug.print("next trusted fact: {any}\n", .{origin_fact.fact}); + std.debug.print("combinator next trusted fact: {any}\n", .{origin_fact.fact}); const origin = origin_fact.origin.*; const fact = origin_fact.fact.*; diff --git a/biscuit-datalog/src/rule.zig b/biscuit-datalog/src/rule.zig index 01249a8..8cd2743 100644 --- a/biscuit-datalog/src/rule.zig +++ b/biscuit-datalog/src/rule.zig @@ -119,7 +119,7 @@ pub const Rule = struct { var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); - std.debug.print("\napplying rule:\n {any}\n", .{rule}); + std.debug.print("\napplying rule (from origin {}):\n {any}\n", .{ origin_id, rule }); const matched_variables = try MatchedVariables.init(arena.allocator(), rule); // TODO: if body is empty stuff diff --git a/biscuit-datalog/src/world.zig b/biscuit-datalog/src/world.zig index 9ebd063..8e5f240 100644 --- a/biscuit-datalog/src/world.zig +++ b/biscuit-datalog/src/world.zig @@ -105,7 +105,7 @@ pub const World = struct { } pub fn addRule(world: *World, origin_id: usize, scope: TrustedOrigins, rule: Rule) !void { - std.debug.print("\nworld: adding rule = {any} ({}, {any})\n", .{ rule, origin_id, scope }); + std.debug.print("\nworld: adding rule (origin {}) = {any} (trusts {any})\n", .{ origin_id, rule, scope }); try world.rule_set.add(origin_id, scope, rule); } diff --git a/biscuit-format/src/main.zig b/biscuit-format/src/main.zig index eaef9c4..87ff1ea 100644 --- a/biscuit-format/src/main.zig +++ b/biscuit-format/src/main.zig @@ -1,5 +1,8 @@ pub const decode = @import("decode.zig"); -pub const serialized_biscuit = @import("serialized_biscuit.zig"); +pub const MIN_SCHEMA_VERSION = @import("serialized_biscuit.zig").MIN_SCHEMA_VERSION; +pub const MAX_SCHEMA_VERSION = @import("serialized_biscuit.zig").MAX_SCHEMA_VERSION; +pub const SignedBlock = @import("signed_block.zig").SignedBlock; +pub const SerializedBiscuit = @import("serialized_biscuit.zig").SerializedBiscuit; test { _ = @import("serialized_biscuit.zig"); diff --git a/biscuit-parser/src/parser.zig b/biscuit-parser/src/parser.zig index 41e6bde..7e8e032 100644 --- a/biscuit-parser/src/parser.zig +++ b/biscuit-parser/src/parser.zig @@ -903,7 +903,7 @@ pub const Parser = struct { if (!parser.startsWith(",")) break; - try parser.expectString("'"); + try parser.expectString(","); } return scps; diff --git a/biscuit-samples/src/main.zig b/biscuit-samples/src/main.zig index 139cc24..e77611f 100644 --- a/biscuit-samples/src/main.zig +++ b/biscuit-samples/src/main.zig @@ -89,18 +89,18 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto. check_accounted_for = true; } }, - .failed_authority_check => return error.NotImplemented, + .failed_authorizer_check => return error.NotImplemented, } } }, - .Authorizer => |expected_failed_authority_check| { + .Authorizer => |expected_failed_authorizer_check| { for (errors.items) |found_failed_check| { switch (found_failed_check) { .no_matching_policy => continue, .denied_by_policy => continue, .failed_block_check => return error.NotImplemented, - .failed_authority_check => |failed_block_check| { - if (failed_block_check.check_id == expected_failed_authority_check.check_id) { + .failed_authorizer_check => |failed_block_check| { + if (failed_block_check.check_id == expected_failed_authorizer_check.check_id) { // continue :blk; check_accounted_for = true; } @@ -136,7 +136,7 @@ pub fn runValidation(alloc: mem.Allocator, token: []const u8, public_key: std.cr var b = try Biscuit.fromBytes(alloc, token, public_key); defer b.deinit(); - var a = b.authorizer(alloc); + var a = try b.authorizer(alloc); defer a.deinit(); var it = std.mem.split(u8, authorizer_code, ";"); diff --git a/biscuit/src/authorizer.zig b/biscuit/src/authorizer.zig index 4a280e7..15b8f01 100644 --- a/biscuit/src/authorizer.zig +++ b/biscuit/src/authorizer.zig @@ -21,15 +21,31 @@ pub const Authorizer = struct { public_key_to_block_id: std.AutoHashMap(usize, std.ArrayList(usize)), scopes: std.ArrayList(Scope), - pub fn init(allocator: std.mem.Allocator, biscuit: Biscuit) Authorizer { + pub fn init(allocator: std.mem.Allocator, biscuit: Biscuit) !Authorizer { + var symbols = SymbolTable.init("authorizer", allocator); + var public_key_to_block_id = std.AutoHashMap(usize, std.ArrayList(usize)).init(allocator); + + // Map public key symbols into authorizer symbols and public_key_to_block_id map + var it = biscuit.public_key_to_block_id.iterator(); + while (it.next()) |entry| { + const biscuit_public_key_index = entry.key_ptr.*; + const block_ids = entry.value_ptr.*; + + const public_key = try biscuit.symbols.getPublicKey(biscuit_public_key_index); + + const authorizer_public_key_index = try symbols.insertPublicKey(public_key); + + try public_key_to_block_id.put(authorizer_public_key_index, try block_ids.clone()); + } + return .{ .allocator = allocator, .checks = std.ArrayList(builder.Check).init(allocator), .policies = std.ArrayList(builder.Policy).init(allocator), .biscuit = biscuit, .world = World.init(allocator), - .symbols = SymbolTable.init("authorizer", allocator), - .public_key_to_block_id = biscuit.public_key_to_block_id, // FIXME: are we okay to just copy here? + .symbols = symbols, + .public_key_to_block_id = public_key_to_block_id, .scopes = std.ArrayList(Scope).init(allocator), }; } @@ -48,6 +64,14 @@ pub const Authorizer = struct { policy.deinit(); } authorizer.policies.deinit(); + + { + var it = authorizer.public_key_to_block_id.valueIterator(); + while (it.next()) |block_ids| { + block_ids.deinit(); + } + } + authorizer.policies.deinit(); } pub fn authorizerTrustedOrigins(authorizer: *Authorizer) !TrustedOrigins { @@ -108,6 +132,19 @@ pub const Authorizer = struct { /// 6. _biscuit_ (where it exists): run the checks from all the non-authority blocks pub fn authorize(authorizer: *Authorizer, errors: *std.ArrayList(AuthorizerError)) !usize { std.debug.print("\nAuthorizing biscuit:\n", .{}); + + std.debug.print("authorizer public keys:\n", .{}); + for (authorizer.symbols.public_keys.items, 0..) |pk, i| { + std.debug.print(" [{}]: {x}\n", .{ i, pk.bytes }); + } + + { + var it = authorizer.public_key_to_block_id.iterator(); + while (it.next()) |entry| { + std.debug.print("public_key_to_block_id: public key id = {}, block_ids = {any}\n", .{ entry.key_ptr.*, entry.value_ptr.items }); + } + } + // 1. // Load facts and rules from authority block into world. Our block's facts // will have a particular symbol table that we map into the symvol table @@ -116,6 +153,10 @@ pub const Authorizer = struct { // For example, the token may have a string "user123" which has id 12. But // when mapped into the world it may have id 5. if (authorizer.biscuit) |biscuit| { + std.debug.print("biscuit token public keys:\n", .{}); + for (biscuit.symbols.public_keys.items, 0..) |pk, i| { + std.debug.print(" [{}]: {x}\n", .{ i, pk.bytes }); + } for (biscuit.authority.facts.items) |authority_fact| { const fact = try authority_fact.convert(&biscuit.symbols, &authorizer.symbols); const origin = try Origin.initWithId(authorizer.allocator, 0); @@ -165,6 +206,7 @@ pub const Authorizer = struct { for (block.rules.items) |block_rule| { const rule = try block_rule.convert(&biscuit.symbols, &authorizer.symbols); + std.debug.print("block rule {any} CONVERTED to rule = {any}\n", .{ block_rule, rule }); const block_rule_trusted_origins = try TrustedOrigins.fromScopes( authorizer.allocator, @@ -180,9 +222,12 @@ pub const Authorizer = struct { } // 2. Run the world to generate all facts + std.debug.print("\nGENERATING NEW FACTS\n", .{}); try authorizer.world.run(authorizer.symbols); + std.debug.print("\nEND GENERATING NEW FACTS\n", .{}); // 3. Run checks that have been added to this authorizer + std.debug.print("\nAUTHORIZER CHECKS\n", .{}); for (authorizer.checks.items) |c| { std.debug.print("authorizer check = {any}\n", .{c}); const check = try c.convert(authorizer.allocator, &authorizer.symbols); @@ -201,10 +246,11 @@ pub const Authorizer = struct { .all => try authorizer.world.queryMatchAll(query, authorizer.symbols, rule_trusted_origins), }; - if (!is_match) try errors.append(.{ .failed_authority_check = .{ .check_id = check_id } }); + if (!is_match) try errors.append(.{ .failed_authorizer_check = .{ .check_id = check_id } }); std.debug.print("match {any} = {}\n", .{ query, is_match }); } } + std.debug.print("END AUTHORIZER CHECKS\n", .{}); // 4. Run checks in the biscuit's authority block if (authorizer.biscuit) |biscuit| { @@ -326,13 +372,13 @@ pub const Authorizer = struct { const AuthorizerErrorKind = enum(u8) { no_matching_policy, denied_by_policy, - failed_authority_check, + failed_authorizer_check, failed_block_check, }; pub const AuthorizerError = union(AuthorizerErrorKind) { no_matching_policy: void, denied_by_policy: struct { deny_policy_id: usize }, - failed_authority_check: struct { check_id: usize }, + failed_authorizer_check: struct { check_id: usize }, failed_block_check: struct { block_id: usize, check_id: usize }, }; diff --git a/biscuit/src/biscuit.zig b/biscuit/src/biscuit.zig index 52e4768..13b0423 100644 --- a/biscuit/src/biscuit.zig +++ b/biscuit/src/biscuit.zig @@ -5,7 +5,7 @@ const Authorizer = @import("authorizer.zig").Authorizer; const Block = @import("block.zig").Block; const SymbolTable = @import("biscuit-datalog").SymbolTable; const World = @import("biscuit-datalog").world.World; -const SerializedBiscuit = @import("biscuit-format").serialized_biscuit.SerializedBiscuit; +const SerializedBiscuit = @import("biscuit-format").SerializedBiscuit; pub const Biscuit = struct { serialized: SerializedBiscuit, @@ -23,27 +23,20 @@ pub const Biscuit = struct { defer block_external_keys.deinit(); defer std.debug.assert(block_external_keys.items.len == 1 + serialized.blocks.items.len); - var symbols = SymbolTable.init("biscuit", allocator); + var token_symbols = SymbolTable.init("biscuit", allocator); - const authority = try Block.fromBytes(allocator, serialized.authority.block, &symbols); - for (authority.public_keys.items) |public_key| { - _ = try symbols.insertPublicKey(public_key); - } + const authority = try Block.fromBytes(allocator, serialized.authority, &token_symbols); try block_external_keys.append(null); std.debug.print("authority block =\n{any}\n", .{authority}); var blocks = std.ArrayList(Block).init(allocator); - for (serialized.blocks.items) |b| { - const block = try Block.fromBytes(allocator, b.block, &symbols); + for (serialized.blocks.items) |signed_block| { + const block = try Block.fromBytes(allocator, signed_block, &token_symbols); std.debug.print("non-authority block =\n{any}\n", .{block}); - const external_key = if (b.external_signature) |external_signature| external_signature.public_key else null; + const external_key = if (signed_block.external_signature) |external_signature| external_signature.public_key else null; try block_external_keys.append(external_key); - for (block.public_keys.items) |public_key| { - _ = try symbols.insertPublicKey(public_key); - } - try blocks.append(block); } @@ -54,7 +47,7 @@ pub const Biscuit = struct { for (block_external_keys.items, 0..) |block_external_key, block_id| { const key = block_external_key orelse continue; - const key_index = try symbols.insertPublicKey(key); + const key_index = try token_symbols.insertPublicKey(key); if (public_key_to_block_id.getPtr(key_index)) |list_ptr| { try list_ptr.append(block_id); } else { @@ -75,7 +68,7 @@ pub const Biscuit = struct { .serialized = serialized, .authority = authority, .blocks = blocks, - .symbols = symbols, + .symbols = token_symbols, .public_key_to_block_id = public_key_to_block_id, }; } @@ -97,8 +90,8 @@ pub const Biscuit = struct { biscuit.serialized.deinit(); } - pub fn authorizer(biscuit: *Biscuit, allocator: std.mem.Allocator) Authorizer { - return Authorizer.init(allocator, biscuit.*); + pub fn authorizer(biscuit: *Biscuit, allocator: std.mem.Allocator) !Authorizer { + return try Authorizer.init(allocator, biscuit.*); } }; diff --git a/biscuit/src/block.zig b/biscuit/src/block.zig index 37c9fda..304dbc3 100644 --- a/biscuit/src/block.zig +++ b/biscuit/src/block.zig @@ -1,14 +1,14 @@ const std = @import("std"); const Ed25519 = std.crypto.sign.Ed25519; -const format = @import("biscuit-format"); +const SignedBlock = @import("biscuit-format").SignedBlock; +const MIN_SCHEMA_VERSION = @import("biscuit-format").MIN_SCHEMA_VERSION; +const MAX_SCHEMA_VERSION = @import("biscuit-format").MAX_SCHEMA_VERSION; const schema = @import("biscuit-schema"); const Fact = @import("biscuit-datalog").fact.Fact; const Rule = @import("biscuit-datalog").rule.Rule; const Check = @import("biscuit-datalog").check.Check; const Scope = @import("biscuit-datalog").Scope; const SymbolTable = @import("biscuit-datalog").symbol_table.SymbolTable; -const MIN_SCHEMA_VERSION = format.serialized_biscuit.MIN_SCHEMA_VERSION; -const MAX_SCHEMA_VERSION = format.serialized_biscuit.MAX_SCHEMA_VERSION; pub const Block = struct { version: u32, @@ -47,7 +47,8 @@ pub const Block = struct { } /// Given a blocks contents as bytes, derserialize into runtime block - pub fn fromBytes(allocator: std.mem.Allocator, data: []const u8, symbols: *SymbolTable) !Block { + pub fn fromBytes(allocator: std.mem.Allocator, signed_block: SignedBlock, token_symbols: *SymbolTable) !Block { + const data = signed_block.block; std.debug.print("Block.fromBytes\n", .{}); const decoded_block = try schema.decodeBlock(allocator, data); defer decoded_block.deinit(); @@ -63,7 +64,16 @@ pub const Block = struct { for (decoded_block.symbols.items) |symbol| { _ = try block.symbols.insert(symbol.getSlice()); - _ = try symbols.insert(symbol.getSlice()); + } + + // If we have an external signature we add the external public key that to the parent biscuit's symbols and we don't add the blocks symbols + // Otherwise add the blocks symbols to the biscuit's symbol table. + if (signed_block.external_signature) |external_signature| { + _ = try token_symbols.insertPublicKey(external_signature.public_key); + } else { + for (decoded_block.symbols.items) |symbol| { + _ = try token_symbols.insert(symbol.getSlice()); + } } for (decoded_block.facts_v2.items) |fact| { @@ -83,6 +93,8 @@ pub const Block = struct { @memcpy(&pubkey_buf, public_key.key.getSlice()); const key = try Ed25519.PublicKey.fromBytes(pubkey_buf); + + _ = try token_symbols.insertPublicKey(key); try block.public_keys.append(key); }