Replaced /api/auth response with cookies

This commit is contained in:
Antoine Gersant 2020-01-12 17:01:30 -08:00
parent 360d864148
commit eca4f68834
5 changed files with 56 additions and 34 deletions

1
Cargo.lock generated
View file

@ -1491,6 +1491,7 @@ dependencies = [
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"simplelog 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unix-daemonize 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unix-daemonize 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -37,6 +37,7 @@ serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
simplelog = "0.7" simplelog = "0.7"
thiserror = "1.0" thiserror = "1.0"
time = "0.1"
toml = "0.5" toml = "0.5"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]

View file

@ -2,7 +2,7 @@
"openapi": "3.0.0", "openapi": "3.0.0",
"info": { "info": {
"description": "", "description": "",
"version": "3.0", "version": "4.0",
"title": "Polaris", "title": "Polaris",
"termsOfService": "" "termsOfService": ""
}, },
@ -182,7 +182,7 @@
"tags": [ "tags": [
"Other" "Other"
], ],
"summary": "Returns information about user permissions and a session cookie for future authenticated requests.", "summary": "Signs in a user. Response has Set-Cookie headers for the session, username and admin permission of the user.",
"operationId": "postAuth", "operationId": "postAuth",
"requestBody": { "requestBody": {
"required": true, "required": true,
@ -196,14 +196,7 @@
}, },
"responses": { "responses": {
"200": { "200": {
"description": "Successful operation", "description": "Successful operation"
"content": {
"application/json": {
"schema": {
"$ref": "#components/schemas/AuthOutput"
}
}
}
}, },
"401": { "401": {
"description": "Invalid credentials" "description": "Invalid credentials"
@ -874,14 +867,6 @@
} }
} }
}, },
"AuthOutput": {
"type": "object",
"properties": {
"admin": {
"type": "boolean"
}
}
},
"CollectionFile": { "CollectionFile": {
"oneOf": [ "oneOf": [
{ {

View file

@ -12,6 +12,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
use time::Duration;
use crate::config::{self, Config, Preferences}; use crate::config::{self, Config, Preferences};
use crate::db::DB; use crate::db::DB;
@ -24,9 +25,11 @@ use crate::user;
use crate::utils; use crate::utils;
use crate::vfs::VFSSource; use crate::vfs::VFSSource;
const CURRENT_MAJOR_VERSION: i32 = 3; const CURRENT_MAJOR_VERSION: i32 = 4;
const CURRENT_MINOR_VERSION: i32 = 1; const CURRENT_MINOR_VERSION: i32 = 0;
const COOKIE_SESSION: &str = "session"; const COOKIE_SESSION: &str = "session";
const COOKIE_USERNAME: &str = "username";
const COOKIE_ADMIN: &str = "admin";
pub fn get_routes() -> Vec<rocket::Route> { pub fn get_routes() -> Vec<rocket::Route> {
routes![ routes![
@ -86,11 +89,32 @@ struct Auth {
username: String, username: String,
} }
fn get_session_cookie(username: &str) -> Cookie<'static> { fn add_session_cookies(cookies: &mut Cookies, username: &str, is_admin: bool) -> () {
Cookie::build(COOKIE_SESSION, username.to_owned()) let duration = Duration::days(1);
let session_cookie = Cookie::build(COOKIE_SESSION, username.to_owned())
.same_site(rocket::http::SameSite::Lax) .same_site(rocket::http::SameSite::Lax)
.http_only(true) .http_only(true)
.finish() .max_age(duration)
.finish();
let username_cookie = Cookie::build(COOKIE_USERNAME, username.to_owned())
.same_site(rocket::http::SameSite::Lax)
.http_only(false)
.max_age(duration)
.path("/")
.finish();
let is_admin_cookie = Cookie::build(COOKIE_ADMIN, format!("{}", is_admin))
.same_site(rocket::http::SameSite::Lax)
.http_only(false)
.max_age(duration)
.path("/")
.finish();
cookies.add_private(session_cookie);
cookies.add(username_cookie);
cookies.add(is_admin_cookie);
} }
impl<'a, 'r> FromRequest<'a, 'r> for Auth { impl<'a, 'r> FromRequest<'a, 'r> for Auth {
@ -116,7 +140,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for Auth {
_ => return Outcome::Failure((Status::InternalServerError, ())), _ => return Outcome::Failure((Status::InternalServerError, ())),
}; };
if user::auth(db.deref().deref(), &username, &password).unwrap_or(false) { if user::auth(db.deref().deref(), &username, &password).unwrap_or(false) {
cookies.add_private(get_session_cookie(&username)); let is_admin = match user::is_admin(db.deref().deref(), &username) {
Ok(a) => a,
Err(_) => return Outcome::Failure((Status::InternalServerError, ())),
};
add_session_cookies(&mut cookies, &username, is_admin);
return Outcome::Success(Auth { return Outcome::Success(Auth {
username: username.to_string(), username: username.to_string(),
}); });
@ -256,17 +284,13 @@ fn auth(
db: State<'_, Arc<DB>>, db: State<'_, Arc<DB>>,
credentials: Json<AuthCredentials>, credentials: Json<AuthCredentials>,
mut cookies: Cookies<'_>, mut cookies: Cookies<'_>,
) -> std::result::Result<Json<AuthOutput>, APIError> { ) -> std::result::Result<(), APIError> {
if !user::auth::<DB>(&db, &credentials.username, &credentials.password)? { if !user::auth::<DB>(&db, &credentials.username, &credentials.password)? {
return Err(APIError::IncorrectCredentials); return Err(APIError::IncorrectCredentials);
} }
let is_admin = user::is_admin::<DB>(&db, &credentials.username)?;
cookies.add_private(get_session_cookie(&credentials.username)); add_session_cookies(&mut cookies, &credentials.username, is_admin);
Ok(())
let auth_output = AuthOutput {
admin: user::is_admin::<DB>(&db, &credentials.username)?,
};
Ok(Json(auth_output))
} }
#[get("/browse")] #[get("/browse")]

View file

@ -58,7 +58,7 @@ fn version() {
let response_body = response.body_string().unwrap(); let response_body = response.body_string().unwrap();
let response_json: api::Version = serde_json::from_str(&response_body).unwrap(); let response_json: api::Version = serde_json::from_str(&response_body).unwrap();
assert_eq!(response_json, api::Version { major: 3, minor: 1 }); assert_eq!(response_json, api::Version { major: 4, minor: 0 });
} }
#[test] #[test]
@ -271,7 +271,18 @@ fn auth() {
.body(serde_json::to_string(&credentials).unwrap()) .body(serde_json::to_string(&credentials).unwrap())
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
assert_eq!(response.cookies()[0].name(), "session"); assert!(response
.cookies()
.iter()
.any(|cookie| cookie.name() == "username"));
assert!(response
.cookies()
.iter()
.any(|cookie| cookie.name() == "admin"));
assert!(response
.cookies()
.iter()
.any(|cookie| cookie.name() == "session"));
} }
} }