Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logout functionality #87

Merged
merged 24 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions assets/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
document.addEventListener('DOMContentLoaded', function () {
AOS.init();

const flashMessage = getCookie('flash_msg');
if (flashMessage) {
if (flashMessage.startsWith("error:")) {
postAlert("bg-secondary-300", flashMessage);
} else {
postAlert("bg-primary-300", flashMessage);
}
document.cookie = "flash_msg=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 UTC;";
PizieDust marked this conversation as resolved.
Show resolved Hide resolved
}

if (window.location.pathname.startsWith("/admin/user/")) {
const tabs = document.querySelectorAll(".tab-link");
const tabPanes = document.querySelectorAll(".tab-pane");
Expand All @@ -25,6 +36,16 @@ document.addEventListener('DOMContentLoaded', function () {
}
});

function getCookie(name) {
const cookies = document.cookie.split(";");
for (let cookie of cookies) {
const [cookieName, cookieValue] = cookie.split("=");
if (cookieName === name) {
return decodeURIComponent(cookieValue);
}
}
}

function getUnikernelName(url) {
const urlObj = new URL(url);
const pathParts = urlObj.pathname.split('/');
Expand Down Expand Up @@ -142,7 +163,7 @@ function postAlert(bg_color, content) {
alertContainer.classList.remove("block", `${bg_color}`)
alertContainer.classList.add("hidden")
alertContainer.removeChild(alert);
}, 1600);
}, 2500);
}

async function deployUnikernel() {
Expand Down Expand Up @@ -427,7 +448,7 @@ async function updatePassword() {
const new_password = document.getElementById("new-password").value;
const confirm_password = document.getElementById("confirm-password").value;
const formAlert = document.getElementById("form-alert");
if (!current_password || !new_password || !confirm_password ) {
if (!current_password || !new_password || !confirm_password) {
formAlert.classList.remove("hidden", "text-primary-500");
formAlert.classList.add("text-secondary-500");
formAlert.textContent = "Please fill in all the required passwords"
Expand Down Expand Up @@ -488,3 +509,23 @@ async function closeSessions() {
buttonLoading(sessionButton, false, "Logout all other sessions")
}
}

async function logout() {
const logoutButton = document.getElementById("logout-button");
try {
buttonLoading(logoutButton, true, "Closing session..")
const molly_csrf = document.getElementById("molly-csrf").value;
fetch('/logout', {
method: 'POST',
body: JSON.stringify(
{
molly_csrf,
}),
headers: { 'Content-Type': 'application/json' }
});
setTimeout(() => window.location.reload(), 1000);
} catch (error) {
postAlert("bg-secondary-300", error);
buttonLoading(logoutButton, false, "Logout")
}
}
18 changes: 16 additions & 2 deletions dashboard.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open Tyxml

let dashboard_layout (user : User_model.user) ~icon
let dashboard_layout ~csrf (user : User_model.user) ~icon
?(page_title = "Dashboard | Mollymawk") ?message ~content () =
let page =
Html.(
Expand All @@ -18,6 +18,7 @@ let dashboard_layout (user : User_model.user) ~icon
];
]
[
Utils.csrf_form_input csrf;
div
~a:
[
Expand Down Expand Up @@ -154,7 +155,7 @@ let dashboard_layout (user : User_model.user) ~icon
a_class
[
"absolute top-1/4 rounded-md right-4 z-50 w-fit \
space-y-2 p-4 shadow border text-wrap hidden";
space-y-2 p-4 shadow text-wrap hidden";
];
]
[];
Expand Down Expand Up @@ -544,6 +545,19 @@ let dashboard_layout (user : User_model.user) ~icon
];
]
else div []);
button
~a:
[
a_id "logout-button";
a_onclick "logout()";
a_class
[
"my-3 py-3 rounded bg-secondary-500 \
hover:bg-secondary-800 w-full text-gray-50 \
font-semibold";
];
]
[ txt "Logout" ];
];
];
section
Expand Down
70 changes: 39 additions & 31 deletions middleware.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,48 @@ let cookie cookie_name (reqd : Httpaf.Reqd.t) =
(fun cookie ->
let parts = cookie |> String.split_on_char '=' in
match parts with
| [ name; _ ] -> String.equal name cookie_name
| [ name; _ ] -> String.equal (String.trim name) cookie_name
| _ -> false)
cookie_list
| _ -> None

let apply_middleware middlewares handler =
List.fold_right (fun middleware acc -> middleware acc) middlewares handler

let redirect_to_login reqd ?(msg = "") () =
let header_list =
[
( "Set-Cookie",
User_model.session_cookie
^ "=;Path=/;HttpOnly=true;Expires=2023-10-27T11:00:00.778Z" );
("location", "/sign-in");
("Content-Length", string_of_int (String.length msg));
]
let redirect_to_page ~path ?(clear_session = false) ?(with_error = false) reqd
?(msg = "") () =
let msg_cookie =
if with_error then "flash_msg=error: " ^ Uri.pct_encode msg ^ ";"
else "flash_msg=" ^ Uri.pct_encode msg ^ ";"
in
let headers = Httpaf.Headers.of_list header_list in
let response = Httpaf.Response.create ~headers `Found in
Httpaf.Reqd.respond_with_string reqd response msg;
Lwt.return_unit

let redirect_to_register reqd ?(msg = "") () =
let header_list =
[
( "Set-Cookie",
User_model.session_cookie
^ "=;Path=/;HttpOnly=true;Expires=2023-10-27T11:00:00.778Z" );
("location", "/sign-up");
("Content-Length", string_of_int (String.length msg));
]
let session_header =
if clear_session then
[
( "Set-Cookie",
User_model.session_cookie
^ "=; Path=/; HttpOnly=true; Expires=2023-10-27T11:00:00.778Z" );
]
else []
in
session_header
@ [
("Set-Cookie", msg_cookie);
("location", path);
("Content-Length", string_of_int (String.length msg));
]
in
let headers = Httpaf.Headers.of_list header_list in
let response = Httpaf.Response.create ~headers `Found in
Httpaf.Reqd.respond_with_string reqd response msg;
Lwt.return_unit

let redirect_to_error ~title ~data status user code api_meth reqd () =
let redirect_to_error ~title ~data status code api_meth reqd () =
let error = { Utils.Status.code; title; success = false; data } in
let data =
if api_meth then Utils.Status.to_json error
else
Dashboard.dashboard_layout user ~page_title:(title ^ " | Mollymawk")
Guest_layout.guest_layout ~page_title:(title ^ " | Mollymawk")
~content:(Error_page.error_layout error)
~icon:"/images/robur.png" ()
in
Expand Down Expand Up @@ -179,15 +177,21 @@ let auth_middleware now users handler reqd =
match user_of_cookie users now reqd with
| Ok user ->
if user.User_model.active then handler reqd
else redirect_to_login ~msg:"User account is deactivated." reqd ()
| Error (`Msg msg) -> redirect_to_login ~msg reqd ()
else
redirect_to_page ~path:"/sign-in" ~clear_session:true ~with_error:true
~msg:"User account is deactivated." reqd ()
| Error (`Msg msg) ->
redirect_to_page ~path:"/sign-in" ~clear_session:true ~with_error:true
~msg reqd ()

let email_verified_middleware now users handler reqd =
match user_of_cookie users now reqd with
| Ok user ->
if User_model.is_email_verified user then handler reqd
else redirect_to_verify_email reqd ()
| Error (`Msg msg) -> redirect_to_login ~msg reqd ()
| Error (`Msg msg) ->
redirect_to_page ~path:"/sign-in" ~clear_session:true ~with_error:true
~msg reqd ()

let is_user_admin_middleware api_meth now users handler reqd =
match user_of_cookie users now reqd with
Expand All @@ -197,8 +201,10 @@ let is_user_admin_middleware api_meth now users handler reqd =
redirect_to_error ~title:"Unauthorized"
~data:
"You don't have the necessary permissions to access this service."
`Unauthorized user 401 api_meth reqd ()
| Error (`Msg msg) -> redirect_to_login ~msg reqd ()
`Unauthorized 401 api_meth reqd ()
| Error (`Msg err) ->
redirect_to_page ~path:"/sign-in" ~clear_session:true ~with_error:true
~msg:err reqd ()

let csrf_match ~input_csrf ~check_csrf = String.equal input_csrf check_csrf

Expand Down Expand Up @@ -237,4 +243,6 @@ let csrf_verification users now form_csrf handler reqd =
http_response
~data:"Missing CSRF token. Please referesh and try again."
~title:"Missing CSRF Token" reqd `Bad_request)
| Error (`Msg err) -> redirect_to_login ~msg:err reqd ()
| Error (`Msg err) ->
redirect_to_page ~path:"/sign-in" ~clear_session:true ~with_error:true
~msg:err reqd ()
3 changes: 1 addition & 2 deletions settings_page.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let settings_layout ~csrf (configuration : Configuration.t) =
let settings_layout (configuration : Configuration.t) =
let ip = Ipaddr.to_string configuration.server_ip in
let port = string_of_int configuration.server_port in
let certificate = X509.Certificate.encode_pem configuration.certificate in
Expand All @@ -11,7 +11,6 @@ let settings_layout ~csrf (configuration : Configuration.t) =
div
~a:[ a_class [ "px-3 flex justify-between items-center" ] ]
[
Utils.csrf_form_input csrf;
div
[
p
Expand Down
11 changes: 11 additions & 0 deletions sign_in.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ let login_page ~icon () =
];
]
[
div
~a:
[
a_id "alert-container";
a_class
[
"absolute top-1/4 rounded-md right-4 z-50 w-fit \
space-y-2 p-4 shadow text-wrap hidden";
];
]
[];
div
~a:[ a_class [ "w-full max-w-lg mt-16 pb-16 mx-auto" ] ]
[
Expand Down
4 changes: 2 additions & 2 deletions sign_up.ml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ let register_page ~csrf ~icon =
a_class
[
"absolute top-1/4 rounded-md right-4 z-50 \
w-fit space-y-2 p-4 shadow border \
text-wrap hidden";
w-fit space-y-2 p-4 shadow text-wrap \
hidden";
];
]
[];
Expand Down
Loading
Loading