@@ -21,28 +21,94 @@ pub const Context = struct {
2121 trusted_proxies : []const network.Network ,
2222};
2323
24+ const Authorization = struct {
25+ pub const Config = struct {
26+ credential : []const u8 ,
27+ };
28+
29+ config : Config ,
30+ allocator : std.mem.Allocator ,
31+
32+ pub fn init (config : Config , mw_config : httpz.MiddlewareConfig ) ! Authorization {
33+ return .{ .config = config , .allocator = mw_config .allocator };
34+ }
35+
36+ pub fn execute (self : * const Authorization , req : * httpz.Request , res : * httpz.Response , executor : anytype ) ! void {
37+ const authorized = authorized : {
38+ const scheme = "Basic " ;
39+ const optional_authorization = req .header ("authorization" );
40+
41+ if (optional_authorization ) | authorization | {
42+ if (std .mem .startsWith (u8 , authorization , scheme )) {
43+ const encoded = authorization [scheme .len .. ];
44+
45+ const decoder = std .base64 .url_safe .Decoder ;
46+
47+ const bufsize = decoder .calcSizeForSlice (encoded ) catch break :authorized false ;
48+ const got = try self .allocator .alloc (u8 , bufsize );
49+ defer self .allocator .free (got );
50+
51+ decoder .decode (got , encoded ) catch break :authorized false ;
52+
53+ if (! std .mem .eql (u8 , got , self .config .credential )) break :authorized false ;
54+
55+ break :authorized true ;
56+ }
57+ }
58+
59+ break :authorized false ;
60+ };
61+
62+ if (! authorized ) {
63+ respondError (res , .unauthorized );
64+ } else {
65+ return executor .next ();
66+ }
67+ }
68+ };
69+
2470server : httpz .Server (* Context ),
71+ _api_middlewares : []const httpz .Middleware (* Context ),
2572
2673const App = @This ();
2774
2875pub fn init (ctx : * Context , config : httpz.Config ) ! App {
29- var app = App { .server = undefined };
76+ var app = App {
77+ .server = undefined ,
78+ ._api_middlewares = undefined ,
79+ };
80+
3081 app .server = try httpz .Server (* Context ).init (ctx .allocator , config , ctx );
3182
3283 var router = try app .server .router (.{});
33- router .get ("/api/bins" , fetchBins , .{});
34- router .put ("/api/bins" , createOrUpdateBin , .{});
35- router .get ("/api/bins/:bin" , inspectBin , .{});
36- router .delete ("/api/bins/:bin" , deleteBin , .{});
37- router .get ("/api/bins/:bin/captures" , viewBin , .{});
38- router .delete ("/api/bins/:bin/captures" , clearBin , .{});
39- router .get ("/api/bins/:bin/captures/:capture" , inspectCapture , .{});
40- router .delete ("/api/bins/:bin/captures/:capture" , deleteCapture , .{});
4184
4285 router .all ("/access/:bin" , captureAccess , .{});
43-
4486 router .get ("/" , serveDashboard , .{});
4587
88+ app ._api_middlewares = middlewares : {
89+ if (ctx .auth ) | credential | {
90+ break :middlewares try ctx .allocator .dupe (httpz .Middleware (* Context ), &.{
91+ try app .server .middleware (Authorization , .{ .credential = credential }),
92+ });
93+ } else {
94+ break :middlewares &.{};
95+ }
96+ };
97+
98+ var api_router = router .group (
99+ "/api" ,
100+ .{ .middlewares = app ._api_middlewares },
101+ );
102+
103+ api_router .get ("/bins" , fetchBins , .{});
104+ api_router .put ("/bins" , createOrUpdateBin , .{});
105+ api_router .get ("/bins/:bin" , inspectBin , .{});
106+ api_router .delete ("/bins/:bin" , deleteBin , .{});
107+ api_router .get ("/bins/:bin/captures" , viewBin , .{});
108+ api_router .delete ("/bins/:bin/captures" , clearBin , .{});
109+ api_router .get ("/bins/:bin/captures/:capture" , inspectCapture , .{});
110+ api_router .delete ("/bins/:bin/captures/:capture" , deleteCapture , .{});
111+
46112 return app ;
47113}
48114
@@ -142,35 +208,6 @@ fn captureAccess(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void
142208 }
143209}
144210
145- fn authorize (ctx : * Context , req : * httpz.Request ) ! bool {
146- if (ctx .auth ) | auth | {
147- const scheme = "Basic " ;
148- const optional_authorization = req .header ("authorization" );
149-
150- if (optional_authorization ) | authorization | {
151- if (std .mem .startsWith (u8 , authorization , scheme )) {
152- const enc_cred = authorization [scheme .len .. ];
153-
154- const decoder = std .base64 .url_safe .Decoder ;
155-
156- const bufsize = decoder .calcSizeForSlice (enc_cred ) catch return false ;
157- const cred = try ctx .allocator .alloc (u8 , bufsize );
158- defer ctx .allocator .free (cred );
159-
160- decoder .decode (cred , enc_cred ) catch return false ;
161-
162- if (! std .mem .eql (u8 , cred , auth )) return false ;
163-
164- return true ;
165- }
166- }
167-
168- return false ;
169- } else {
170- return true ;
171- }
172- }
173-
174211fn isValidBinName (name : []const u8 ) bool {
175212 if (name .len == 0 ) return false ;
176213
@@ -185,11 +222,6 @@ fn isValidBinName(name: []const u8) bool {
185222}
186223
187224fn viewBin (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
188- if (! try authorize (ctx , req )) {
189- respondError (res , .unauthorized );
190- return ;
191- }
192-
193225 const bin_name = req .param ("bin" ).? ;
194226
195227 const bin = try sql_query .bins .getId (ctx .db , bin_name ) orelse {
@@ -220,11 +252,6 @@ fn viewBin(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
220252}
221253
222254fn fetchBins (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
223- if (! try authorize (ctx , req )) {
224- respondError (res , .unauthorized );
225- return ;
226- }
227-
228255 const query = try req .query ();
229256 const options = models .PageParams .parseFromStringKeyValue (query ) catch {
230257 respondError (res , .unprocessable_entity );
@@ -242,11 +269,6 @@ fn fetchBins(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
242269}
243270
244271fn createOrUpdateBin (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
245- if (! try authorize (ctx , req )) {
246- respondError (res , .unauthorized );
247- return ;
248- }
249-
250272 var bin = req .json (models .Bin ) catch null orelse {
251273 respondError (res , .bad_request );
252274 return ;
@@ -274,11 +296,6 @@ fn createOrUpdateBin(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !
274296}
275297
276298fn inspectBin (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
277- if (! try authorize (ctx , req )) {
278- respondError (res , .unauthorized );
279- return ;
280- }
281-
282299 const bin_name = req .param ("bin" ).? ;
283300
284301 var arena = std .heap .ArenaAllocator .init (ctx .allocator );
@@ -293,11 +310,6 @@ fn inspectBin(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
293310}
294311
295312fn deleteBin (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
296- if (! try authorize (ctx , req )) {
297- respondError (res , .unauthorized );
298- return ;
299- }
300-
301313 const bin_name = req .param ("bin" ).? ;
302314
303315 try sql_query .bins .delete (ctx .db , bin_name );
@@ -306,11 +318,6 @@ fn deleteBin(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
306318}
307319
308320fn clearBin (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
309- if (! try authorize (ctx , req )) {
310- respondError (res , .unauthorized );
311- return ;
312- }
313-
314321 const bin_name = req .param ("bin" ).? ;
315322 const bin = try sql_query .bins .getId (ctx .db , bin_name ) orelse {
316323 respondError (res , .not_found );
@@ -323,11 +330,6 @@ fn clearBin(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
323330}
324331
325332fn inspectCapture (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
326- if (! try authorize (ctx , req )) {
327- respondError (res , .unauthorized );
328- return ;
329- }
330-
331333 const bin_name = req .param ("bin" ).? ;
332334 const capture_id = std .fmt .parseInt (i64 , req .param ("capture" ).? , 10 ) catch {
333335 respondError (res , .unprocessable_entity );
@@ -351,11 +353,6 @@ fn inspectCapture(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !voi
351353}
352354
353355fn deleteCapture (ctx : * Context , req : * httpz.Request , res : * httpz.Response ) ! void {
354- if (! try authorize (ctx , req )) {
355- respondError (res , .unauthorized );
356- return ;
357- }
358-
359356 const bin_name = req .param ("bin" ).? ;
360357 const capture = std .fmt .parseInt (i64 , req .param ("capture" ).? , 10 ) catch {
361358 respondError (res , .unprocessable_entity );
0 commit comments