diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-11-14 21:55:59 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-11-14 21:55:59 +0100 |
| commit | 3f18f02d07802d1fc705a500e5978a9b3cb2e751 (patch) | |
| tree | 283970a2f5a693706456b853c550eeaa669b5d72 /src/storage/user.zig | |
| parent | 351ad457f0ff95e20301a146b8c88a8f0f659aa1 (diff) | |
implement login
Diffstat (limited to 'src/storage/user.zig')
| -rw-r--r-- | src/storage/user.zig | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/storage/user.zig b/src/storage/user.zig new file mode 100644 index 0000000..c981661 --- /dev/null +++ b/src/storage/user.zig @@ -0,0 +1,133 @@ +const std = @import("std"); +const Storage = @import("root.zig"); + +const Self = @This(); + +pub const UserInfo = struct { + name: []const u8, + full_name: []const u8, + birthday: []const u8, + hash: []const u8, + is_admin: bool, +}; + +dir: std.fs.Dir, +info: UserInfo, +arena: std.heap.ArenaAllocator, + +pub fn open( + storage: *Storage, + name: []const u8, + inner_allocator: std.mem.Allocator +) !Self { + var arena: std.heap.ArenaAllocator = .init(inner_allocator); + errdefer arena.deinit(); + const allocator = arena.allocator(); + + var user_dir = try storage.dir.openDir("user", .{}); + defer user_dir.close(); + + var dir = try user_dir.openDir(name, .{}); + errdefer dir.close(); + + const file = try dir.openFile("info.json", .{}); + defer file.close(); + const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize)); + + const info = try std.json.parseFromSliceLeaky(UserInfo, allocator, content, .{}); + + return .{ + .dir = dir, + .arena = arena, + .info = info, + }; +} + +pub fn new( + storage: *Storage, + name: []const u8, + full_name: []const u8, + birthday: []const u8, + password: []const u8, + is_admin: bool, + inner_allocator: std.mem.Allocator +) !Self { + var arena: std.heap.ArenaAllocator = .init(inner_allocator); + const allocator = arena.allocator(); + errdefer arena.deinit(); + + var user_dir = try storage.dir.openDir("user", .{}); + defer user_dir.close(); + + try user_dir.makeDir(name); + var dir = try user_dir.openDir(name, .{}); + errdefer dir.close(); + + var file = try dir.createFile("info.json", .{}); + file.close(); + + const hash_buf = try allocator.alloc(u8, 256); + const hash = try std.crypto.pwhash.bcrypt.strHash( + password, + .{ + .params = .{ + .rounds_log = 3, + .silently_truncate_password = false, + }, + .encoding = .phc, + }, + hash_buf + ); + + const info: UserInfo = .{ + .name = name, + .full_name = full_name, + .birthday = birthday, + .hash = hash, + .is_admin = is_admin, + }; + + + return .{ + .dir = dir, + .arena = arena, + .info = info, + }; +} + +pub fn sync(self: *Self) !void { + const file = try self.dir.openFile("info.json", .{ .mode = .write_only }); + defer file.close(); + + var buffer: [1024]u8 = undefined; + var writer = file.writer(&buffer); + + var stringify = std.json.Stringify { + .writer = &writer.interface, + .options = .{ .whitespace = .indent_2 } + }; + + try stringify.write(self.info); + try writer.interface.flush(); +} + +pub fn check_password(self: *Self, password: []const u8) bool { + std.crypto.pwhash.bcrypt.strVerify(self.info.hash, password, .{ + .silently_truncate_password = false, + }) catch return false; + return true; +} + +pub fn deinit(self: *Self) void { + self.dir.close(); + self.arena.deinit(); + self.* = undefined; +} + +pub fn exists(storage: *Storage, name: []const u8) bool { + var user = storage.dir.openDir("user", .{}) catch return false; + defer user.deinit(); + + user.access(name) catch return false; + return true; +} |