|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | +// |
| 4 | +// Burble Admin FFI — C-compatible bridge for BoJ MCP cartridge. |
| 5 | +// Implements the operations defined in BurbleAdmin.Protocol (Idris2 ABI). |
| 6 | + |
| 7 | +const std = @import("std"); |
| 8 | + |
| 9 | +// ═══════════════════════════════════════════════════════════════════════ |
| 10 | +// Types (mirrors Idris2 ABI) |
| 11 | +// ═══════════════════════════════════════════════════════════════════════ |
| 12 | + |
| 13 | +pub const Operation = enum(i32) { |
| 14 | + list_rooms = 0, |
| 15 | + create_room = 1, |
| 16 | + delete_room = 2, |
| 17 | + list_users = 3, |
| 18 | + kick_user = 4, |
| 19 | + get_metrics = 5, |
| 20 | + manage_recordings = 6, |
| 21 | +}; |
| 22 | + |
| 23 | +pub const PermLevel = enum(i32) { |
| 24 | + read_only = 0, |
| 25 | + moderator = 1, |
| 26 | + admin = 2, |
| 27 | +}; |
| 28 | + |
| 29 | +// ═══════════════════════════════════════════════════════════════════════ |
| 30 | +// Permission checking (matches Idris2 proof) |
| 31 | +// ═══════════════════════════════════════════════════════════════════════ |
| 32 | + |
| 33 | +/// Returns the minimum permission level for an operation. |
| 34 | +/// Matches burble_min_perm in Protocol.idr exactly. |
| 35 | +pub export fn burble_admin_min_perm(op: i32) callconv(.C) i32 { |
| 36 | + return switch (@as(Operation, @enumFromInt(op))) { |
| 37 | + .list_rooms => 0, |
| 38 | + .create_room => 1, |
| 39 | + .delete_room => 2, |
| 40 | + .list_users => 0, |
| 41 | + .kick_user => 1, |
| 42 | + .get_metrics => 0, |
| 43 | + .manage_recordings => 2, |
| 44 | + }; |
| 45 | +} |
| 46 | + |
| 47 | +/// Check if a user with the given permission level can perform the operation. |
| 48 | +/// Returns 1 if allowed, 0 if denied. |
| 49 | +pub export fn burble_admin_check_perm(op: i32, user_perm: i32) callconv(.C) i32 { |
| 50 | + const required = burble_admin_min_perm(op); |
| 51 | + return if (user_perm >= required) 1 else 0; |
| 52 | +} |
| 53 | + |
| 54 | +// ═══════════════════════════════════════════════════════════════════════ |
| 55 | +// Room capacity validation |
| 56 | +// ═══════════════════════════════════════════════════════════════════════ |
| 57 | + |
| 58 | +/// Validate room capacity (1-500). Returns clamped value. |
| 59 | +pub export fn burble_admin_clamp_capacity(requested: i32) callconv(.C) i32 { |
| 60 | + if (requested < 1) return 1; |
| 61 | + if (requested > 500) return 500; |
| 62 | + return requested; |
| 63 | +} |
| 64 | + |
| 65 | +// ═══════════════════════════════════════════════════════════════════════ |
| 66 | +// Tests |
| 67 | +// ═══════════════════════════════════════════════════════════════════════ |
| 68 | + |
| 69 | +test "permission levels match ABI" { |
| 70 | + // ReadOnly ops |
| 71 | + try std.testing.expectEqual(@as(i32, 0), burble_admin_min_perm(0)); // list_rooms |
| 72 | + try std.testing.expectEqual(@as(i32, 0), burble_admin_min_perm(3)); // list_users |
| 73 | + try std.testing.expectEqual(@as(i32, 0), burble_admin_min_perm(5)); // get_metrics |
| 74 | + |
| 75 | + // Moderator ops |
| 76 | + try std.testing.expectEqual(@as(i32, 1), burble_admin_min_perm(1)); // create_room |
| 77 | + try std.testing.expectEqual(@as(i32, 1), burble_admin_min_perm(4)); // kick_user |
| 78 | + |
| 79 | + // Admin ops |
| 80 | + try std.testing.expectEqual(@as(i32, 2), burble_admin_min_perm(2)); // delete_room |
| 81 | + try std.testing.expectEqual(@as(i32, 2), burble_admin_min_perm(6)); // manage_recordings |
| 82 | +} |
| 83 | + |
| 84 | +test "permission check" { |
| 85 | + // Admin can do everything |
| 86 | + try std.testing.expectEqual(@as(i32, 1), burble_admin_check_perm(0, 2)); |
| 87 | + try std.testing.expectEqual(@as(i32, 1), burble_admin_check_perm(2, 2)); |
| 88 | + |
| 89 | + // ReadOnly can't delete |
| 90 | + try std.testing.expectEqual(@as(i32, 0), burble_admin_check_perm(2, 0)); |
| 91 | +} |
| 92 | + |
| 93 | +test "capacity clamping" { |
| 94 | + try std.testing.expectEqual(@as(i32, 1), burble_admin_clamp_capacity(0)); |
| 95 | + try std.testing.expectEqual(@as(i32, 50), burble_admin_clamp_capacity(50)); |
| 96 | + try std.testing.expectEqual(@as(i32, 500), burble_admin_clamp_capacity(999)); |
| 97 | +} |
0 commit comments