Compare commits
No commits in common. "fbbe6719b6823435889fe6638c9449fc3ba36f11" and "81b249d34196b3645d2e99100bc8fac553dcdcc0" have entirely different histories.
fbbe6719b6
...
81b249d341
13 changed files with 124 additions and 30 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -346,6 +346,12 @@ 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"
|
||||||
|
@ -714,6 +720,21 @@ 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"
|
||||||
|
@ -723,6 +744,18 @@ 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"
|
||||||
|
@ -1232,6 +1265,21 @@ 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"
|
||||||
|
@ -1346,8 +1394,10 @@ 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",
|
||||||
|
@ -1515,6 +1565,44 @@ 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,14 +14,8 @@ 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
|
||||||
|
@ -34,7 +28,6 @@ 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"] }
|
||||||
|
@ -45,9 +38,7 @@ 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"] }
|
||||||
|
@ -56,7 +47,18 @@ 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,4 +32,3 @@
|
||||||
- [ ] Add admin panel via API
|
- [ ] Add admin panel via API
|
||||||
- [ ] Add admin CLI
|
- [ ] Add admin CLI
|
||||||
|
|
||||||
- [ ] add TOTP
|
|
||||||
|
|
|
@ -208,9 +208,12 @@ 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;
|
||||||
if let Err(err) = res {
|
match res {
|
||||||
error!("Failed to save authorization in DB. {}", err);
|
Err(err) => {
|
||||||
return (StatusCode::INTERNAL_SERVER_ERROR, Html("Internal server error: Failed to process authorization form.")).into_response();
|
error!("Failed to save authorization in DB. {}", err);
|
||||||
|
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("/me".to_string())
|
&query_params.redirect_to.unwrap_or(format!("/me"))
|
||||||
).unwrap());
|
).unwrap());
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{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: &str = "/var/lib/autotasker/autotasker.db";
|
pub const DEFAULT_DB_PATH: &'static str = &"/var/lib/autotasker/autotasker.db";
|
||||||
pub const DEFAULT_ASSETS_PATH: &str = "/usr/local/lib/autotasker/assets";
|
pub const DEFAULT_ASSETS_PATH: &'static str = &"/usr/local/lib/autotasker/assets";
|
||||||
pub const DEFAULT_CONFIG_PATH: &str = "/etc/autotasker/config.yaml";
|
pub const DEFAULT_CONFIG_PATH: &'static 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,5 +1,6 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use axum::{extract::{OriginalUri, Request, State}, http::StatusCode, middleware::Next, response::{Html, IntoResponse, Redirect, Response}, Extension};
|
use axum::{extract::{OriginalUri, Query, Request, State}, http::{HeaderMap, HeaderValue, StatusCode}, middleware::Next, response::{Html, IntoResponse, Redirect, Response}, Extension};
|
||||||
use axum_extra::extract::CookieJar;
|
use axum_extra::extract::CookieJar;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -23,7 +24,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;
|
||||||
res
|
return 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()
|
||||||
.any(|uri| *uri == input_redirect_uri)
|
.find(|uri| **uri == input_redirect_uri).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_scope(scope_str: &str) -> Result<Vec<AuthorizationScope>> {
|
pub fn parse_scope(scope_str: &str) -> Result<Vec<AuthorizationScope>> {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use fully_pub::fully_pub;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, 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;
|
||||||
|
@ -9,16 +10,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");
|
||||||
|
|
||||||
token
|
return 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.first().ok_or(anyhow!("Expected username in encoded Authorization header value."))?.to_string(),
|
components.get(0).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