aboutsummaryrefslogtreecommitdiff
path: root/src/storage/user.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/storage/user.zig')
-rw-r--r--src/storage/user.zig133
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;
+}