aboutsummaryrefslogtreecommitdiff
path: root/src/storage
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2025-11-15 11:54:00 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2025-11-15 11:56:24 +0100
commite8ff326f0e5bd61fb0a8c22c48f296fcf0249830 (patch)
tree19d8c026f8460ec577f446a7d61e68c38acddde0 /src/storage
parent007bc3fe0615f38cb31d5116759ed3500f6fd3e6 (diff)
backend implement auth-service
Diffstat (limited to 'src/storage')
-rw-r--r--src/storage/root.zig9
-rw-r--r--src/storage/session-manager/root.zig59
-rw-r--r--src/storage/session-manager/session.zig34
-rw-r--r--src/storage/user.zig36
4 files changed, 133 insertions, 5 deletions
diff --git a/src/storage/root.zig b/src/storage/root.zig
index bf9803e..f1a753c 100644
--- a/src/storage/root.zig
+++ b/src/storage/root.zig
@@ -3,10 +3,14 @@ const config = @import("../config.zig");
const prompt = @import("../prompt.zig");
pub const User = @import("user.zig");
+pub const SessionManager = @import("session-manager/root.zig");
+pub const Session = SessionManager.Session;
const Self = @This();
dir: std.fs.Dir,
+sessions: SessionManager = .empty,
+allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator) !Self {
const dir = std.fs.cwd().openDir(config.storage_path, .{}) catch blk: {
@@ -14,7 +18,10 @@ pub fn init(allocator: std.mem.Allocator) !Self {
break :blk try std.fs.cwd().openDir(config.storage_path, .{});
};
- var self = Self { .dir = dir };
+ var self = Self {
+ .dir = dir,
+ .allocator = allocator,
+ };
dir.access("user", .{}) catch |err| switch (err) {
error.FileNotFound => {
diff --git a/src/storage/session-manager/root.zig b/src/storage/session-manager/root.zig
new file mode 100644
index 0000000..7f44bf0
--- /dev/null
+++ b/src/storage/session-manager/root.zig
@@ -0,0 +1,59 @@
+const std = @import("std");
+const config = @import("../../config.zig");
+const Storage = @import("../root.zig");
+
+pub const Session = @import("session.zig");
+
+const Self = @This();
+
+pub const empty: Self = .{
+ .cache = .empty,
+};
+
+cache: std.StringHashMapUnmanaged(Session),
+
+pub fn add(self: *Self, storage: *Storage, info: Storage.User.Info) !*Session {
+ const session = try Session.init(storage.allocator, info);
+ errdefer session.deinit(storage.allocator);
+ try self.cache.put(storage.allocator, session.fingerprint, session);
+ return self.cache.getPtr(session.fingerprint) orelse unreachable;
+}
+
+pub fn renew(self: *Self, storage: *Storage, fingerprint: []const u8) !*Session {
+ if (self.cache.get(fingerprint)) |s| {
+ var session = s;
+ try session.reset();
+
+ try self.cache.put(storage.allocator, session.fingerprint, session);
+
+ _ = self.cache.remove(fingerprint);
+
+ return self.cache.getPtr(session.fingerprint) orelse unreachable;
+ }
+ return error.SessionNotFound;
+}
+
+pub fn remove(self: *Self, storage: *Storage, fingerprint: []const u8) void {
+ if (self.cache.getPtr(fingerprint)) |session| {
+ session.deinit(storage.allocator);
+ _ = self.cache.remove(fingerprint);
+ }
+}
+
+pub fn get(self: *Self, storage: *Storage, fingerprint: []const u8) ?*Session {
+ if (self.cache.getPtr(fingerprint)) |session| {
+ const now = std.time.Instant.now() catch return null;
+ const since = now.since(session.age);
+
+ if (since > config.session_expires_after) {
+ std.debug.print("here\n", .{});
+ session.deinit(storage.allocator);
+ _ = self.cache.remove(fingerprint);
+ return null;
+ }
+
+ return session;
+ }
+
+ return null;
+}
diff --git a/src/storage/session-manager/session.zig b/src/storage/session-manager/session.zig
new file mode 100644
index 0000000..943334b
--- /dev/null
+++ b/src/storage/session-manager/session.zig
@@ -0,0 +1,34 @@
+const std = @import("std");
+const User = @import("../user.zig");
+
+const Self = @This();
+
+const fingerprint_size = 512;
+
+age: std.time.Instant,
+info: User.Info,
+fingerprint: []u8,
+
+pub fn init(allocator: std.mem.Allocator, info: User.Info) !Self {
+ var self: Self = undefined;
+ self.info = try info.clone(allocator);
+ self.fingerprint = try allocator.alloc(u8, fingerprint_size);
+ try self.reset();
+
+ return self;
+}
+
+pub fn reset(self: *Self) !void {
+ var raw_buffer: [fingerprint_size / 2]u8 = undefined;
+ std.crypto.random.bytes(&raw_buffer);
+
+ var writer = std.Io.Writer.fixed(self.fingerprint);
+ writer.print("{x}", .{raw_buffer}) catch unreachable;
+
+ self.age = try std.time.Instant.now();
+}
+
+pub fn deinit(self: *const Self, allocator: std.mem.Allocator) void {
+ self.info.deinit(allocator);
+ allocator.free(self.fingerprint);
+}
diff --git a/src/storage/user.zig b/src/storage/user.zig
index c981661..4170fd1 100644
--- a/src/storage/user.zig
+++ b/src/storage/user.zig
@@ -3,16 +3,44 @@ const Storage = @import("root.zig");
const Self = @This();
-pub const UserInfo = struct {
+pub const Info = struct {
name: []const u8,
full_name: []const u8,
birthday: []const u8,
hash: []const u8,
is_admin: bool,
+
+ pub fn clone(self: *const @This(), allocator: std.mem.Allocator) !@This() {
+ var clone_self: @This() = undefined;
+ var i: usize = 0;
+
+ inline for (std.meta.fields(@This())) |field_info| {
+ if (field_info.type == []const u8) {
+ @field(clone_self, field_info.name) = try allocator.dupe(
+ u8,
+ @field(self, field_info.name),
+ );
+ errdefer allocator.free(@field(clone_self, field_info.name));
+ } else {
+ @field(clone_self, field_info.name) = @field(self, field_info.name);
+ }
+ i += 1;
+ }
+
+ return clone_self;
+ }
+
+ pub fn deinit(self: *const @This(), allocator: std.mem.Allocator) void {
+ inline for (std.meta.fields(@This())) |field_info| {
+ if (field_info.type == []const u8) {
+ allocator.free(@field(self, field_info.name));
+ }
+ }
+ }
};
dir: std.fs.Dir,
-info: UserInfo,
+info: Info,
arena: std.heap.ArenaAllocator,
pub fn open(
@@ -34,7 +62,7 @@ pub fn open(
defer file.close();
const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
- const info = try std.json.parseFromSliceLeaky(UserInfo, allocator, content, .{});
+ const info = try std.json.parseFromSliceLeaky(Info, allocator, content, .{});
return .{
.dir = dir,
@@ -79,7 +107,7 @@ pub fn new(
hash_buf
);
- const info: UserInfo = .{
+ const info: Info = .{
.name = name,
.full_name = full_name,
.birthday = birthday,