Skip to content

Commit

Permalink
remove obsolete Referer and admin_url
Browse files Browse the repository at this point in the history
rfc2616 has been obsoleted by rfc7231 in 2014 (and also by the recently
released rfc9110) which allows relative urls in the Location header.

c.f. https://www.rfc-editor.org/rfc/rfc7231#section-7.1.2
and https://www.rfc-editor.org/rfc/rfc9110#section-10.2.2
  • Loading branch information
stefan0xC committed Nov 24, 2022
1 parent 24f30dc commit f4e84d2
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 55 deletions.
75 changes: 21 additions & 54 deletions src/api/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rocket::serde::json::Json;
use rocket::{
form::Form,
http::{Cookie, CookieJar, MediaType, SameSite, Status},
request::{self, FromRequest, Outcome, Request},
request::{FromRequest, Outcome, Request},
response::{content::RawHtml as Html, Redirect},
Catcher, Route,
};
Expand Down Expand Up @@ -61,7 +61,7 @@ pub fn routes() -> Vec<Route> {
organizations_overview,
delete_organization,
diagnostics,
get_diagnostics_config,
get_diagnostics_config
]
}

Expand Down Expand Up @@ -93,15 +93,21 @@ fn admin_path() -> String {
format!("{}{}", CONFIG.domain_path(), ADMIN_PATH)
}

struct Referer(Option<String>);

#[rocket::async_trait]
impl<'r> FromRequest<'r> for Referer {
type Error = ();
fn admin_redirect_url(redirect: &str) -> String {
format!("{}/?redirect=/{}", admin_path(), redirect)
}

async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
Outcome::Success(Referer(request.headers().get_one("Referer").map(str::to_string)))
#[catch(401)]
fn unauthorized(request: &Request<'_>) -> Result<Redirect, Error> {
if request.format() == Some(&MediaType::JSON) {
err_code!("Authorization failed.", Status::Unauthorized.code);
}
let redirect = if let Ok(path) = request.segments::<std::path::PathBuf>(0..) {
format!("{}", path.display())
} else {
String::new()
};
Ok(Redirect::to(admin_redirect_url(&redirect)))
}

#[derive(Debug)]
Expand All @@ -126,27 +132,6 @@ impl<'r> FromRequest<'r> for IpHeader {
}
}

/// Used for `Location` response headers, which must specify an absolute URI
/// (see https://tools.ietf.org/html/rfc2616#section-14.30).
fn admin_url(referer: Referer) -> String {
// If we get a referer use that to make it work when, DOMAIN is not set
if let Some(mut referer) = referer.0 {
if let Some(start_index) = referer.find(ADMIN_PATH) {
referer.truncate(start_index + ADMIN_PATH.len());
return referer;
}
}

if CONFIG.domain_set() {
// Don't use CONFIG.domain() directly, since the user may want to keep a
// trailing slash there, particularly when running under a subpath.
format!("{}{}{}", CONFIG.domain_origin(), CONFIG.domain_path(), ADMIN_PATH)
} else {
// Last case, when no referer or domain set, technically invalid but better than nothing
ADMIN_PATH.to_string()
}
}

#[derive(Responder)]
enum AdminResponse {
#[response(status = 401)]
Expand All @@ -155,19 +140,6 @@ enum AdminResponse {
TooManyRequests(ApiResult<Html<String>>),
}

#[catch(401)]
fn unauthorized(request: &Request<'_>) -> Result<Redirect, Error> {
if request.format() == Some(&MediaType::JSON) {
err_code!("Authorization failed.", Status::Unauthorized.code);
}
let redirect = if let Ok(path) = request.segments::<std::path::PathBuf>(0..) {
format!("{}", path.display())
} else {
String::new()
};
Ok(Redirect::to(format!("{}/?redirect=/{}", admin_url(Referer(None)), redirect)))
}

#[get("/?<_redirect..>")]
fn admin_login(_redirect: &str) -> ApiResult<Html<String>> {
render_admin_login(None)
Expand Down Expand Up @@ -223,8 +195,7 @@ fn post_admin_login(
.finish();

cookies.add(cookie);
//AdminResponse::Ok(render_admin_page())
Ok(Redirect::to(format!("{}{}", admin_url(Referer(None)), redirect)))
Ok(Redirect::to(format!("{}{}", admin_path(), redirect)))
}
}

Expand Down Expand Up @@ -276,14 +247,10 @@ impl AdminTemplateData {
}
}

fn render_admin_page() -> ApiResult<Html<String>> {
let text = AdminTemplateData::new().render()?;
Ok(Html(text))
}

#[get("/")]
fn admin_page(_token: AdminToken) -> ApiResult<Html<String>> {
render_admin_page()
let text = AdminTemplateData::new().render()?;
Ok(Html(text))
}

#[derive(Deserialize, Debug)]
Expand Down Expand Up @@ -337,9 +304,9 @@ async fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
}

#[get("/logout")]
fn logout(cookies: &CookieJar<'_>, referer: Referer) -> Redirect {
fn logout(cookies: &CookieJar<'_>) -> Redirect {
cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish());
Redirect::temporary(admin_url(referer))
Redirect::to(admin_path())
}

#[get("/users")]
Expand Down Expand Up @@ -628,7 +595,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn)
"uses_proxy": uses_proxy,
"db_type": *DB_TYPE,
"db_version": get_sql_server_version(&mut conn).await,
"admin_url": format!("{}/diagnostics", admin_url(Referer(None))),
"admin_url": format!("{}{}/diagnostics", CONFIG.domain_origin(), admin_path()),
"overrides": &CONFIG.get_overrides().join(", "),
"server_time_local": Local::now().format("%Y-%m-%d %H:%M:%S %Z").to_string(),
"server_time": Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(), // Run the date/time check as the last item to minimize the difference
Expand Down
2 changes: 1 addition & 1 deletion src/static/templates/admin/diagnostics.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@
let jsonResponse = await fetch('{{urlpath}}/admin/diagnostics/config', {
'headers': { 'Accept': 'application/json' }
});
});
if (!jsonResponse.ok) {
alert("Generation failed: " + jsonResponse.statusText);
throw new Error(jsonResponse);
Expand Down

0 comments on commit f4e84d2

Please sign in to comment.