2021-06-08 14:54:23 +00:00
|
|
|
|
|
|
|
let src = Logs.Src.create "authorization" ~doc:"Builder_web authorization"
|
|
|
|
module Log = (val Logs.src_log src : Logs.LOG)
|
|
|
|
|
|
|
|
open Lwt.Syntax
|
|
|
|
|
|
|
|
let realm = "builder-web"
|
|
|
|
|
|
|
|
let user_info_local = Dream.new_local ~name:"user_info" ()
|
|
|
|
|
|
|
|
let authenticate handler = fun req ->
|
|
|
|
let unauthorized () =
|
|
|
|
let headers = ["WWW-Authenticate", Printf.sprintf "Basic realm=\"%s\"" realm] in
|
|
|
|
Dream.respond ~headers ~status:`Unauthorized "Forbidden!"
|
|
|
|
in
|
2021-06-10 11:32:05 +00:00
|
|
|
match Dream.session "username" req with
|
|
|
|
| Some username ->
|
|
|
|
let* user_info = Dream.sql req (Model.user username) in
|
|
|
|
begin match user_info with
|
|
|
|
| Ok (Some (user_id, user_info)) ->
|
|
|
|
handler (Dream.with_local user_info_local (user_id, user_info) req)
|
|
|
|
| Ok None ->
|
|
|
|
Log.warn (fun m -> m "User %S from session doesn't exist" username);
|
|
|
|
let* () = Dream.invalidate_session req in
|
|
|
|
Dream.respond ~status:`Internal_Server_Error "Internal server error"
|
|
|
|
| Error e ->
|
|
|
|
Log.warn (fun m -> m "Error getting user: %a" Model.pp_error e);
|
|
|
|
Dream.respond ~status:`Internal_Server_Error "Internal server error"
|
|
|
|
end
|
|
|
|
| None ->
|
|
|
|
match Dream.header "Authorization" req with
|
|
|
|
| None -> unauthorized ()
|
|
|
|
| Some data -> match String.split_on_char ' ' data with
|
|
|
|
| [ "Basic" ; user_pass ] ->
|
|
|
|
(match Base64.decode user_pass with
|
|
|
|
| Error `Msg msg ->
|
|
|
|
Log.info (fun m -> m "Invalid user / pasword encoding in %S: %S" data msg);
|
2021-06-08 14:54:23 +00:00
|
|
|
Dream.respond ~status:`Bad_Request "Couldn't decode authorization"
|
2021-06-10 11:32:05 +00:00
|
|
|
| Ok user_pass -> match String.split_on_char ':' user_pass with
|
|
|
|
| [] | [_] ->
|
|
|
|
Log.info (fun m -> m "Invalid user / pasword encoding in %S" data);
|
|
|
|
Dream.respond ~status:`Bad_Request "Couldn't decode authorization"
|
|
|
|
| user :: password ->
|
|
|
|
let pass = String.concat ":" password in
|
|
|
|
let* user_info = Dream.sql req (Model.user user) in
|
|
|
|
match user_info with
|
|
|
|
| Ok (Some (id, user_info)) ->
|
|
|
|
if Builder_web_auth.verify_password pass user_info
|
|
|
|
then handler (Dream.with_local user_info_local (id, user_info) req)
|
|
|
|
else unauthorized ()
|
|
|
|
| Ok None ->
|
|
|
|
let _ : _ Builder_web_auth.user_info =
|
|
|
|
Builder_web_auth.hash ~username:user ~password:pass ~restricted:true () in
|
|
|
|
unauthorized ()
|
|
|
|
| Error e ->
|
|
|
|
Log.warn (fun m -> m "Error getting user: %a" Model.pp_error e);
|
|
|
|
Dream.respond ~status:`Internal_Server_Error "Internal server error")
|
|
|
|
| _ ->
|
|
|
|
Log.warn (fun m -> m "Error retrieving authorization %S" data);
|
|
|
|
Dream.respond ~status:`Bad_Request "Couldn't decode authorization"
|
2021-06-08 14:54:23 +00:00
|
|
|
|
|
|
|
let authorized req job_name =
|
|
|
|
match Dream.local user_info_local req with
|
|
|
|
| None -> Lwt.return (Error (`Msg "not authenticated"))
|
|
|
|
| Some (id, user) ->
|
|
|
|
if user.restricted then
|
|
|
|
Dream.sql req (Model.authorized id job_name)
|
|
|
|
else
|
|
|
|
Lwt_result.return ()
|