mirror of
https://github.com/agersant/polaris
synced 2024-11-10 02:04:13 +00:00
Removed deprecated authentication methods
This commit is contained in:
parent
d41e837561
commit
63e971059a
8 changed files with 42 additions and 450 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -578,13 +578,7 @@ version = "0.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"time 0.3.14",
|
||||
"version_check",
|
||||
]
|
||||
|
@ -981,15 +975,6 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
|
@ -1524,7 +1509,6 @@ dependencies = [
|
|||
"ape",
|
||||
"base64",
|
||||
"branca",
|
||||
"cookie 0.16.0",
|
||||
"crossbeam-channel",
|
||||
"daemonize",
|
||||
"diesel",
|
||||
|
|
|
@ -19,7 +19,6 @@ anyhow = "1.0.56"
|
|||
ape = "0.4.0"
|
||||
base64 = "0.13"
|
||||
branca = "0.10.1"
|
||||
cookie = { version = "0.16", features = ["signed", "key-expansion"] }
|
||||
crossbeam-channel = "0.5"
|
||||
diesel_migrations = { version = "2.0", features = ["sqlite"] }
|
||||
futures-util = { version = "0.3" }
|
||||
|
|
|
@ -93,10 +93,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -125,10 +123,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -155,10 +151,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -186,10 +180,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -215,10 +207,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -245,10 +235,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -277,10 +265,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -309,10 +295,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -351,10 +335,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -381,10 +363,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"admin_http_basic": [],
|
||||
"admin_http_bearer": [],
|
||||
"admin_query_parameter": [],
|
||||
"admin_cookie": []
|
||||
"admin_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -410,10 +390,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -440,10 +418,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -453,7 +429,7 @@
|
|||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Signs in a user. Response has Set-Cookie headers for the session, username and admin permission of the user.",
|
||||
"summary": "Signs in a user.",
|
||||
"operationId": "postAuth",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
|
@ -506,10 +482,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -548,10 +522,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -580,10 +552,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -622,10 +592,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -654,10 +622,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -686,10 +652,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -728,10 +692,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -767,10 +729,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -825,10 +785,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -857,10 +815,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -899,10 +855,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -939,10 +893,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -969,10 +921,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1001,10 +951,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1033,10 +981,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1062,10 +1008,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1134,10 +1078,8 @@
|
|||
},
|
||||
"security": [
|
||||
{
|
||||
"auth_http_basic": [],
|
||||
"auth_http_bearer": [],
|
||||
"auth_query_parameter": [],
|
||||
"auth_cookie": []
|
||||
"auth_query_parameter": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1490,28 +1432,6 @@
|
|||
"in": "query",
|
||||
"name": "auth_token",
|
||||
"description": "Identical to the auth_query_parameter scheme but only for users recognized as admin by the Polaris server"
|
||||
},
|
||||
"auth_http_basic": {
|
||||
"type": "http",
|
||||
"scheme": "basic",
|
||||
"description": "[deprecated]"
|
||||
},
|
||||
"admin_http_basic": {
|
||||
"type": "http",
|
||||
"scheme": "basic",
|
||||
"description": "[deprecated] Identical to the auth_http_basic scheme but only for users recognized as admin by the Polaris server"
|
||||
},
|
||||
"auth_cookie": {
|
||||
"type": "apikey",
|
||||
"in": "cookie",
|
||||
"name": "session",
|
||||
"description": "[deprecated] A token obtained via the SET-COOKIE header in a response to a request via the auth_http_basic scheme, or a request to the `auth` endpoint."
|
||||
},
|
||||
"admin_cookie": {
|
||||
"type": "apikey",
|
||||
"in": "cookie",
|
||||
"name": "session",
|
||||
"description": "[deprecated] Identical to the auth_cookie scheme but only for users recognized as admin by the Polaris server"
|
||||
}
|
||||
},
|
||||
"links": {},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use actix_files::NamedFile;
|
||||
use actix_web::body::{BoxBody, MessageBody};
|
||||
use actix_web::body::BoxBody;
|
||||
use actix_web::http::header::ContentEncoding;
|
||||
use actix_web::{
|
||||
delete,
|
||||
dev::{Payload, Service, ServiceRequest, ServiceResponse},
|
||||
dev::Payload,
|
||||
error::{ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
||||
get,
|
||||
http::StatusCode,
|
||||
|
@ -11,12 +11,10 @@ use actix_web::{
|
|||
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
||||
FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||
};
|
||||
use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
||||
use cookie::{self, *};
|
||||
use futures_util::future::{err, ok};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use futures_util::future::err;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::str;
|
||||
|
@ -75,6 +73,7 @@ pub fn make_config() -> impl FnOnce(&mut ServiceConfig) + Clone {
|
|||
impl ResponseError for APIError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
APIError::AuthenticationRequired => StatusCode::UNAUTHORIZED,
|
||||
APIError::IncorrectCredentials => StatusCode::UNAUTHORIZED,
|
||||
APIError::EmptyUsername => StatusCode::BAD_REQUEST,
|
||||
APIError::EmptyPassword => StatusCode::BAD_REQUEST,
|
||||
|
@ -93,76 +92,9 @@ impl ResponseError for APIError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Cookies {
|
||||
jar: CookieJar,
|
||||
key: Key,
|
||||
}
|
||||
|
||||
impl Cookies {
|
||||
fn new(key: Key) -> Self {
|
||||
let jar = CookieJar::new();
|
||||
Self { jar, key }
|
||||
}
|
||||
|
||||
fn add_original(&mut self, cookie: Cookie<'static>) {
|
||||
self.jar.add_original(cookie);
|
||||
}
|
||||
|
||||
fn add(&mut self, cookie: Cookie<'static>) {
|
||||
self.jar.add(cookie);
|
||||
}
|
||||
|
||||
fn add_signed(&mut self, cookie: Cookie<'static>) {
|
||||
self.jar.signed_mut(&self.key).add(cookie);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn get(&self, name: &str) -> Option<&Cookie> {
|
||||
self.jar.get(name)
|
||||
}
|
||||
|
||||
fn get_signed(&mut self, name: &str) -> Option<Cookie> {
|
||||
self.jar.signed(&self.key).get(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRequest for Cookies {
|
||||
type Error = actix_web::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
||||
|
||||
fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
|
||||
let request_cookies = match request.cookies() {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
||||
};
|
||||
|
||||
let key = match request.app_data::<Data<Key>>() {
|
||||
Some(k) => k.as_ref(),
|
||||
None => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
||||
};
|
||||
|
||||
let mut cookies = Cookies::new(key.clone());
|
||||
for cookie in request_cookies.deref() {
|
||||
cookies.add_original(cookie.clone());
|
||||
}
|
||||
|
||||
Box::pin(ok(cookies))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AuthSource {
|
||||
AuthorizationBasic,
|
||||
AuthorizationBearer,
|
||||
Cookie,
|
||||
QueryParameter,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Auth {
|
||||
username: String,
|
||||
source: AuthSource,
|
||||
}
|
||||
|
||||
impl FromRequest for Auth {
|
||||
|
@ -175,29 +107,11 @@ impl FromRequest for Auth {
|
|||
None => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
||||
};
|
||||
|
||||
let cookies_future = Cookies::from_request(request, payload);
|
||||
let basic_auth_future = BasicAuth::from_request(request, payload);
|
||||
let bearer_auth_future = BearerAuth::from_request(request, payload);
|
||||
let query_params_future =
|
||||
web::Query::<dto::AuthQueryParameters>::from_request(request, payload);
|
||||
|
||||
Box::pin(async move {
|
||||
// Auth via session cookie
|
||||
{
|
||||
let mut cookies = cookies_future.await?;
|
||||
if let Some(session_cookie) = cookies.get_signed(dto::COOKIE_SESSION) {
|
||||
let username = session_cookie.value().to_string();
|
||||
let exists = block(move || user_manager.exists(&username)).await?;
|
||||
if !exists {
|
||||
return Err(ErrorUnauthorized(APIError::Unspecified));
|
||||
}
|
||||
return Ok(Auth {
|
||||
username: session_cookie.value().to_string(),
|
||||
source: AuthSource::Cookie,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Auth via bearer token in query parameter
|
||||
if let Ok(query) = query_params_future.await {
|
||||
let auth_token = user::AuthToken(query.auth_token.clone());
|
||||
|
@ -207,7 +121,6 @@ impl FromRequest for Auth {
|
|||
.await?;
|
||||
return Ok(Auth {
|
||||
username: authorization.username,
|
||||
source: AuthSource::QueryParameter,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -220,29 +133,10 @@ impl FromRequest for Auth {
|
|||
.await?;
|
||||
return Ok(Auth {
|
||||
username: authorization.username,
|
||||
source: AuthSource::AuthorizationBearer,
|
||||
});
|
||||
}
|
||||
|
||||
// Auth via basic authorization header
|
||||
{
|
||||
let basic_auth = basic_auth_future.await?;
|
||||
let username = basic_auth.user_id().to_string();
|
||||
let password = basic_auth
|
||||
.password()
|
||||
.map(|s| s.as_ref())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let auth_result = block(move || user_manager.login(&username, &password)).await;
|
||||
if auth_result.is_ok() {
|
||||
Ok(Auth {
|
||||
username: basic_auth.user_id().to_string(),
|
||||
source: AuthSource::AuthorizationBasic,
|
||||
})
|
||||
} else {
|
||||
Err(ErrorUnauthorized(APIError::Unspecified))
|
||||
}
|
||||
}
|
||||
Err(ErrorUnauthorized(APIError::AuthenticationRequired))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -285,93 +179,6 @@ impl FromRequest for AdminRights {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn http_auth_middleware<
|
||||
B: MessageBody + 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
|
||||
>(
|
||||
request: ServiceRequest,
|
||||
service: &S,
|
||||
) -> Pin<Box<dyn Future<Output = Result<ServiceResponse<B>, actix_web::Error>>>> {
|
||||
let user_manager = match request.app_data::<Data<user::Manager>>() {
|
||||
Some(m) => m.clone(),
|
||||
None => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
||||
};
|
||||
|
||||
let (request, mut payload) = request.into_parts();
|
||||
let auth_future = Auth::from_request(&request, &mut payload);
|
||||
let cookies_future = Cookies::from_request(&request, &mut payload);
|
||||
let request = ServiceRequest::from_parts(request, payload);
|
||||
|
||||
let response_future = service.call(request);
|
||||
Box::pin(async move {
|
||||
let mut response = response_future.await?;
|
||||
if let Ok(auth) = auth_future.await {
|
||||
let set_cookies = match auth.source {
|
||||
AuthSource::AuthorizationBasic => true,
|
||||
AuthSource::AuthorizationBearer => false,
|
||||
AuthSource::Cookie => false,
|
||||
AuthSource::QueryParameter => false,
|
||||
};
|
||||
if set_cookies {
|
||||
let cookies = cookies_future.await?;
|
||||
let username = auth.username.clone();
|
||||
let is_admin = block(move || {
|
||||
user_manager
|
||||
.is_admin(&auth.username)
|
||||
.map_err(|_| APIError::Unspecified)
|
||||
})
|
||||
.await?;
|
||||
add_auth_cookies(response.response_mut(), &cookies, &username, is_admin)?;
|
||||
}
|
||||
}
|
||||
Ok(response)
|
||||
})
|
||||
}
|
||||
|
||||
fn add_auth_cookies<T>(
|
||||
response: &mut HttpResponse<T>,
|
||||
cookies: &Cookies,
|
||||
username: &str,
|
||||
is_admin: bool,
|
||||
) -> Result<(), http::Error> {
|
||||
let mut cookies = cookies.clone();
|
||||
|
||||
cookies.add_signed(
|
||||
Cookie::build(dto::COOKIE_SESSION, username.to_owned())
|
||||
.same_site(cookie::SameSite::Lax)
|
||||
.http_only(true)
|
||||
.permanent()
|
||||
.finish(),
|
||||
);
|
||||
|
||||
cookies.add(
|
||||
Cookie::build(dto::COOKIE_USERNAME, username.to_owned())
|
||||
.same_site(cookie::SameSite::Lax)
|
||||
.http_only(false)
|
||||
.permanent()
|
||||
.path("/")
|
||||
.finish(),
|
||||
);
|
||||
|
||||
cookies.add(
|
||||
Cookie::build(dto::COOKIE_ADMIN, format!("{}", is_admin))
|
||||
.same_site(cookie::SameSite::Lax)
|
||||
.http_only(false)
|
||||
.permanent()
|
||||
.path("/")
|
||||
.finish(),
|
||||
);
|
||||
|
||||
let headers = response.headers_mut();
|
||||
for cookie in cookies.jar.delta() {
|
||||
http::HeaderValue::from_str(&cookie.to_string()).map(|c| {
|
||||
headers.append(http::header::SET_COOKIE, c);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct MediaFile {
|
||||
named_file: NamedFile,
|
||||
}
|
||||
|
@ -476,11 +283,7 @@ async fn put_mount_dirs(
|
|||
vfs_manager: Data<vfs::Manager>,
|
||||
new_mount_dirs: Json<Vec<dto::MountDir>>,
|
||||
) -> Result<HttpResponse, APIError> {
|
||||
let new_mount_dirs: Vec<MountDir> = new_mount_dirs
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|m| m.into())
|
||||
.collect();
|
||||
let new_mount_dirs: Vec<MountDir> = new_mount_dirs.iter().cloned().map(|m| m.into()).collect();
|
||||
block(move || vfs_manager.set_mount_dirs(&new_mount_dirs)).await?;
|
||||
Ok(HttpResponse::new(StatusCode::OK))
|
||||
}
|
||||
|
@ -598,7 +401,6 @@ async fn trigger_index(
|
|||
async fn login(
|
||||
user_manager: Data<user::Manager>,
|
||||
credentials: Json<dto::Credentials>,
|
||||
cookies: Cookies,
|
||||
) -> Result<HttpResponse, APIError> {
|
||||
let username = credentials.username.clone();
|
||||
let (user::AuthToken(token), is_admin) =
|
||||
|
@ -613,9 +415,7 @@ async fn login(
|
|||
token,
|
||||
is_admin,
|
||||
};
|
||||
let mut response = HttpResponse::Ok().json(authorization);
|
||||
add_auth_cookies(&mut response, &cookies, &username, is_admin)
|
||||
.map_err(|_| APIError::Unspecified)?;
|
||||
let response = HttpResponse::Ok().json(authorization);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ pub mod test;
|
|||
|
||||
pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
||||
move |cfg: &mut ServiceConfig| {
|
||||
let encryption_key = cookie::Key::derive_from(&app.auth_secret.key[..]);
|
||||
cfg.app_data(web::Data::new(app.index))
|
||||
.app_data(web::Data::new(app.config_manager))
|
||||
.app_data(web::Data::new(app.ddns_manager))
|
||||
|
@ -25,11 +24,9 @@ pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
|||
.app_data(web::Data::new(app.thumbnail_manager))
|
||||
.app_data(web::Data::new(app.user_manager))
|
||||
.app_data(web::Data::new(app.vfs_manager))
|
||||
.app_data(web::Data::new(encryption_key))
|
||||
.service(
|
||||
web::scope("/api")
|
||||
.configure(api::make_config())
|
||||
.wrap_fn(api::http_auth_middleware)
|
||||
.wrap(NormalizePath::trim()),
|
||||
)
|
||||
.service(
|
||||
|
@ -60,7 +57,7 @@ pub fn run(app: App) -> anyhow::Result<()> {
|
|||
error!("Error starting HTTP server: {:?}", e);
|
||||
e
|
||||
})?
|
||||
.run()
|
||||
.run(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -5,9 +5,6 @@ use std::convert::From;
|
|||
|
||||
pub const API_MAJOR_VERSION: i32 = 6;
|
||||
pub const API_MINOR_VERSION: i32 = 1;
|
||||
pub const COOKIE_SESSION: &str = "session";
|
||||
pub const COOKIE_USERNAME: &str = "username";
|
||||
pub const COOKIE_ADMIN: &str = "admin";
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub struct Version {
|
||||
|
|
|
@ -5,6 +5,8 @@ use crate::app::{config, playlist, settings, user};
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum APIError {
|
||||
#[error("Authentication is required")]
|
||||
AuthenticationRequired,
|
||||
#[error("Incorrect Credentials")]
|
||||
IncorrectCredentials,
|
||||
#[error("EmptyUsername")]
|
||||
|
|
|
@ -1,57 +1,10 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use cookie::Cookie;
|
||||
use headers::{self, HeaderMapExt};
|
||||
use http::{Response, StatusCode};
|
||||
use http::StatusCode;
|
||||
|
||||
use crate::service::dto;
|
||||
use crate::service::test::{constants::*, protocol, ServiceType, TestService};
|
||||
use crate::test_name;
|
||||
|
||||
fn validate_added_cookies<T>(response: &Response<T>) {
|
||||
let twenty_years = Duration::from_secs(20 * 365 * 24 * 60 * 60);
|
||||
|
||||
let cookies: Vec<Cookie> = response
|
||||
.headers()
|
||||
.get_all(http::header::SET_COOKIE)
|
||||
.iter()
|
||||
.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
|
||||
let session = cookies
|
||||
.iter()
|
||||
.find(|c| c.name() == dto::COOKIE_SESSION)
|
||||
.unwrap();
|
||||
assert_ne!(session.value(), TEST_USERNAME);
|
||||
assert!(session.max_age().unwrap() >= twenty_years);
|
||||
|
||||
let username = cookies
|
||||
.iter()
|
||||
.find(|c| c.name() == dto::COOKIE_USERNAME)
|
||||
.unwrap();
|
||||
assert_eq!(username.value(), TEST_USERNAME);
|
||||
assert!(session.max_age().unwrap() >= twenty_years);
|
||||
|
||||
let is_admin = cookies
|
||||
.iter()
|
||||
.find(|c| c.name() == dto::COOKIE_ADMIN)
|
||||
.unwrap();
|
||||
assert_eq!(is_admin.value(), false.to_string());
|
||||
assert!(session.max_age().unwrap() >= twenty_years);
|
||||
}
|
||||
|
||||
fn validate_no_cookies<T>(response: &Response<T>) {
|
||||
let cookies: Vec<Cookie> = response
|
||||
.headers()
|
||||
.get_all(http::header::SET_COOKIE)
|
||||
.iter()
|
||||
.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_SESSION));
|
||||
assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_USERNAME));
|
||||
assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_ADMIN));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn login_rejects_bad_username() {
|
||||
let mut service = ServiceType::new(&test_name!());
|
||||
|
@ -85,62 +38,6 @@ fn login_golden_path() {
|
|||
assert_eq!(authorization.username, TEST_USERNAME);
|
||||
assert!(!authorization.is_admin);
|
||||
assert!(!authorization.token.is_empty());
|
||||
|
||||
validate_added_cookies(&response);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requests_without_auth_header_do_not_set_cookies() {
|
||||
let mut service = ServiceType::new(&test_name!());
|
||||
service.complete_initial_setup();
|
||||
service.login();
|
||||
|
||||
let request = protocol::random();
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
validate_no_cookies(&response);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authentication_via_basic_http_header_rejects_bad_username() {
|
||||
let mut service = ServiceType::new(&test_name!());
|
||||
service.complete_initial_setup();
|
||||
|
||||
let mut request = protocol::random();
|
||||
let basic = headers::Authorization::basic("garbage", TEST_PASSWORD);
|
||||
request.headers_mut().typed_insert(basic);
|
||||
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authentication_via_basic_http_header_rejects_bad_password() {
|
||||
let mut service = ServiceType::new(&test_name!());
|
||||
service.complete_initial_setup();
|
||||
|
||||
let mut request = protocol::random();
|
||||
let basic = headers::Authorization::basic(TEST_PASSWORD, "garbage");
|
||||
request.headers_mut().typed_insert(basic);
|
||||
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authentication_via_basic_http_header_golden_path() {
|
||||
let mut service = ServiceType::new(&test_name!());
|
||||
service.complete_initial_setup();
|
||||
|
||||
let mut request = protocol::random();
|
||||
let basic = headers::Authorization::basic(TEST_USERNAME, TEST_PASSWORD);
|
||||
request.headers_mut().typed_insert(basic);
|
||||
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
validate_added_cookies(&response);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -175,8 +72,6 @@ fn authentication_via_bearer_http_header_golden_path() {
|
|||
request.headers_mut().typed_insert(bearer);
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
validate_no_cookies(&response);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -214,6 +109,4 @@ fn authentication_via_query_param_golden_path() {
|
|||
|
||||
let response = service.fetch(&request);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
validate_no_cookies(&response);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue