mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-05 21:03:20 +00:00
add logout feature
This commit is contained in:
parent
2c6dfa0797
commit
21e02257c2
6 changed files with 69 additions and 34 deletions
|
@ -6,7 +6,7 @@ use crate::models::user::UserModel;
|
|||
use crate::{LoginSessionData, SessionData};
|
||||
|
||||
pub struct AuthUser {
|
||||
pub user: LoginSessionData
|
||||
pub session: LoginSessionData
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -29,7 +29,7 @@ impl<'r> FromRequest<'r> for AuthUser {
|
|||
_ => return Outcome::Forward(Status::Unauthorized),
|
||||
};
|
||||
if let Some(login) = &sess.login {
|
||||
Outcome::Success(Self { user: login.clone() })
|
||||
Outcome::Success(Self { session: login.clone() })
|
||||
} else {
|
||||
Outcome::Forward(Status::Unauthorized)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use rocket::data::ByteUnit;
|
|||
use rocket::fs::{relative, FileServer};
|
||||
use rocket::futures::AsyncWriteExt;
|
||||
use rocket::http::private::cookie::CookieBuilder;
|
||||
use rocket::http::uri::Uri;
|
||||
use rocket::response::Redirect;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket_dyn_templates::handlebars::{handlebars_helper, Context, Handlebars, Helper, HelperResult, Output, RenderContext};
|
||||
|
@ -123,7 +124,7 @@ async fn rocket() -> _ {
|
|||
api::library::move_file, api::library::upload_file, api::library::download_file, api::library::list_files, api::library::get_file, api::library::delete_file,
|
||||
])
|
||||
.mount("/auth", routes![
|
||||
ui::auth::login, ui::auth::login_handler, ui::auth::register, ui::auth::register_handler,
|
||||
ui::auth::logout, ui::auth::login, ui::auth::login_handler, ui::auth::register, ui::auth::register_handler,
|
||||
])
|
||||
.mount("/", routes![
|
||||
ui::help::about,
|
||||
|
@ -141,7 +142,7 @@ async fn rocket() -> _ {
|
|||
#[catch(401)]
|
||||
pub fn not_authorized(req: &Request) -> Redirect {
|
||||
// uri!(ui::auth::login) doesn't work, it redirects to /login instead
|
||||
Redirect::to(format!("/auth/login?path={}", req.uri()))
|
||||
Redirect::to(format!("/auth/login?return_to={}", req.uri().path().percent_encode()))
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
|
|
|
@ -1,25 +1,37 @@
|
|||
use std::net::IpAddr;
|
||||
use log::debug;
|
||||
use rocket::{get, post, uri, FromForm, Route, State};
|
||||
use rocket::{get, post, uri, FromForm, Responder, Route, State};
|
||||
use rocket::form::{Context, Contextual, Error, Form};
|
||||
use rocket::form::error::Entity;
|
||||
use rocket::http::Status;
|
||||
use rocket::fs::relative;
|
||||
use rocket::http::{Header, Status};
|
||||
use rocket::http::uri::{Origin, Reference, Uri};
|
||||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use rocket_session_store::Session;
|
||||
use crate::models::user::{validate_user, validate_user_form, UserAuthError, UserModel};
|
||||
use crate::{LoginSessionData, SessionData, DB};
|
||||
use crate::guards::AuthUser;
|
||||
use crate::routes::ui;
|
||||
use crate::routes::ui::user::list_library_files;
|
||||
use crate::util::{gen_csrf_token, set_csrf, validate_csrf_form, JsonErrorResponse, ResponseError};
|
||||
|
||||
#[get("/login")]
|
||||
pub async fn login(route: &Route, session: Session<'_, SessionData>) -> Template {
|
||||
#[get("/logout")]
|
||||
pub async fn logout(session: Session<'_, SessionData>, user: AuthUser) -> Redirect {
|
||||
session.remove().await.unwrap();
|
||||
Redirect::to(uri!("/auth", login(_, Some(true))))
|
||||
}
|
||||
|
||||
#[get("/login?<return_to>&<logged_out>")]
|
||||
pub async fn login(route: &Route, session: Session<'_, SessionData>, return_to: Option<String>, logged_out: Option<bool>) -> Template {
|
||||
// TODO: redirect if already logged in
|
||||
let csrf_token = set_csrf(&session).await;
|
||||
Template::render("auth/login", context! {
|
||||
route: route.uri.path(),
|
||||
csrf_token: csrf_token,
|
||||
form: &Context::default(),
|
||||
return_to,
|
||||
logged_out
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -36,15 +48,22 @@ struct LoginForm<'r> {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Responder)]
|
||||
#[response(status = 302)]
|
||||
struct HackyRedirectBecauseRocketBug {
|
||||
inner: String,
|
||||
location: Header<'static>,
|
||||
}
|
||||
|
||||
#[post("/login", data = "<form>")]
|
||||
#[post("/login?<return_to>", data = "<form>")]
|
||||
pub async fn login_handler(
|
||||
pool: &State<DB>,
|
||||
route: &Route,
|
||||
ip_addr: IpAddr,
|
||||
session: Session<'_, SessionData>,
|
||||
mut form: Form<Contextual<'_, LoginForm<'_>>>,
|
||||
) -> Result<Redirect, Template> {
|
||||
return_to: Option<String>,
|
||||
) -> Result<HackyRedirectBecauseRocketBug, Template> {
|
||||
validate_csrf_form(&mut form.context, &session).await;
|
||||
let user = validate_user_form(&mut form.context, &pool).await;
|
||||
if form.context.status() == Status::Ok {
|
||||
|
@ -56,15 +75,23 @@ pub async fn login_handler(
|
|||
ip_address: ip_addr,
|
||||
}),
|
||||
}).await.unwrap();
|
||||
|
||||
return Ok(Redirect::to(uri!(ui::user::index())))
|
||||
debug!("returning user to {:?}", return_to);
|
||||
let return_to_path = return_to.unwrap_or("/".to_string());
|
||||
// Rocket redirect fails when `Redirect::to("/path/ has spaces")` has spaces, so manually do location... works better
|
||||
return Ok(HackyRedirectBecauseRocketBug {
|
||||
inner: "Login successful, redirecting...".to_string(),
|
||||
location: Header::new("Location", return_to_path),
|
||||
})
|
||||
// let return_to_uri = Uri::parse::<Origin>(&return_to_path).unwrap_or(Uri::parse::<Origin>("/").unwrap());
|
||||
// return Ok(Redirect::found(return_to_uri))
|
||||
}
|
||||
}
|
||||
|
||||
let csrf_token = set_csrf(&session).await;
|
||||
let ctx = context! {
|
||||
csrf_token,
|
||||
form: &form.context
|
||||
form: &form.context,
|
||||
return_to
|
||||
};
|
||||
Err(Template::render("auth/login", &ctx))
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ use crate::util::{JsonErrorResponse, ResponseError};
|
|||
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index(route: &Route) -> Template {
|
||||
Template::render("index", context! { user: true, route: route.uri.path(), test: "value" })
|
||||
pub async fn index(user: AuthUser, route: &Route) -> Template {
|
||||
Template::render("index", context! { session: user.session, route: route.uri.path(), test: "value" })
|
||||
}
|
||||
|
||||
#[get("/library/<library_id>")]
|
||||
pub async fn redirect_list_library_files(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str)
|
||||
pub async fn redirect_list_library_files(user: AuthUser, libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str)
|
||||
-> Result<Redirect, ResponseError>
|
||||
{
|
||||
let libs = libraries.lock().await;
|
||||
|
@ -67,7 +67,7 @@ pub async fn list_library_files(user: AuthUser, route: &Route, libraries: &State
|
|||
debug!("parent={:?}", parent);
|
||||
debug!("segments={:?}", segments);
|
||||
Ok(Template::render("libraries", context! {
|
||||
user: user.user,
|
||||
session: user.session,
|
||||
route: route.uri.path(),
|
||||
library: library.model(),
|
||||
files: files,
|
||||
|
@ -92,7 +92,7 @@ struct FileAttachment {
|
|||
}
|
||||
|
||||
#[get("/file/<library_id>/<path..>")]
|
||||
pub async fn get_library_file<'a>(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: PathBuf)
|
||||
pub async fn get_library_file<'a>(user: AuthUser, libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: PathBuf)
|
||||
-> Result<FileAttachment, ResponseError>
|
||||
{
|
||||
let libs = libraries.lock().await;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<h1 class="title is-1 has-text-centered">storage-app</h1>
|
||||
<div class="box is-radiusless">
|
||||
<h4 class="title is-4 has-text-centered">Login</h4>
|
||||
{{#unless (eq (len form.form_errors) 0) }}
|
||||
{{#unless (eq (len form.form_errors) 0) }}
|
||||
<div class="notification is-danger is-light">
|
||||
<b>Login failed with errors:</b>
|
||||
<ul>
|
||||
|
@ -14,7 +14,12 @@
|
|||
</ul>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<form method="post" action="/auth/login">
|
||||
{{#if logged_out }}
|
||||
<div class="notification is-success is-light">
|
||||
You have been logged out successfully.
|
||||
</div>
|
||||
{{/if}}
|
||||
<form method="post" action="/auth/login?return_to={{return_to}}">
|
||||
<input type="hidden" name="_csrf" value="{{ csrf_token }}">
|
||||
<div class="field">
|
||||
<label class="label">Username / Email</label>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
{{#if user}} <!-- TODO: only show w/ route is library/files -->
|
||||
{{#if session.user }} <!-- TODO: only show w/ route is library/files -->
|
||||
<div class="navbar-item" style="width:300px">
|
||||
<div class="field" style="width:100%" >
|
||||
<p class="control has-icons-left">
|
||||
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if user}}
|
||||
{{#if session.user }}
|
||||
<div class="navbar-item">
|
||||
<a class="icon has-text-black">
|
||||
<i class="far fa-bell"></i>
|
||||
|
@ -37,25 +37,27 @@
|
|||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">
|
||||
<img src="/static/img/default_user.png" alt="User Image" />
|
||||
{{ debug user }}
|
||||
{{ session.user.name }}
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<div class="navbar-dropdown mr-4 is-right is-radiusless">
|
||||
<div class="dropdown-content">
|
||||
<div class="block px-4 py-4">
|
||||
<b>Quota</b>
|
||||
<progress class="progress" min=0 value=20 max=100 />
|
||||
<div class="navbar-item">
|
||||
{{ session.user.username }}
|
||||
</div>
|
||||
|
||||
<a class="navbar-item" href="/help/about">
|
||||
About
|
||||
<div class="navbar-item">
|
||||
{{ session.user.email }}
|
||||
</div>
|
||||
<hr class="navbar-divider">
|
||||
<a class="navbar-item" href="/settings">
|
||||
<i class="fa fa-cog"></i>Settings
|
||||
</a>
|
||||
<a class="navbar-item">
|
||||
Contact
|
||||
<a class="navbar-item" href="/admin">
|
||||
<i class="fa fa-star"></i> Admin Panel
|
||||
</a>
|
||||
<hr class="navbar-divider">
|
||||
<a class="navbar-item">
|
||||
Report an issue
|
||||
<a class="navbar-item has-text-danger" href="/auth/logout">
|
||||
<i class="fa fa-square-up-right"></i>Logout
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue