feat: use UUID library
This commit is contained in:
parent
0166219065
commit
94d96f1657
12 changed files with 53 additions and 68 deletions
|
@ -8,6 +8,8 @@ pub fn build(b: *std.Build) void {
|
|||
.root_source_file = .{ .path = "assets.zig" },
|
||||
});
|
||||
|
||||
const uuid_mod = b.dependency("uuid", .{}).module("uuid");
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "anvilauth",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
|
@ -17,6 +19,7 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
|
||||
exe.root_module.addImport("assets", assets_mod);
|
||||
exe.root_module.addImport("uuid", uuid_mod);
|
||||
|
||||
exe.linkSystemLibrary("libpq");
|
||||
exe.linkSystemLibrary("openssl");
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
.name = "anvilauth",
|
||||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{},
|
||||
.dependencies = .{
|
||||
.uuid = .{
|
||||
.url = "git+https://github.com/silversquirl/zig-uuid.git#66b79e3a32c255c7450fac7764847fd6453e94c5",
|
||||
.hash = "1220c4d64594998417a77f84bc6beba3f8aa7e67c0c69fe060c2a7f6385b4d56a34e",
|
||||
},
|
||||
},
|
||||
.paths = .{""},
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const std = @import("std");
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const ffi = @import("ffi.zig");
|
||||
const c = ffi.c;
|
||||
|
||||
const Id = @import("Id.zig");
|
||||
|
||||
con: *c.PGconn,
|
||||
|
||||
const Db = @This();
|
||||
|
@ -67,7 +67,7 @@ pub fn execParams(self: Db, query: [:0]const u8, params: anytype) Result {
|
|||
len.* = @intCast(bytes.len);
|
||||
format.* = 1;
|
||||
},
|
||||
Id => {
|
||||
UUID => {
|
||||
val.* = ¶m.bytes;
|
||||
len.* = param.bytes.len;
|
||||
format.* = 1;
|
||||
|
@ -133,7 +133,7 @@ pub const Result = struct {
|
|||
pub inline fn get(self: Result, comptime T: type, row: c_int, col: c_int) T {
|
||||
return switch (T) {
|
||||
[]const u8 => c.PQgetvalue(self.res, row, col)[0..@intCast(c.PQgetlength(self.res, row, col))],
|
||||
Id => .{ .bytes = c.PQgetvalue(self.res, row, col)[0..16].* },
|
||||
UUID => .{ .bytes = c.PQgetvalue(self.res, row, col)[0..16].* },
|
||||
else => @compileError("unsuppored type: " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
|
30
src/Id.zig
30
src/Id.zig
|
@ -1,30 +0,0 @@
|
|||
/// This file implements something similar to UUIDs without the bullshit.
|
||||
/// It implements a simple even U-er UID, which is simply 16 random bytes.
|
||||
const std = @import("std");
|
||||
|
||||
bytes: [16]u8,
|
||||
|
||||
const Id = @This();
|
||||
|
||||
/// Returns a hex encoded version of the ID.
|
||||
pub fn toString(self: Id) [32]u8 {
|
||||
var ret: [32]u8 = undefined;
|
||||
_ = std.fmt.bufPrint(&ret, "{}", .{std.fmt.fmtSliceHexLower(&self.bytes)}) catch unreachable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn genRandom(rand: std.rand.Random) Id {
|
||||
var bytes: [16]u8 = undefined;
|
||||
rand.bytes(&bytes);
|
||||
return .{ .bytes = bytes };
|
||||
}
|
||||
|
||||
// Parses the given string as an ID, or returns null if it is invalid.
|
||||
pub fn parse(str: []const u8) ?Id {
|
||||
if (str.len != 32) return null;
|
||||
var bytes: [16]u8 = undefined;
|
||||
for (&bytes, 0..) |*out, ihalf| {
|
||||
out.* = std.fmt.parseInt(u8, str[ihalf * 2 ..][0..2], 16) catch return null;
|
||||
}
|
||||
return .{ .bytes = bytes };
|
||||
}
|
|
@ -89,4 +89,3 @@ pub fn parseQueryParametersOrDefaults(params: []const u8, comptime T: type) Quer
|
|||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const std = @import("std");
|
||||
const c = ffi.c;
|
||||
|
||||
const ffi = @import("ffi.zig");
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const Id = @import("Id.zig");
|
||||
const ffi = @import("ffi.zig");
|
||||
|
||||
pub fn jsonUserWriter(
|
||||
alloc: std.mem.Allocator,
|
||||
|
@ -29,12 +29,12 @@ pub fn JsonUserWriter(Writer: type) type {
|
|||
|
||||
const Self = @This();
|
||||
|
||||
pub fn writeHeaderAndStartProperties(self: *Self, id: Id, name: []const u8) !void {
|
||||
pub fn writeHeaderAndStartProperties(self: *Self, id: UUID, name: []const u8) !void {
|
||||
std.debug.assert(self.state == .start);
|
||||
|
||||
try self.json_stream.beginObject();
|
||||
try self.json_stream.objectField("id");
|
||||
try self.json_stream.write(&id.toString());
|
||||
try self.json_stream.write(&id.toStringCompact());
|
||||
try self.json_stream.objectField("name");
|
||||
try self.json_stream.write(name);
|
||||
try self.json_stream.objectField("properties");
|
||||
|
@ -88,7 +88,7 @@ pub fn JsonUserWriter(Writer: type) type {
|
|||
|
||||
pub fn texturesProperty(
|
||||
self: *Self,
|
||||
user_id: Id,
|
||||
user_id: UUID,
|
||||
user_name: []const u8,
|
||||
skin_url: []const u8,
|
||||
) !void {
|
||||
|
@ -100,7 +100,7 @@ pub fn JsonUserWriter(Writer: type) type {
|
|||
try write_stream.objectField("timestamp");
|
||||
try write_stream.write(std.time.timestamp());
|
||||
try write_stream.objectField("profileId");
|
||||
try write_stream.write(&user_id.toString());
|
||||
try write_stream.write(&user_id.toStringCompact());
|
||||
try write_stream.objectField("profileName");
|
||||
try write_stream.write(user_name);
|
||||
try write_stream.objectField("textures");
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
const std = @import("std");
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const conutil = @import("../../../../conutil.zig");
|
||||
|
||||
const Id = @import("../../../../Id.zig");
|
||||
const State = @import("../../../../State.zig");
|
||||
|
||||
pub fn matches(path: []const u8) bool {
|
||||
|
@ -72,12 +73,12 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
|
||||
try json_writer.beginArray();
|
||||
for (0..@intCast(db_res.rows())) |rowidx| {
|
||||
const id = db_res.get(Id, @intCast(rowidx), 0);
|
||||
const id = db_res.get(UUID, @intCast(rowidx), 0);
|
||||
const name = db_res.get([]const u8, @intCast(rowidx), 1);
|
||||
|
||||
try json_writer.beginObject();
|
||||
try json_writer.objectField("id");
|
||||
try json_writer.write(&id.toString());
|
||||
try json_writer.write(&id.toStringCompact());
|
||||
try json_writer.objectField("name");
|
||||
try json_writer.write(name);
|
||||
try json_writer.endObject();
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const std = @import("std");
|
||||
const c = ffi.c;
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const ffi = @import("../../../ffi.zig");
|
||||
const conutil = @import("../../../conutil.zig");
|
||||
|
||||
const Id = @import("../../../Id.zig");
|
||||
const State = @import("../../../State.zig");
|
||||
|
||||
pub fn matches(path: []const u8) bool {
|
||||
|
@ -94,7 +95,7 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
if (sel_result.rows() != 1 or sel_result.cols() != 1)
|
||||
return error.InvalidResultFromPostgresServer;
|
||||
|
||||
const userid = sel_result.get(Id, 0, 0);
|
||||
const userid = sel_result.get(UUID, 0, 0);
|
||||
|
||||
const Profile = struct {
|
||||
name: []const u8,
|
||||
|
@ -120,14 +121,19 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
const client_token: [:0]const u8 = req_payload.value.clientToken orelse gentoken: {
|
||||
// TODO: according to https://wiki.vg/Legacy_Mojang_Authentication, the normal server
|
||||
// would invalidate all existing tokens here. This makes no sense, so we don't do it.
|
||||
@memcpy(&gen_token_buf, &Id.genRandom(state.rand).toString());
|
||||
var rand_bytes: [16]u8 = undefined;
|
||||
state.rand.bytes(&rand_bytes);
|
||||
@memcpy(&gen_token_buf, &UUID.fromRawBytes(4, rand_bytes)
|
||||
.toStringCompact());
|
||||
break :gentoken &gen_token_buf;
|
||||
};
|
||||
|
||||
// remains valid for one week
|
||||
const expiry = std.time.timestamp() + std.time.ms_per_week;
|
||||
|
||||
const tokenid = Id.genRandom(state.rand);
|
||||
var tokenid_bytes: [16]u8 = undefined;
|
||||
state.rand.bytes(&tokenid_bytes);
|
||||
const tokenid = UUID.fromRawBytes(4, tokenid_bytes);
|
||||
|
||||
const add_tok_stat = state.db.execParams(
|
||||
\\INSERT INTO tokens (id, userid, expiry, client_token)
|
||||
|
@ -138,7 +144,7 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
defer add_tok_stat.deinit();
|
||||
try add_tok_stat.expectCommand();
|
||||
|
||||
const uid_hex = userid.toString();
|
||||
const uid_hex = userid.toStringCompact();
|
||||
|
||||
const profile = Profile{
|
||||
.name = req_payload.value.username,
|
||||
|
@ -158,7 +164,7 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
},
|
||||
} else null,
|
||||
.clientToken = client_token,
|
||||
.accessToken = &tokenid.toString(),
|
||||
.accessToken = &tokenid.toStringCompact(),
|
||||
.availableProfiles = &.{profile},
|
||||
.selectedProfile = profile,
|
||||
};
|
||||
|
|
|
@ -36,10 +36,8 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
const json = try std.json.stringifyAlloc(state.allocator, response_payload, .{});
|
||||
defer state.allocator.free(json);
|
||||
|
||||
try req.respond(json, .{
|
||||
.extra_headers = &.{.{
|
||||
.name = "Content-Type",
|
||||
.value = "application/json",
|
||||
}}
|
||||
});
|
||||
try req.respond(json, .{ .extra_headers = &.{.{
|
||||
.name = "Content-Type",
|
||||
.value = "application/json",
|
||||
}} });
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const std = @import("std");
|
||||
const c = ffi.c;
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const conutil = @import("../../../../../conutil.zig");
|
||||
const ffi = @import("../../../../../ffi.zig");
|
||||
|
||||
const Id = @import("../../../../../Id.zig");
|
||||
const jsonUserWriter = @import("../../../../../json_user_writer.zig").jsonUserWriter;
|
||||
const State = @import("../../../../../State.zig");
|
||||
|
||||
|
@ -37,7 +38,7 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
if (sel_dbret.cols() != 1) return error.InvalidResultFromPostgresServer;
|
||||
|
||||
if (sel_dbret.rows() >= 1) {
|
||||
const id = sel_dbret.get(Id, 0, 0);
|
||||
const id = sel_dbret.get(UUID, 0, 0);
|
||||
|
||||
const skin_url = try state.getSkinUrl(params.username);
|
||||
defer if (skin_url) |url| state.allocator.free(url);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const std = @import("std");
|
||||
const c = ffi.c;
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const conutil = @import("../../../../../conutil.zig");
|
||||
const ffi = @import("../../../../../ffi.zig");
|
||||
|
||||
const Id = @import("../../../../../Id.zig");
|
||||
const State = @import("../../../../../State.zig");
|
||||
|
||||
pub fn matches(path: []const u8) bool {
|
||||
|
@ -28,13 +29,13 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
};
|
||||
defer req_payload.deinit();
|
||||
|
||||
const access_token = Id.parse(req_payload.value.accessToken) orelse {
|
||||
try conutil.sendJsonError(req, .bad_request, "accessToken is not a valid ID!", .{});
|
||||
const access_token = UUID.fromString(req_payload.value.accessToken) catch {
|
||||
try conutil.sendJsonError(req, .bad_request, "accessToken is not a valid UUID!", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
const sel_profile = Id.parse(req_payload.value.selectedProfile) orelse {
|
||||
try conutil.sendJsonError(req, .bad_request, "selectedProfile is not a valid ID!", .{});
|
||||
const sel_profile = UUID.fromString(req_payload.value.selectedProfile) catch {
|
||||
try conutil.sendJsonError(req, .bad_request, "selectedProfile is not a valid UUID!", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -45,7 +46,7 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
if (dbret.cols() != 1) return error.InvalidResultFromPostgresServer;
|
||||
|
||||
if (dbret.rows() >= 1) {
|
||||
const token_user = dbret.get(Id, 0, 0);
|
||||
const token_user = dbret.get(UUID, 0, 0);
|
||||
if (std.mem.eql(u8, &sel_profile.bytes, &token_user.bytes)) {
|
||||
const ins_dbret = state.db.execParams(
|
||||
\\INSERT INTO joins (userid, serverid)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const std = @import("std");
|
||||
const c = ffi.c;
|
||||
|
||||
const UUID = @import("uuid").Uuid;
|
||||
|
||||
const ffi = @import("../../../../../ffi.zig");
|
||||
const conutil = @import("../../../../../conutil.zig");
|
||||
|
||||
const Id = @import("../../../../../Id.zig");
|
||||
const jsonUserWriter = @import("../../../../../json_user_writer.zig").jsonUserWriter;
|
||||
const State = @import("../../../../../State.zig");
|
||||
|
||||
|
@ -18,11 +19,11 @@ pub fn call(req: *std.http.Server.Request, state: *State) !void {
|
|||
const req_url = try std.Uri.parseWithoutScheme(req.head.target);
|
||||
|
||||
// This is sound as we only go here if the path starts with path_prefix.
|
||||
const profile_id = Id.parse(req_url.path[path_prefix.len..]) orelse {
|
||||
const profile_id = UUID.fromString(req_url.path[path_prefix.len..]) catch {
|
||||
try conutil.sendJsonError(
|
||||
req,
|
||||
.bad_request,
|
||||
"not a valid UUID: {s} (NOTE: AnvilAuth technically doesn't use UUIDs, this endpoint expects 16 hex-encoded, undelimited bytes.)",
|
||||
"not a valid UUID: {s}",
|
||||
.{req_url.path[path_prefix.len..]},
|
||||
);
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue