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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
|
||||||
"hkdf",
|
|
||||||
"hmac",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand",
|
|
||||||
"sha2",
|
|
||||||
"subtle",
|
|
||||||
"time 0.3.14",
|
"time 0.3.14",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
@ -981,15 +975,6 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hkdf"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
|
||||||
dependencies = [
|
|
||||||
"hmac",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -1524,7 +1509,6 @@ dependencies = [
|
||||||
"ape",
|
"ape",
|
||||||
"base64",
|
"base64",
|
||||||
"branca",
|
"branca",
|
||||||
"cookie 0.16.0",
|
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"daemonize",
|
"daemonize",
|
||||||
"diesel",
|
"diesel",
|
||||||
|
|
|
@ -19,7 +19,6 @@ anyhow = "1.0.56"
|
||||||
ape = "0.4.0"
|
ape = "0.4.0"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
branca = "0.10.1"
|
branca = "0.10.1"
|
||||||
cookie = { version = "0.16", features = ["signed", "key-expansion"] }
|
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
diesel_migrations = { version = "2.0", features = ["sqlite"] }
|
diesel_migrations = { version = "2.0", features = ["sqlite"] }
|
||||||
futures-util = { version = "0.3" }
|
futures-util = { version = "0.3" }
|
||||||
|
|
|
@ -93,10 +93,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -125,10 +123,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -155,10 +151,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -186,10 +180,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -215,10 +207,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -245,10 +235,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -277,10 +265,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -309,10 +295,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -351,10 +335,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -381,10 +363,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"admin_http_basic": [],
|
|
||||||
"admin_http_bearer": [],
|
"admin_http_bearer": [],
|
||||||
"admin_query_parameter": [],
|
"admin_query_parameter": []
|
||||||
"admin_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -410,10 +390,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -440,10 +418,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -453,7 +429,7 @@
|
||||||
"tags": [
|
"tags": [
|
||||||
"Users"
|
"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",
|
"operationId": "postAuth",
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"required": true,
|
"required": true,
|
||||||
|
@ -506,10 +482,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -548,10 +522,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -580,10 +552,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -622,10 +592,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -654,10 +622,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -686,10 +652,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -728,10 +692,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -767,10 +729,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -825,10 +785,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -857,10 +815,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -899,10 +855,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -939,10 +893,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -969,10 +921,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1001,10 +951,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1033,10 +981,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1062,10 +1008,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1134,10 +1078,8 @@
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"auth_http_basic": [],
|
|
||||||
"auth_http_bearer": [],
|
"auth_http_bearer": [],
|
||||||
"auth_query_parameter": [],
|
"auth_query_parameter": []
|
||||||
"auth_cookie": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1490,28 +1432,6 @@
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"name": "auth_token",
|
"name": "auth_token",
|
||||||
"description": "Identical to the auth_query_parameter scheme but only for users recognized as admin by the Polaris server"
|
"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": {},
|
"links": {},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use actix_web::body::{BoxBody, MessageBody};
|
use actix_web::body::BoxBody;
|
||||||
use actix_web::http::header::ContentEncoding;
|
use actix_web::http::header::ContentEncoding;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
delete,
|
delete,
|
||||||
dev::{Payload, Service, ServiceRequest, ServiceResponse},
|
dev::Payload,
|
||||||
error::{ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
error::{ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
||||||
get,
|
get,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
@ -11,12 +11,10 @@ use actix_web::{
|
||||||
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
||||||
FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
|
FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||||
};
|
};
|
||||||
use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
use cookie::{self, *};
|
use futures_util::future::err;
|
||||||
use futures_util::future::{err, ok};
|
|
||||||
use percent_encoding::percent_decode_str;
|
use percent_encoding::percent_decode_str;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
@ -75,6 +73,7 @@ pub fn make_config() -> impl FnOnce(&mut ServiceConfig) + Clone {
|
||||||
impl ResponseError for APIError {
|
impl ResponseError for APIError {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
|
APIError::AuthenticationRequired => StatusCode::UNAUTHORIZED,
|
||||||
APIError::IncorrectCredentials => StatusCode::UNAUTHORIZED,
|
APIError::IncorrectCredentials => StatusCode::UNAUTHORIZED,
|
||||||
APIError::EmptyUsername => StatusCode::BAD_REQUEST,
|
APIError::EmptyUsername => StatusCode::BAD_REQUEST,
|
||||||
APIError::EmptyPassword => 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)]
|
#[derive(Debug)]
|
||||||
struct Auth {
|
struct Auth {
|
||||||
username: String,
|
username: String,
|
||||||
source: AuthSource,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRequest for Auth {
|
impl FromRequest for Auth {
|
||||||
|
@ -175,29 +107,11 @@ impl FromRequest for Auth {
|
||||||
None => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
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 bearer_auth_future = BearerAuth::from_request(request, payload);
|
||||||
let query_params_future =
|
let query_params_future =
|
||||||
web::Query::<dto::AuthQueryParameters>::from_request(request, payload);
|
web::Query::<dto::AuthQueryParameters>::from_request(request, payload);
|
||||||
|
|
||||||
Box::pin(async move {
|
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
|
// Auth via bearer token in query parameter
|
||||||
if let Ok(query) = query_params_future.await {
|
if let Ok(query) = query_params_future.await {
|
||||||
let auth_token = user::AuthToken(query.auth_token.clone());
|
let auth_token = user::AuthToken(query.auth_token.clone());
|
||||||
|
@ -207,7 +121,6 @@ impl FromRequest for Auth {
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(Auth {
|
return Ok(Auth {
|
||||||
username: authorization.username,
|
username: authorization.username,
|
||||||
source: AuthSource::QueryParameter,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,29 +133,10 @@ impl FromRequest for Auth {
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(Auth {
|
return Ok(Auth {
|
||||||
username: authorization.username,
|
username: authorization.username,
|
||||||
source: AuthSource::AuthorizationBearer,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth via basic authorization header
|
Err(ErrorUnauthorized(APIError::AuthenticationRequired))
|
||||||
{
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
struct MediaFile {
|
||||||
named_file: NamedFile,
|
named_file: NamedFile,
|
||||||
}
|
}
|
||||||
|
@ -476,11 +283,7 @@ async fn put_mount_dirs(
|
||||||
vfs_manager: Data<vfs::Manager>,
|
vfs_manager: Data<vfs::Manager>,
|
||||||
new_mount_dirs: Json<Vec<dto::MountDir>>,
|
new_mount_dirs: Json<Vec<dto::MountDir>>,
|
||||||
) -> Result<HttpResponse, APIError> {
|
) -> Result<HttpResponse, APIError> {
|
||||||
let new_mount_dirs: Vec<MountDir> = new_mount_dirs
|
let new_mount_dirs: Vec<MountDir> = new_mount_dirs.iter().cloned().map(|m| m.into()).collect();
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|m| m.into())
|
|
||||||
.collect();
|
|
||||||
block(move || vfs_manager.set_mount_dirs(&new_mount_dirs)).await?;
|
block(move || vfs_manager.set_mount_dirs(&new_mount_dirs)).await?;
|
||||||
Ok(HttpResponse::new(StatusCode::OK))
|
Ok(HttpResponse::new(StatusCode::OK))
|
||||||
}
|
}
|
||||||
|
@ -598,7 +401,6 @@ async fn trigger_index(
|
||||||
async fn login(
|
async fn login(
|
||||||
user_manager: Data<user::Manager>,
|
user_manager: Data<user::Manager>,
|
||||||
credentials: Json<dto::Credentials>,
|
credentials: Json<dto::Credentials>,
|
||||||
cookies: Cookies,
|
|
||||||
) -> Result<HttpResponse, APIError> {
|
) -> Result<HttpResponse, APIError> {
|
||||||
let username = credentials.username.clone();
|
let username = credentials.username.clone();
|
||||||
let (user::AuthToken(token), is_admin) =
|
let (user::AuthToken(token), is_admin) =
|
||||||
|
@ -613,9 +415,7 @@ async fn login(
|
||||||
token,
|
token,
|
||||||
is_admin,
|
is_admin,
|
||||||
};
|
};
|
||||||
let mut response = HttpResponse::Ok().json(authorization);
|
let response = HttpResponse::Ok().json(authorization);
|
||||||
add_auth_cookies(&mut response, &cookies, &username, is_admin)
|
|
||||||
.map_err(|_| APIError::Unspecified)?;
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ pub mod test;
|
||||||
|
|
||||||
pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
||||||
move |cfg: &mut ServiceConfig| {
|
move |cfg: &mut ServiceConfig| {
|
||||||
let encryption_key = cookie::Key::derive_from(&app.auth_secret.key[..]);
|
|
||||||
cfg.app_data(web::Data::new(app.index))
|
cfg.app_data(web::Data::new(app.index))
|
||||||
.app_data(web::Data::new(app.config_manager))
|
.app_data(web::Data::new(app.config_manager))
|
||||||
.app_data(web::Data::new(app.ddns_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.thumbnail_manager))
|
||||||
.app_data(web::Data::new(app.user_manager))
|
.app_data(web::Data::new(app.user_manager))
|
||||||
.app_data(web::Data::new(app.vfs_manager))
|
.app_data(web::Data::new(app.vfs_manager))
|
||||||
.app_data(web::Data::new(encryption_key))
|
|
||||||
.service(
|
.service(
|
||||||
web::scope("/api")
|
web::scope("/api")
|
||||||
.configure(api::make_config())
|
.configure(api::make_config())
|
||||||
.wrap_fn(api::http_auth_middleware)
|
|
||||||
.wrap(NormalizePath::trim()),
|
.wrap(NormalizePath::trim()),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
|
@ -60,7 +57,7 @@ pub fn run(app: App) -> anyhow::Result<()> {
|
||||||
error!("Error starting HTTP server: {:?}", e);
|
error!("Error starting HTTP server: {:?}", e);
|
||||||
e
|
e
|
||||||
})?
|
})?
|
||||||
.run()
|
.run(),
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,6 @@ use std::convert::From;
|
||||||
|
|
||||||
pub const API_MAJOR_VERSION: i32 = 6;
|
pub const API_MAJOR_VERSION: i32 = 6;
|
||||||
pub const API_MINOR_VERSION: i32 = 1;
|
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)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
|
|
|
@ -5,6 +5,8 @@ use crate::app::{config, playlist, settings, user};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum APIError {
|
pub enum APIError {
|
||||||
|
#[error("Authentication is required")]
|
||||||
|
AuthenticationRequired,
|
||||||
#[error("Incorrect Credentials")]
|
#[error("Incorrect Credentials")]
|
||||||
IncorrectCredentials,
|
IncorrectCredentials,
|
||||||
#[error("EmptyUsername")]
|
#[error("EmptyUsername")]
|
||||||
|
|
|
@ -1,57 +1,10 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use cookie::Cookie;
|
|
||||||
use headers::{self, HeaderMapExt};
|
use headers::{self, HeaderMapExt};
|
||||||
use http::{Response, StatusCode};
|
use http::StatusCode;
|
||||||
|
|
||||||
use crate::service::dto;
|
use crate::service::dto;
|
||||||
use crate::service::test::{constants::*, protocol, ServiceType, TestService};
|
use crate::service::test::{constants::*, protocol, ServiceType, TestService};
|
||||||
use crate::test_name;
|
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]
|
#[test]
|
||||||
fn login_rejects_bad_username() {
|
fn login_rejects_bad_username() {
|
||||||
let mut service = ServiceType::new(&test_name!());
|
let mut service = ServiceType::new(&test_name!());
|
||||||
|
@ -85,62 +38,6 @@ fn login_golden_path() {
|
||||||
assert_eq!(authorization.username, TEST_USERNAME);
|
assert_eq!(authorization.username, TEST_USERNAME);
|
||||||
assert!(!authorization.is_admin);
|
assert!(!authorization.is_admin);
|
||||||
assert!(!authorization.token.is_empty());
|
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]
|
#[test]
|
||||||
|
@ -175,8 +72,6 @@ fn authentication_via_bearer_http_header_golden_path() {
|
||||||
request.headers_mut().typed_insert(bearer);
|
request.headers_mut().typed_insert(bearer);
|
||||||
let response = service.fetch(&request);
|
let response = service.fetch(&request);
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
validate_no_cookies(&response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -214,6 +109,4 @@ fn authentication_via_query_param_golden_path() {
|
||||||
|
|
||||||
let response = service.fetch(&request);
|
let response = service.fetch(&request);
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
|
||||||
validate_no_cookies(&response);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue