From 4210aa5e8324122b9947abef79cb1e8dcdc33426 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Fri, 8 Nov 2024 20:42:54 +0000 Subject: [PATCH] WIP --- build.zig.zon | 4 +- src/jetzig/auth.zig | 4 ++ src/jetzig/config.zig | 6 ++- src/jetzig/http/Cookies.zig | 2 +- src/jetzig/middleware/AuthMiddleware.zig | 52 ++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/jetzig/middleware/AuthMiddleware.zig diff --git a/build.zig.zon b/build.zig.zon index c62b429..8f9565e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -17,8 +17,8 @@ .path = "../jetquery", }, .jetcommon = .{ - .url = "https://github.com/jetzig-framework/jetcommon/archive/5946df967d3cf2f843035464a6b8a17cb573afd3.tar.gz", - .hash = "12206a50c39a1766d24abcaa9f8fbeaaa20c853f987c667e0b8325e53d9c7ede7d24", + .url = "https://github.com/jetzig-framework/jetcommon/archive/a248776ba56d6cc2b160d593ac3305756adcd26e.tar.gz", + .hash = "1220a61e8650f84b28baf31fae5da31712aec4b711b3a41d11ed07c908bac96648d8", }, .args = .{ .url = "https://github.com/ikskuh/zig-args/archive/0abdd6947a70e6d8cc83b66228cea614aa856206.tar.gz", diff --git a/src/jetzig/auth.zig b/src/jetzig/auth.zig index 8e1fa96..457a862 100644 --- a/src/jetzig/auth.zig +++ b/src/jetzig/auth.zig @@ -4,6 +4,10 @@ const jetzig = @import("../jetzig.zig"); pub const IdType = enum { string, integer }; +pub const AuthOptions = struct { + user_model: []const u8, +}; + pub fn getUserId(comptime id_type: IdType, request: *jetzig.Request) !?switch (id_type) { .integer => i128, .string => []const u8, diff --git a/src/jetzig/config.zig b/src/jetzig/config.zig index 306d507..b81029d 100644 --- a/src/jetzig/config.zig +++ b/src/jetzig/config.zig @@ -151,11 +151,15 @@ pub const smtp: mail.SMTPConfig = .{ }; /// HTTP cookie configuration -pub const cookie_options: http.Cookies.CookieOptions = .{ +pub const cookies: http.Cookies.CookieOptions = .{ .domain = "localhost", .path = "/", }; +pub const auth: @import("auth.zig").AuthOptions = .{ + .user_model = "User", +}; + /// Force email delivery in development mode (instead of printing email body to logger). pub const force_development_email_delivery = false; diff --git a/src/jetzig/http/Cookies.zig b/src/jetzig/http/Cookies.zig index 30c5fe5..736715e 100644 --- a/src/jetzig/http/Cookies.zig +++ b/src/jetzig/http/Cookies.zig @@ -22,7 +22,7 @@ pub const CookieOptions = struct { partitioned: bool = false, }; -const cookie_options = jetzig.config.get(CookieOptions, "cookie_options"); +const cookie_options = jetzig.config.get(CookieOptions, "cookies"); pub const Cookie = struct { name: []const u8, diff --git a/src/jetzig/middleware/AuthMiddleware.zig b/src/jetzig/middleware/AuthMiddleware.zig new file mode 100644 index 0000000..122273b --- /dev/null +++ b/src/jetzig/middleware/AuthMiddleware.zig @@ -0,0 +1,52 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub const middleware_name = "auth"; + +// Default model is `.User`. +const user_model = jetzig.config.get(jetzig.auth.AuthOptions, "auth").user_model; + +/// Define any custom data fields you want to store here. Assigning to these fields in the `init` +/// function allows you to access them in the `beforeRequest` and `afterRequest` functions, where +/// they can also be modified. +user: ?@TypeOf(jetzig.database.Query(user_model).find(0)).ResultType(), + +const Self = @This(); + +/// Initialize middleware. +pub fn init(request: *jetzig.http.Request) !*Self { + const middleware = try request.allocator.create(Self); + middleware.* = .{ .user = null }; + return middleware; +} + +const map = std.StaticStringMap(void).initComptime(.{ + .{ ".html", void }, + .{ ".json", void }, +}); + +/// For HTML/JSON requests, fetch a user ID from the encrypted session cookie and execute a +/// database query to match the user ID to a database record. Expects a `User` model defined in +/// the schema, configurable with `auth.user_model`. +/// +/// User ID is accessible from a request: +/// ```zig +/// +pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void { + if (request.path.extension) |extension| { + if (map.get(extension) == null) return; + } + const user_id = try jetzig.auth.getUserId(.integer, request) orelse return; + + const query = jetzig.database.Query(user_model).find(user_id); + if (try request.repo.execute(query)) |user| { + self.user = user; + } +} + +/// Invoked after `afterRequest` is called, use this function to do any clean-up. +/// Note that `request.allocator` is an arena allocator, so any allocations are automatically +/// done before the next request starts processing. +pub fn deinit(self: *Self, request: *jetzig.http.Request) void { + request.allocator.destroy(self); +}