mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Add version header and warnings (#1175)
This commit is contained in:
parent
719de54342
commit
92d79489fc
|
@ -30,7 +30,7 @@ pub use reqwest::StatusCode;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::error::Error as SerdeJsonError;
|
use serde_json::error::Error as SerdeJsonError;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::{Mutex, RwLock};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use webauthn_rs_proto::{
|
use webauthn_rs_proto::{
|
||||||
|
@ -46,6 +46,9 @@ pub const APPLICATION_JSON: &str = "application/json";
|
||||||
pub const KOPID: &str = "X-KANIDM-OPID";
|
pub const KOPID: &str = "X-KANIDM-OPID";
|
||||||
pub const KSESSIONID: &str = "X-KANIDM-AUTH-SESSION-ID";
|
pub const KSESSIONID: &str = "X-KANIDM-AUTH-SESSION-ID";
|
||||||
|
|
||||||
|
const KVERSION: &str = "X-KANIDM-VERSION";
|
||||||
|
const EXPECT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
|
@ -106,6 +109,7 @@ pub struct KanidmClient {
|
||||||
pub(crate) builder: KanidmClientBuilder,
|
pub(crate) builder: KanidmClientBuilder,
|
||||||
pub(crate) bearer_token: RwLock<Option<String>>,
|
pub(crate) bearer_token: RwLock<Option<String>>,
|
||||||
pub(crate) auth_session_id: RwLock<Option<String>>,
|
pub(crate) auth_session_id: RwLock<Option<String>>,
|
||||||
|
pub(crate) check_version: Mutex<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
@ -404,6 +408,7 @@ impl KanidmClientBuilder {
|
||||||
bearer_token: RwLock::new(None),
|
bearer_token: RwLock::new(None),
|
||||||
origin,
|
origin,
|
||||||
auth_session_id: RwLock::new(None),
|
auth_session_id: RwLock::new(None),
|
||||||
|
check_version: Mutex::new(true),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,6 +445,31 @@ impl KanidmClient {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn expect_version(&self, response: &reqwest::Response) {
|
||||||
|
let mut guard = self.check_version.lock().await;
|
||||||
|
|
||||||
|
if !*guard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ver = response
|
||||||
|
.headers()
|
||||||
|
.get(KVERSION)
|
||||||
|
.and_then(|hv| hv.to_str().ok())
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
|
let matching = ver == EXPECT_VERSION;
|
||||||
|
|
||||||
|
if !matching {
|
||||||
|
warn!(server_version = ?ver, client_version = ?EXPECT_VERSION, "Mismatched client and server version - features may not work, or other unforseen errors may occur.")
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(matching);
|
||||||
|
|
||||||
|
// Check is done once, mark as no longer needing to occur
|
||||||
|
*guard = false;
|
||||||
|
}
|
||||||
|
|
||||||
async fn perform_simple_post_request<R: Serialize, T: DeserializeOwned>(
|
async fn perform_simple_post_request<R: Serialize, T: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
dest: &str,
|
dest: &str,
|
||||||
|
@ -457,6 +487,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -517,6 +549,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
// If we have a sessionid header in the response, get it now.
|
// If we have a sessionid header in the response, get it now.
|
||||||
|
|
||||||
let headers = response.headers();
|
let headers = response.headers();
|
||||||
|
@ -577,6 +611,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -630,6 +666,8 @@ impl KanidmClient {
|
||||||
.await
|
.await
|
||||||
.map_err(ClientError::Transport)?;
|
.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -681,6 +719,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -721,6 +761,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -766,6 +808,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -816,6 +860,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
@ -1183,6 +1229,8 @@ impl KanidmClient {
|
||||||
|
|
||||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
let opid = response
|
let opid = response
|
||||||
.headers()
|
.headers()
|
||||||
.get(KOPID)
|
.get(KOPID)
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::unix::fs::symlink;
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::os::unix::fs::symlink;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
@ -81,7 +81,11 @@ fn chown(path: &Path, gid: u32) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_home_directory(info: &HomeDirectoryInfo, home_prefix: &str, use_etc_skel: bool) -> Result<(), String> {
|
fn create_home_directory(
|
||||||
|
info: &HomeDirectoryInfo,
|
||||||
|
home_prefix: &str,
|
||||||
|
use_etc_skel: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
// Final sanity check to prevent certain classes of attacks.
|
// Final sanity check to prevent certain classes of attacks.
|
||||||
let name = info
|
let name = info
|
||||||
.name
|
.name
|
||||||
|
|
|
@ -211,3 +211,21 @@ impl<State: Clone + Send + Sync + 'static> tide::Middleware<State>
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KANIDM_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VersionHeaderMiddleware;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<State: Clone + Send + Sync + 'static> tide::Middleware<State> for VersionHeaderMiddleware {
|
||||||
|
async fn handle(
|
||||||
|
&self,
|
||||||
|
request: tide::Request<State>,
|
||||||
|
next: tide::Next<'_, State>,
|
||||||
|
) -> tide::Result {
|
||||||
|
let mut response = next.run(request).await;
|
||||||
|
response.insert_header("X-KANIDM-VERSION", KANIDM_VERSION);
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -374,18 +374,20 @@ pub fn create_https_server(
|
||||||
js_files: js_files.to_owned(),
|
js_files: js_files.to_owned(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add middleware?
|
// Add the logging subsystem.
|
||||||
tserver.with(sketching::middleware::TreeMiddleware::new(
|
tserver.with(sketching::middleware::TreeMiddleware::new(
|
||||||
trust_x_forward_for,
|
trust_x_forward_for,
|
||||||
));
|
));
|
||||||
|
|
||||||
// We do not force a session ttl, because we validate this elsewhere in usage.
|
// Add cookie handling.
|
||||||
tserver.with(
|
tserver.with(
|
||||||
// We do not force a session ttl, because we validate this elsewhere in usage.
|
// We do not force a session ttl, because we validate this elsewhere in usage.
|
||||||
tide::sessions::SessionMiddleware::new(tide::sessions::CookieStore::new(), cookie_key)
|
tide::sessions::SessionMiddleware::new(tide::sessions::CookieStore::new(), cookie_key)
|
||||||
.with_cookie_name("kanidm-session")
|
.with_cookie_name("kanidm-session")
|
||||||
.with_same_site_policy(tide::http::cookies::SameSite::Strict),
|
.with_same_site_policy(tide::http::cookies::SameSite::Strict),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Strict responses.
|
||||||
tserver.with(StrictResponseMiddleware::default());
|
tserver.with(StrictResponseMiddleware::default());
|
||||||
|
|
||||||
// Add routes
|
// Add routes
|
||||||
|
@ -467,6 +469,8 @@ pub fn create_https_server(
|
||||||
|
|
||||||
// ==== Some routes can be cached - these are here:
|
// ==== Some routes can be cached - these are here:
|
||||||
let mut tserver_cacheable = tserver.at("");
|
let mut tserver_cacheable = tserver.at("");
|
||||||
|
// Add our version injector, we only add this to apis.
|
||||||
|
tserver_cacheable.with(VersionHeaderMiddleware::default());
|
||||||
tserver_cacheable.with(CacheableMiddleware::default());
|
tserver_cacheable.with(CacheableMiddleware::default());
|
||||||
|
|
||||||
// We allow clients to cache the unix token for accounts and groups.
|
// We allow clients to cache the unix token for accounts and groups.
|
||||||
|
@ -486,6 +490,8 @@ pub fn create_https_server(
|
||||||
|
|
||||||
// ==== These routes can not be cached
|
// ==== These routes can not be cached
|
||||||
let mut appserver = tserver.at("");
|
let mut appserver = tserver.at("");
|
||||||
|
// Add our version injector, we only add this to apis.
|
||||||
|
appserver.with(VersionHeaderMiddleware::default());
|
||||||
appserver.with(NoCacheMiddleware::default());
|
appserver.with(NoCacheMiddleware::default());
|
||||||
|
|
||||||
// let mut well_known = appserver.at("/.well-known");
|
// let mut well_known = appserver.at("/.well-known");
|
||||||
|
|
Loading…
Reference in a new issue