From 94cf1228477bcd436126bfc49c06ee26a99fecd6 Mon Sep 17 00:00:00 2001 From: U-Zyn Chua Date: Wed, 16 Apr 2025 15:59:18 +0800 Subject: [PATCH 1/3] Allow setting of custom session cookie name. --- src/jetzig/http/Session.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/jetzig/http/Session.zig b/src/jetzig/http/Session.zig index 1c96a60..1b0c341 100644 --- a/src/jetzig/http/Session.zig +++ b/src/jetzig/http/Session.zig @@ -2,12 +2,12 @@ const std = @import("std"); const jetzig = @import("../../jetzig.zig"); -pub const cookie_name = "_jetzig-session"; pub const Cipher = std.crypto.aead.chacha_poly.XChaCha20Poly1305; allocator: std.mem.Allocator, encryption_key: []const u8, cookies: *jetzig.http.Cookies, +cookie_name: []const u8, initialized: bool = false, data: jetzig.data.Data, @@ -20,17 +20,21 @@ pub fn init( cookies: *jetzig.http.Cookies, encryption_key: []const u8, ) Self { + const env_cookie_name = std.process.getEnvVarOwned(allocator, "JETZIG_SESSION_COOKIE") catch null; + const cookie_name = env_cookie_name orelse "_jetzig-session"; + return .{ .allocator = allocator, .data = jetzig.data.Data.init(allocator), .cookies = cookies, + .cookie_name = cookie_name, .encryption_key = encryption_key, }; } /// Parse session cookie. pub fn parse(self: *Self) !void { - if (self.cookies.get(cookie_name)) |cookie| { + if (self.cookies.get(self.cookie_name)) |cookie| { try self.parseSessionCookie(cookie.value); } else { try self.reset(); @@ -111,7 +115,7 @@ fn save(self: *Self) !void { defer self.allocator.free(encrypted); const encoded = try jetzig.util.base64Encode(self.allocator, encrypted); defer self.allocator.free(encoded); - try self.cookies.put(.{ .name = cookie_name, .value = encoded }); + try self.cookies.put(.{ .name = self.cookie_name, .value = encoded }); } fn parseSessionCookie(self: *Self, cookie_value: []const u8) !void { From 4c568f2606c914cedb6b5d67d3c2e91e2da7659a Mon Sep 17 00:00:00 2001 From: U-Zyn Chua Date: Wed, 16 Apr 2025 15:59:45 +0800 Subject: [PATCH 2/3] Minor typo fix --- demo/src/app/views/anti_csrf/index.zmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/src/app/views/anti_csrf/index.zmpl b/demo/src/app/views/anti_csrf/index.zmpl index 4b914a7..c1442c1 100644 --- a/demo/src/app/views/anti_csrf/index.zmpl +++ b/demo/src/app/views/anti_csrf/index.zmpl @@ -7,4 +7,4 @@ -
Try clearing `_jetzig_session` cookie before clicking "Submit Spam"
+
Try clearing `_jetzig-session` cookie before clicking "Submit Spam"
From aae9fd182b9bedae9ce49af0b0e204d5e0b28d0e Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Wed, 16 Apr 2025 19:29:44 +0100 Subject: [PATCH 3/3] Use `Server.env.vars` to get session cookie name --- src/jetzig/http/Request.zig | 4 +++- src/jetzig/http/Session.zig | 39 +++++++++++++++++++++++++++---------- src/jetzig/testing/App.zig | 4 ++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/jetzig/http/Request.zig b/src/jetzig/http/Request.zig index d6517fc..4e31d3e 100644 --- a/src/jetzig/http/Request.zig +++ b/src/jetzig/http/Request.zig @@ -497,12 +497,14 @@ pub fn cookies(self: *Request) !*jetzig.http.Cookies { /// `jetzig.http.Session`. pub fn session(self: *Request) !*jetzig.http.Session { if (self._session) |capture| return capture; - + const cookie_name = self.server.env.vars.get("JETZIG_SESSION_COOKIE") orelse + jetzig.http.Session.default_cookie_name; const local_session = try self.allocator.create(jetzig.http.Session); local_session.* = jetzig.http.Session.init( self.allocator, try self.cookies(), self.server.env.secret, + .{ .cookie_name = cookie_name }, ); local_session.parse() catch |err| { switch (err) { diff --git a/src/jetzig/http/Session.zig b/src/jetzig/http/Session.zig index 1b0c341..384745b 100644 --- a/src/jetzig/http/Session.zig +++ b/src/jetzig/http/Session.zig @@ -15,19 +15,23 @@ state: enum { parsed, pending } = .pending, const Self = @This(); +pub const default_cookie_name = "_jetzig-session"; + +pub const Options = struct { + cookie_name: []const u8 = default_cookie_name, +}; + pub fn init( allocator: std.mem.Allocator, cookies: *jetzig.http.Cookies, encryption_key: []const u8, + options: Options, ) Self { - const env_cookie_name = std.process.getEnvVarOwned(allocator, "JETZIG_SESSION_COOKIE") catch null; - const cookie_name = env_cookie_name orelse "_jetzig-session"; - return .{ .allocator = allocator, .data = jetzig.data.Data.init(allocator), .cookies = cookies, - .cookie_name = cookie_name, + .cookie_name = options.cookie_name, .encryption_key = encryption_key, }; } @@ -184,7 +188,7 @@ test "put and get session key/value" { try cookies.parse(); const secret: [Cipher.key_length]u8 = [_]u8{0x69} ** Cipher.key_length; - var session = Self.init(allocator, &cookies, &secret); + var session = Self.init(allocator, &cookies, &secret, .{}); defer session.deinit(); var data = jetzig.data.Data.init(allocator); @@ -203,7 +207,7 @@ test "remove session key/value" { try cookies.parse(); const secret: [Cipher.key_length]u8 = [_]u8{0x69} ** Cipher.key_length; - var session = Self.init(allocator, &cookies, &secret); + var session = Self.init(allocator, &cookies, &secret, .{}); defer session.deinit(); var data = jetzig.data.Data.init(allocator); @@ -228,7 +232,7 @@ test "get value from parsed/decrypted cookie" { try cookies.parse(); const secret: [Cipher.key_length]u8 = [_]u8{0x69} ** Cipher.key_length; - var session = Self.init(allocator, &cookies, &secret); + var session = Self.init(allocator, &cookies, &secret, .{}); defer session.deinit(); try session.parse(); @@ -237,17 +241,32 @@ test "get value from parsed/decrypted cookie" { } test "invalid cookie value - too short" { + const allocator = std.testing.allocator; + var cookies = jetzig.http.Cookies.init(allocator, "_jetzig-session=abc"); + defer cookies.deinit(); + try cookies.parse(); + + const secret: [Cipher.key_length]u8 = [_]u8{0x69} ** Cipher.key_length; + var session = Self.init(allocator, &cookies, &secret, .{}); + defer session.deinit(); + + try std.testing.expectError(error.JetzigInvalidSessionCookie, session.parse()); +} + +test "custom session cookie name" { const allocator = std.testing.allocator; var cookies = jetzig.http.Cookies.init( allocator, - "_jetzig-session=abc", + "custom-cookie-name=fPCFwZHvPDT-XCVcsQUSspDLchS3tRuJDqPpB2v3127VXpRP_bPcPLgpHK6RiVkfcP1bMtU", ); defer cookies.deinit(); try cookies.parse(); const secret: [Cipher.key_length]u8 = [_]u8{0x69} ** Cipher.key_length; - var session = Self.init(allocator, &cookies, &secret); + var session = Self.init(allocator, &cookies, &secret, .{ .cookie_name = "custom-cookie-name" }); defer session.deinit(); - try std.testing.expectError(error.JetzigInvalidSessionCookie, session.parse()); + try session.parse(); + var value = (session.get("foo")).?; + try std.testing.expectEqualStrings("bar", try value.toString()); } diff --git a/src/jetzig/testing/App.zig b/src/jetzig/testing/App.zig index f3c2a4f..ddc2060 100644 --- a/src/jetzig/testing/App.zig +++ b/src/jetzig/testing/App.zig @@ -55,7 +55,7 @@ pub fn init(allocator: std.mem.Allocator, routes_module: type) !App { try cookies.parse(); const session = try alloc.create(jetzig.http.Session); - session.* = jetzig.http.Session.init(alloc, cookies, jetzig.testing.secret); + session.* = jetzig.http.Session.init(alloc, cookies, jetzig.testing.secret, .{}); app.* = App{ .arena = arena, @@ -237,7 +237,7 @@ pub fn initSession(self: *App) !void { const allocator = self.arena.allocator(); var local_session = try allocator.create(jetzig.http.Session); - local_session.* = jetzig.http.Session.init(allocator, self.cookies, jetzig.testing.secret); + local_session.* = jetzig.http.Session.init(allocator, self.cookies, jetzig.testing.secret, .{}); try local_session.parse(); self.session = local_session;