feat: add putLenPrefixed and encodeSet to protocol
This commit is contained in:
1 parent
06617d9292
commit
4c072af95d
1 file changed
+157
@@ -156,3 +156,160 @@ pub const Cursor = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn putLenPrefixed(writer: encode.ByteWriter, v: []const u8) !void {
|
||||
try writer.putU32(@intCast(v.len));
|
||||
try writer.slice(v);
|
||||
}
|
||||
|
||||
// --- set ---
|
||||
|
||||
pub fn encodeSet(allocator: Allocator, key: []const u8, value: []const u8) ![]u8 {
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
errdefer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try putLenPrefixed(w, key);
|
||||
try w.slice(value);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const SetArgs = struct { key: []const u8, value: []const u8 };
|
||||
|
||||
pub fn decodeSet(payload: []const u8) !SetArgs {
|
||||
var c = Cursor{ .data = payload };
|
||||
const key = try c.takeLenPrefixed();
|
||||
const value = c.data[c.pos..];
|
||||
return .{ .key = key, .value = value };
|
||||
}
|
||||
|
||||
// --- auth ---
|
||||
|
||||
pub fn encodeAuth(allocator: Allocator, username: []const u8, password: []const u8) ![]u8 {
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
errdefer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try putLenPrefixed(w, username);
|
||||
try w.slice(password);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const AuthArgs = struct { username: []const u8, password: []const u8 };
|
||||
|
||||
pub fn decodeAuth(payload: []const u8) !AuthArgs {
|
||||
var c = Cursor{ .data = payload };
|
||||
const username = try c.takeLenPrefixed();
|
||||
const password = c.data[c.pos..];
|
||||
return .{ .username = username, .password = password };
|
||||
}
|
||||
|
||||
/// A successful `auth` response's payload: empty when the server has no
|
||||
/// session store wired up (see server.zig's `enableSessions`), or the raw
|
||||
/// session token bytes when it does. There is no separate decode function
|
||||
/// on the client side - `frame.payload` itself IS the token (or empty),
|
||||
/// no framing needed for a single opaque blob.
|
||||
pub fn encodeAuthOk(allocator: Allocator, token: ?[]const u8) ![]u8 {
|
||||
return allocator.dupe(u8, token orelse "");
|
||||
}
|
||||
|
||||
// --- auth_token ---
|
||||
|
||||
/// Reconnects a fresh connection to a role without presenting the
|
||||
/// password again, by presenting a token previously handed out in an
|
||||
/// `auth` response (see `session.zig`'s `SessionStore.issue`). The
|
||||
/// payload is nothing but the opaque token bytes - no framing needed for
|
||||
/// a single blob, exactly like `encodeAuthOk`.
|
||||
pub fn encodeAuthToken(allocator: Allocator, token: []const u8) ![]u8 {
|
||||
return allocator.dupe(u8, token);
|
||||
}
|
||||
|
||||
pub fn decodeAuthToken(payload: []const u8) []const u8 {
|
||||
return payload;
|
||||
}
|
||||
|
||||
// --- user_admin ---
|
||||
|
||||
/// Remote counterpart of the CLI's local `user add|passwd|role|del|list`,
|
||||
/// carried as one opcode so `server.zig`'s `requiredRole` can gate all five
|
||||
/// actions behind a single `admin`-only check instead of five separate
|
||||
/// opcodes. `role`/`rounds` are only meaningful for the actions that use
|
||||
/// them (`create` reads both, `set_role` reads only `role`, `set_password`
|
||||
/// reads only `rounds`); callers send 0 for the fields an action ignores.
|
||||
pub const UserAdminAction = enum(u8) {
|
||||
create = 0,
|
||||
set_password = 1,
|
||||
set_role = 2,
|
||||
delete = 3,
|
||||
list = 4,
|
||||
|
||||
pub fn fromByte(b: u8) !UserAdminAction {
|
||||
return std.meta.intToEnum(UserAdminAction, b) catch error.Malformed;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn encodeUserAdmin(allocator: Allocator, action: UserAdminAction, username: []const u8, password: []const u8, role: authmod.Role, rounds: u32) ![]u8 {
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
errdefer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try w.byte(@intFromEnum(action));
|
||||
try w.byte(@intFromEnum(role));
|
||||
try w.putU32(rounds);
|
||||
try putLenPrefixed(w, username);
|
||||
try w.slice(password);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const UserAdminArgs = struct {
|
||||
action: UserAdminAction,
|
||||
roleByte: u8,
|
||||
rounds: u32,
|
||||
username: []const u8,
|
||||
password: []const u8,
|
||||
};
|
||||
|
||||
pub fn decodeUserAdmin(payload: []const u8) !UserAdminArgs {
|
||||
var c = Cursor{ .data = payload };
|
||||
const action = try UserAdminAction.fromByte(try c.takeByte());
|
||||
const roleByte = try c.takeByte();
|
||||
const rounds = try c.takeU32();
|
||||
const username = try c.takeLenPrefixed();
|
||||
const password = c.data[c.pos..];
|
||||
return .{ .action = action, .roleByte = roleByte, .rounds = rounds, .username = username, .password = password };
|
||||
}
|
||||
|
||||
/// Encodes the response of a `list` user_admin request: every username
|
||||
/// paired with its role byte. Reuses `authmod.UserInfo`/`freeUserList`
|
||||
/// rather than inventing a parallel type, the same way `encodeStats` reuses
|
||||
/// `dbmod.Stats` and `encodeRows` reuses `rowmod.Row`.
|
||||
pub fn encodeUserList(allocator: Allocator, users: []const authmod.UserInfo) ![]u8 {
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
errdefer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try w.putU32(@intCast(users.len));
|
||||
for (users) |u| {
|
||||
try putLenPrefixed(w, u.username);
|
||||
try w.byte(@intFromEnum(u.role));
|
||||
}
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
/// Decodes a user list response into independently owned entries - free
|
||||
/// with `authmod.freeUserList`.
|
||||
pub fn decodeUserList(allocator: Allocator, payload: []const u8) ![]authmod.UserInfo {
|
||||
var c = Cursor{ .data = payload };
|
||||
const count = try c.takeU32();
|
||||
var list = try std.ArrayList(authmod.UserInfo).initCapacity(allocator, count);
|
||||
errdefer {
|
||||
for (list.items) |u| u.deinit(allocator);
|
||||
list.deinit();
|
||||
}
|
||||
var i: u32 = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
const uname = try c.takeLenPrefixed();
|
||||
const role = authmod.Role.fromByte(try c.takeByte()) catch return error.Malformed;
|
||||
const owned = try allocator.dupe(u8, uname);
|
||||
list.appendAssumeCapacity(.{ .username = owned, .role = role });
|
||||
}
|
||||
return list.toOwnedSlice();
|
||||
}
|
||||
|
||||
// --- ttl_op ---
|
||||
|
||||
Reference in new issue
Block a user