Compare commits
2 commits
81b249d341
...
fbbe6719b6
Author | SHA1 | Date | |
---|---|---|---|
fbbe6719b6 | |||
7a06c33b99 |
13 changed files with 30 additions and 124 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -346,12 +346,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.7"
|
version = "0.21.7"
|
||||||
|
@ -720,21 +714,6 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
|
||||||
dependencies = [
|
|
||||||
"foreign-types-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-shared"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -744,18 +723,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "frank_jwt"
|
|
||||||
version = "3.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9febc9f09c7569636ba0e3d98a12addd6b11b3b3bc1d7baad06d52c60c1bbadd"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.1",
|
|
||||||
"openssl",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fully_pub"
|
name = "fully_pub"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -1265,21 +1232,6 @@ dependencies = [
|
||||||
"simple_asn1",
|
"simple_asn1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jwt"
|
|
||||||
version = "0.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.1",
|
|
||||||
"crypto-common",
|
|
||||||
"digest",
|
|
||||||
"hmac",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -1394,10 +1346,8 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"frank_jwt",
|
|
||||||
"fully_pub",
|
"fully_pub",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"jwt",
|
|
||||||
"log",
|
"log",
|
||||||
"minijinja",
|
"minijinja",
|
||||||
"minijinja-embed",
|
"minijinja-embed",
|
||||||
|
@ -1565,44 +1515,6 @@ version = "1.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl"
|
|
||||||
version = "0.10.68"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cfg-if",
|
|
||||||
"foreign-types",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"openssl-macros",
|
|
||||||
"openssl-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-macros"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.79",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-sys"
|
|
||||||
version = "0.9.104"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
|
20
Cargo.toml
20
Cargo.toml
|
@ -14,8 +14,14 @@ edition = "2021"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
fully_pub = "0.1"
|
fully_pub = "0.1"
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
|
strum = "0.26.3"
|
||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
uuid = { version = "1.8", features = ["serde", "v4"] }
|
uuid = { version = "1.8", features = ["serde", "v4"] }
|
||||||
|
dotenvy = "0.15.7"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
rand = "0.8.5"
|
||||||
|
rand_core = { version = "0.6.4", features = ["std"] }
|
||||||
|
url = "2.5.3"
|
||||||
argh = "0.1" # for CLI
|
argh = "0.1" # for CLI
|
||||||
|
|
||||||
# Async
|
# Async
|
||||||
|
@ -28,6 +34,7 @@ env_logger = "0.11"
|
||||||
# Serialization
|
# Serialization
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
@ -38,7 +45,9 @@ redis = { version = "0.27.3", default-features = false, features = ["acl"] }
|
||||||
|
|
||||||
# Web
|
# Web
|
||||||
axum = { version = "0.7.7", features = ["json", "multipart"] }
|
axum = { version = "0.7.7", features = ["json", "multipart"] }
|
||||||
|
axum-extra = { version = "0.9.4", features = ["cookie"] }
|
||||||
axum-template = { version = "2.4.0", features = ["minijinja"] }
|
axum-template = { version = "2.4.0", features = ["minijinja"] }
|
||||||
|
axum_typed_multipart = "0.13.1"
|
||||||
minijinja = { version = "2.1", features = ["builtins"] }
|
minijinja = { version = "2.1", features = ["builtins"] }
|
||||||
# to make work the static assets server
|
# to make work the static assets server
|
||||||
tower-http = { version = "0.6.1", features = ["fs"] }
|
tower-http = { version = "0.6.1", features = ["fs"] }
|
||||||
|
@ -47,18 +56,7 @@ tower-http = { version = "0.6.1", features = ["fs"] }
|
||||||
totp-rs = "5.6"
|
totp-rs = "5.6"
|
||||||
minijinja-embed = "2.3.1"
|
minijinja-embed = "2.3.1"
|
||||||
axum-macros = "0.4.2"
|
axum-macros = "0.4.2"
|
||||||
jwt = "0.16.0"
|
|
||||||
dotenvy = "0.15.7"
|
|
||||||
frank_jwt = "3.1.3"
|
|
||||||
jsonwebtoken = "9.3.0"
|
jsonwebtoken = "9.3.0"
|
||||||
axum-extra = { version = "0.9.4", features = ["cookie"] }
|
|
||||||
axum_typed_multipart = "0.13.1"
|
|
||||||
base64 = "0.22.1"
|
|
||||||
rand = "0.8.5"
|
|
||||||
rand_core = { version = "0.6.4", features = ["std"] }
|
|
||||||
url = "2.5.3"
|
|
||||||
strum = "0.26.3"
|
|
||||||
serde_urlencoded = "0.7.1"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
minijinja-embed = "2.3.1"
|
minijinja-embed = "2.3.1"
|
||||||
|
|
1
TODO.md
1
TODO.md
|
@ -32,3 +32,4 @@
|
||||||
- [ ] Add admin panel via API
|
- [ ] Add admin panel via API
|
||||||
- [ ] Add admin CLI
|
- [ ] Add admin CLI
|
||||||
|
|
||||||
|
- [ ] add TOTP
|
||||||
|
|
|
@ -208,12 +208,9 @@ pub async fn perform_authorize(
|
||||||
.bind(authorization.created_at.to_rfc3339_opts(SecondsFormat::Millis, true))
|
.bind(authorization.created_at.to_rfc3339_opts(SecondsFormat::Millis, true))
|
||||||
.execute(&app_state.db)
|
.execute(&app_state.db)
|
||||||
.await;
|
.await;
|
||||||
match res {
|
if let Err(err) = res {
|
||||||
Err(err) => {
|
|
||||||
error!("Failed to save authorization in DB. {}", err);
|
error!("Failed to save authorization in DB. {}", err);
|
||||||
return (StatusCode::INTERNAL_SERVER_ERROR, Html("Internal server error: Failed to process authorization form.")).into_response();
|
return (StatusCode::INTERNAL_SERVER_ERROR, Html("Internal server error: Failed to process authorization form.")).into_response();
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
info!("Created authorization {}", &authorization.id);
|
info!("Created authorization {}", &authorization.id);
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ pub async fn perform_login(
|
||||||
headers.insert("Set-Cookie", HeaderValue::from_str(&jwt_cookie).unwrap());
|
headers.insert("Set-Cookie", HeaderValue::from_str(&jwt_cookie).unwrap());
|
||||||
// TODO: check redirection for arbitrary URL, enforce relative path
|
// TODO: check redirection for arbitrary URL, enforce relative path
|
||||||
headers.insert("Location", HeaderValue::from_str(
|
headers.insert("Location", HeaderValue::from_str(
|
||||||
&query_params.redirect_to.unwrap_or(format!("/me"))
|
&query_params.redirect_to.unwrap_or("/me".to_string())
|
||||||
).unwrap());
|
).unwrap());
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use sqlx::{sqlite::{SqliteConnectOptions, SqlitePoolOptions}, Pool, Sqlite, ConnectOptions};
|
use sqlx::{sqlite::{SqliteConnectOptions, SqlitePoolOptions}, Pool, Sqlite, ConnectOptions};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ use log::info;
|
||||||
use sqlx::{Pool, Sqlite};
|
use sqlx::{Pool, Sqlite};
|
||||||
use models::config::{AppSecrets, Config};
|
use models::config::{AppSecrets, Config};
|
||||||
|
|
||||||
pub const DEFAULT_DB_PATH: &'static str = &"/var/lib/autotasker/autotasker.db";
|
pub const DEFAULT_DB_PATH: &str = "/var/lib/autotasker/autotasker.db";
|
||||||
pub const DEFAULT_ASSETS_PATH: &'static str = &"/usr/local/lib/autotasker/assets";
|
pub const DEFAULT_ASSETS_PATH: &str = "/usr/local/lib/autotasker/assets";
|
||||||
pub const DEFAULT_CONFIG_PATH: &'static str = &"/etc/autotasker/config.yaml";
|
pub const DEFAULT_CONFIG_PATH: &str = "/etc/autotasker/config.yaml";
|
||||||
|
|
||||||
fn get_config(path: String) -> Result<Config> {
|
fn get_config(path: String) -> Result<Config> {
|
||||||
let inp_def_yaml = fs::read_to_string(path)
|
let inp_def_yaml = fs::read_to_string(path)
|
||||||
|
@ -45,7 +45,7 @@ async fn get_app_context(start_app_config: StartAppConfig) -> Result<(Config, Ap
|
||||||
|
|
||||||
let database_path = &start_app_config.database_path.unwrap_or(DEFAULT_DB_PATH.to_string());
|
let database_path = &start_app_config.database_path.unwrap_or(DEFAULT_DB_PATH.to_string());
|
||||||
info!("Using database file at {}", database_path);
|
info!("Using database file at {}", database_path);
|
||||||
let pool = prepare_database(&database_path).await.context("Could not prepare db.")?;
|
let pool = prepare_database(database_path).await.context("Could not prepare db.")?;
|
||||||
|
|
||||||
let config_path = start_app_config.config_path.unwrap_or(DEFAULT_CONFIG_PATH.to_string());
|
let config_path = start_app_config.config_path.unwrap_or(DEFAULT_CONFIG_PATH.to_string());
|
||||||
info!("Using config file at {}", &config_path);
|
info!("Using config file at {}", &config_path);
|
||||||
|
|
|
@ -99,7 +99,7 @@ pub async fn enforce_jwt_auth_middleware(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let token_claims: AppUserTokenClaims = match verify_token(&app_state.secrets, &jwt) {
|
let token_claims: AppUserTokenClaims = match verify_token(&app_state.secrets, jwt) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Err(
|
return Err(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use axum::{extract::{OriginalUri, Query, Request, State}, http::{HeaderMap, HeaderValue, StatusCode}, middleware::Next, response::{Html, IntoResponse, Redirect, Response}, Extension};
|
use axum::{extract::{OriginalUri, Request, State}, http::StatusCode, middleware::Next, response::{Html, IntoResponse, Redirect, Response}, Extension};
|
||||||
use axum_extra::extract::CookieJar;
|
use axum_extra::extract::CookieJar;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -24,7 +23,7 @@ pub async fn auth_middleware(
|
||||||
return Ok(next.run(req).await)
|
return Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let token_claims: UserTokenClaims = match verify_token(&app_state.secrets, &jwt) {
|
let token_claims: UserTokenClaims = match verify_token(&app_state.secrets, jwt) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
return Err(
|
return Err(
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl TemplateRenderer {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
*res.status_mut() = status;
|
*res.status_mut() = status;
|
||||||
return res;
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::models::{authorization::AuthorizationScope, config::Application};
|
||||||
pub fn verify_redirect_uri(app: &Application, input_redirect_uri: &str) -> bool {
|
pub fn verify_redirect_uri(app: &Application, input_redirect_uri: &str) -> bool {
|
||||||
app.allowed_redirect_uris
|
app.allowed_redirect_uris
|
||||||
.iter()
|
.iter()
|
||||||
.find(|uri| **uri == input_redirect_uri).is_some()
|
.any(|uri| *uri == input_redirect_uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_scope(scope_str: &str) -> Result<Vec<AuthorizationScope>> {
|
pub fn parse_scope(scope_str: &str) -> Result<Vec<AuthorizationScope>> {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use fully_pub::fully_pub;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
||||||
|
|
||||||
use crate::models::config::AppSecrets;
|
use crate::models::config::AppSecrets;
|
||||||
|
@ -10,16 +9,16 @@ pub fn create_token<T: Serialize>(secrets: &AppSecrets, claims: T) -> String {
|
||||||
let token = encode(
|
let token = encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
&claims,
|
&claims,
|
||||||
&EncodingKey::from_secret(&secrets.jwt_secret.as_bytes())
|
&EncodingKey::from_secret(secrets.jwt_secret.as_bytes())
|
||||||
).expect("Create token");
|
).expect("Create token");
|
||||||
|
|
||||||
return token;
|
token
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_token<T: DeserializeOwned>(secrets: &AppSecrets, jwt: &str) -> Result<T> {
|
pub fn verify_token<T: DeserializeOwned>(secrets: &AppSecrets, jwt: &str) -> Result<T> {
|
||||||
let token_data = decode::<T>(
|
let token_data = decode::<T>(
|
||||||
&jwt,
|
jwt,
|
||||||
&DecodingKey::from_secret(&secrets.jwt_secret.as_bytes()),
|
&DecodingKey::from_secret(secrets.jwt_secret.as_bytes()),
|
||||||
&Validation::new(Algorithm::HS256)
|
&Validation::new(Algorithm::HS256)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn parse_basic_auth(header_value: &str) -> Result<(String, String)> {
|
||||||
let components: Vec<&str> = basic_auth_str.split(':').collect();
|
let components: Vec<&str> = basic_auth_str.split(':').collect();
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
components.get(0).ok_or(anyhow!("Expected username in encoded Authorization header value."))?.to_string(),
|
components.first().ok_or(anyhow!("Expected username in encoded Authorization header value."))?.to_string(),
|
||||||
components.get(1).ok_or(anyhow!("Expected password in encoded Authorization header value."))?.to_string()
|
components.get(1).ok_or(anyhow!("Expected password in encoded Authorization header value."))?.to_string()
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue