From f77f4053205306b62f4ef2230e597d6b19f269c5 Mon Sep 17 00:00:00 2001 From: renerocksai Date: Sun, 16 Mar 2025 16:28:23 +0100 Subject: [PATCH] doc update --- doc/authentication.md | 49 ++++++++++++++++++++------------------ src/endpoint.zig | 55 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 27 deletions(-) diff --git a/doc/authentication.md b/doc/authentication.md index 0df5483..019c3b2 100644 --- a/doc/authentication.md +++ b/doc/authentication.md @@ -9,11 +9,13 @@ authentication, see the [UserPassSession](../src/http_auth.zig#L319) and its For convenience, Authenticator types exist that can authenticate requests. -Zap also provides an `Endpoint.Authenticating` endpoint-wrapper. Have a look at the [example](../examples/endpoint_auth) and the [tests](../src/tests/test_auth.zig). +Zap also provides an `Endpoint.Authenticating` endpoint-wrapper. Have a look at +the [example](../examples/endpoint_auth) and the +[tests](../src/tests/test_auth.zig). The following describes the Authenticator types. All of them provide the -`authenticateRequest()` function, which takes a `zap.Request` and returns -a bool value whether it could be authenticated or not. +`authenticateRequest()` function, which takes a `zap.Request` and returns a bool +value whether it could be authenticated or not. Further down, we show how to use the Authenticators, and also the `Endpoint.Authenticating`. @@ -24,7 +26,7 @@ The `zap.Auth.Basic` Authenticator accepts 2 comptime values: - `Lookup`: either a map to look up passwords for users or a set to lookup base64 encoded tokens (user:pass -> base64-encode = token) -- `kind` : +- `kind` : - `UserPass` : decode the authentication header, split into user and password, then lookup the password in the provided map and compare it. - `Token68` : don't bother decoding, the 'lookup' set is filled with @@ -187,7 +189,8 @@ fn on_request(r: zap.Request) void { Here, we only show using one of the Authenticator types. See the tests for more examples. -The `Endpoint.Authenticating` honors `.unauthorized` in the endpoint settings, where you can pass in a callback to deal with unauthorized requests. If you leave it to `null`, the endpoint will automatically reply with a `401 - Unauthorized` response. +The `Endpoint.Authenticating` uses the `.unauthorized()` method of the endpoint +to deal with unauthorized requests. `unauthorized` must be implemented. The example below should make clear how to wrap an endpoint into an `Endpoint.Authenticating`: @@ -205,18 +208,20 @@ const HTTP_RESPONSE: []const u8 = \\ ; -// authenticated requests go here -fn endpoint_http_get(e: *zap.Endpoint, r: zap.Request) void { - _ = e; - r.sendBody(HTTP_RESPONSE) catch return; -} +pub const Endpoint = struct { + path: []const u8, -// just for fun, we also catch the unauthorized callback -fn endpoint_http_unauthorized(e: *zap.Endpoint, r: zap.Request) void { - _ = e; - r.setStatus(.unauthorized); - r.sendBody("UNAUTHORIZED ACCESS") catch return; -} + // authenticated requests go here + fn get(_: *Endpoint, r: zap.Request) void { + r.sendBody(HTTP_RESPONSE) catch return; + } + + // just for fun, we also catch the unauthorized callback + fn unauthorized(_: *Endpoint, r: zap.Request) void { + r.setStatus(.unauthorized); + r.sendBody("UNAUTHORIZED ACCESS") catch return; + } +}; pub fn main() !void { // setup listener @@ -233,11 +238,9 @@ pub fn main() !void { defer listener.deinit(); // create mini endpoint - var ep = zap.Endpoint.init(.{ + var ep : Endpoint = .{ .path = "/test", - .get = endpoint_http_get, - .unauthorized = endpoint_http_unauthorized, - }); + }; // create authenticator const Authenticator = zap.Auth.BearerSingle; @@ -245,15 +248,15 @@ pub fn main() !void { defer authenticator.deinit(); // create authenticating endpoint - const BearerAuthEndpoint = zap.Endpoint.Authenticating(Authenticator); + const BearerAuthEndpoint = zap.Endpoint.Authenticating(Endpoint, Authenticator); var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator); - try listener.register(auth_ep.endpoint()); + try listener.register(&auth_ep); listener.listen() catch {}; std.debug.print( \\ Run the following: - \\ + \\ \\ curl http://localhost:3000/test -i -H "Authorization: Bearer ABCDEFG" -v \\ curl http://localhost:3000/test -i -H "Authorization: Bearer invalid" -v \\ diff --git a/src/endpoint.zig b/src/endpoint.zig index 236e33a..11515b5 100644 --- a/src/endpoint.zig +++ b/src/endpoint.zig @@ -1,9 +1,57 @@ +//! Endpoint and supporting types. +//! Create one and define all callbacks. Then, pass it to a HttpListener's +//! `register()` function to register with the listener. +//! +//! **NOTE**: Endpoints must implement the following "interface": +//! +//! ```zig +//! /// The http request path / slug of the endpoint +//! path: []const u8, +//! +//! /// Handlers by request method: +//! pub fn get(_: *Self, _: zap.Request) void {} +//! pub fn post(_: *Self, _: zap.Request) void {} +//! pub fn put(_: *Self, _: zap.Request) void {} +//! pub fn delete(_: *Self, _: zap.Request) void {} +//! pub fn patch(_: *Self, _: zap.Request) void {} +//! pub fn options(_: *Self, _: zap.Request) void {} +//! +//! // optional, if auth stuff is used: +//! pub fn unauthorized(_: *Self, _: zap.Request) void {} +//! ``` +//! +//! Example: +//! A simple endpoint listening on the /stop route that shuts down zap. The +//! main thread usually continues at the instructions after the call to +//! zap.start(). +//! +//! ```zig +//! const StopEndpoint = struct { +//! +//! pub fn init( path: []const u8,) StopEndpoint { +//! return .{ +//! .path = path, +//! }; +//! } +//! +//! pub fn post(_: *Self, _: zap.Request) void {} +//! pub fn put(_: *Self, _: zap.Request) void {} +//! pub fn delete(_: *Self, _: zap.Request) void {} +//! pub fn patch(_: *Self, _: zap.Request) void {} +//! pub fn options(_: *Self, _: zap.Request) void {} +//! +//! pub fn get(self: *StopEndpoint, r: zap.Request) void { +//! _ = self; +//! _ = r; +//! zap.stop(); +//! } +//! }; +//! ``` + const std = @import("std"); const zap = @import("zap.zig"); const auth = @import("http_auth.zig"); -const Endpoint = @This(); - // zap types const Request = zap.Request; const ListenerSettings = zap.HttpListenerSettings; @@ -93,8 +141,7 @@ const EndpointWrapper = struct { } }; -/// Wrap an endpoint with an Authenticator -> new Endpoint of type Endpoint -/// is available via the `endpoint()` function. +/// Wrap an endpoint with an Authenticator pub fn Authenticating(EndpointType: type, Authenticator: type) type { return struct { authenticator: *Authenticator,