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