ocaml http/1, http/2 and websocket client and server library
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(las): Phoenix-style auth with Pipeline and scopes

- Add Auth.require_authenticated as proper Plug.t
- Organize routes into scopes: browser, public, auth-protected
- Change Route.plug signature to plug-first for |> piping
- Apply negotiate plug to browser scope, auth plug to protected scope
- Per-route plugs for rate limiting on login/register/submit

+42 -34
+7 -2
bin/las/auth.ml
··· 35 35 36 36 let logout () = Hcs.Plug.Session.clear () 37 37 38 - let require_auth handler (req : Hcs.Server.request) = 38 + let require_authenticated : Hcs.Plug.t = 39 + fun handler req -> 39 40 match current_user_id () with 40 41 | None -> Hcs.Response.redirect "/login" 41 42 | Some _ -> handler req 42 43 43 - let require_auth_api handler (req : Hcs.Server.request) = 44 + let require_authenticated_api : Hcs.Plug.t = 45 + fun handler req -> 44 46 match current_user_id () with 45 47 | None -> Hcs.Response.unauthorized () 46 48 | Some _ -> handler req 49 + 50 + let require_auth handler req = require_authenticated handler req 51 + let require_auth_api handler req = require_authenticated_api handler req
+33 -30
bin/las/routes.ml
··· 1 1 open Hcs 2 2 3 - let negotiate = Plug.Negotiate.create ~formats:[ Json; Html ] () 4 - 5 - let with_negotiate handler params req = 6 - Plug.apply negotiate (handler params) req 7 - 8 3 let router clock = 4 + let negotiate = Plug.Negotiate.create ~formats:[ Json; Html ] () in 9 5 let submit_rate_limit = 10 6 Plug.Rate_limit.create ~clock 11 7 ~key:(fun _ -> ··· 19 15 |> Option.value ~default:"unknown") 20 16 ~requests:5 ~per:60.0 21 17 in 22 - Router.compile 18 + 19 + let browser = Pipeline.create [ negotiate ] in 20 + let auth = Pipeline.create [ Auth.require_authenticated ] in 21 + 22 + Router.compile_scopes 23 23 [ 24 - Router.Route.get "/favicon.ico" Favicon.handler; 25 - Router.Route.get "/" (with_negotiate Handlers.index); 26 - Router.Route.get "/new" (with_negotiate Handlers.index_new); 27 - Router.Route.get "/links/:id" (with_negotiate Handlers.show_link); 28 - Router.Route.get "/login" Handlers.login_form; 29 - Router.Route.post "/login" (fun params req -> 30 - Plug.apply auth_rate_limit (Handlers.login_submit params) req); 31 - Router.Route.get "/register" Handlers.register_form; 32 - Router.Route.post "/register" (fun params req -> 33 - Plug.apply auth_rate_limit (Handlers.register_submit params) req); 34 - Router.Route.post "/logout" (fun params req -> 35 - Auth.require_auth (Handlers.logout_submit params) req); 36 - Router.Route.get "/submit" (fun params req -> 37 - Auth.require_auth (Handlers.submit_form params) req); 38 - Router.Route.post "/links" (fun params req -> 39 - Plug.apply submit_rate_limit 40 - (Auth.require_auth (fun r -> 41 - Plug.apply negotiate (Handlers.create_link params) r)) 42 - req); 43 - Router.Route.post "/links/:id/vote" Handlers.vote; 44 - Router.Route.post "/links/:id/comments" (fun params req -> 45 - Auth.require_auth (Handlers.create_comment params) req); 46 - Router.Route.get "/events" (fun params req -> 47 - Auth.require_auth (Realtime.sse_handler params) req); 24 + Router.scope "/" ~through:browser 25 + [ 26 + Router.Route.get "/" Handlers.index; 27 + Router.Route.get "/new" Handlers.index_new; 28 + Router.Route.get "/links/:id" Handlers.show_link; 29 + Router.Route.get "/favicon.ico" Favicon.handler; 30 + ]; 31 + Router.scope "/" ~through:Pipeline.empty 32 + [ 33 + Router.Route.get "/login" Handlers.login_form; 34 + Router.Route.post "/login" Handlers.login_submit 35 + |> Router.Route.plug auth_rate_limit; 36 + Router.Route.get "/register" Handlers.register_form; 37 + Router.Route.post "/register" Handlers.register_submit 38 + |> Router.Route.plug auth_rate_limit; 39 + ]; 40 + Router.scope "/" ~through:auth 41 + [ 42 + Router.Route.post "/logout" Handlers.logout_submit; 43 + Router.Route.get "/submit" Handlers.submit_form; 44 + Router.Route.post "/links" Handlers.create_link 45 + |> Router.Route.plug submit_rate_limit 46 + |> Router.Route.plug negotiate; 47 + Router.Route.post "/links/:id/vote" Handlers.vote; 48 + Router.Route.post "/links/:id/comments" Handlers.create_comment; 49 + Router.Route.get "/events" Realtime.sse_handler; 50 + ]; 48 51 ]
+1 -1
lib/router.ml
··· 136 136 { method_ = Some `OPTIONS; path; handler; plugs = [] } 137 137 138 138 let any path handler = { method_ = None; path; handler; plugs = [] } 139 - let plug route p = { route with plugs = Pipeline.plug route.plugs p } 139 + let plug p route = { route with plugs = Pipeline.plug route.plugs p } 140 140 end 141 141 142 142 let normalize_path path =
+1 -1
test/test_hcs.ml
··· 672 672 let test_route_with_plug () = 673 673 let dummy_plug : Hcs.Plug.t = fun handler -> handler in 674 674 let base_route = Route.get "/admin" "admin_handler" in 675 - let route = Route.plug base_route dummy_plug in 675 + let route = Route.plug dummy_plug base_route in 676 676 let router = compile [ route ] in 677 677 match lookup router ~method_:`GET ~path:"/admin" with 678 678 | Some { handler; plugs; _ } ->