Add version header and warnings (#1175)

This commit is contained in:
Firstyear 2022-11-08 10:45:38 +10:00 committed by GitHub
parent 719de54342
commit 92d79489fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 5 deletions

View file

@ -30,7 +30,7 @@ pub use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::error::Error as SerdeJsonError;
use tokio::sync::RwLock;
use tokio::sync::{Mutex, RwLock};
use url::Url;
use uuid::Uuid;
use webauthn_rs_proto::{
@ -46,6 +46,9 @@ pub const APPLICATION_JSON: &str = "application/json";
pub const KOPID: &str = "X-KANIDM-OPID";
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)]
pub enum ClientError {
Unauthorized,
@ -106,6 +109,7 @@ pub struct KanidmClient {
pub(crate) builder: KanidmClientBuilder,
pub(crate) bearer_token: RwLock<Option<String>>,
pub(crate) auth_session_id: RwLock<Option<String>>,
pub(crate) check_version: Mutex<bool>,
}
#[cfg(target_family = "unix")]
@ -404,6 +408,7 @@ impl KanidmClientBuilder {
bearer_token: RwLock::new(None),
origin,
auth_session_id: RwLock::new(None),
check_version: Mutex::new(true),
})
}
}
@ -440,6 +445,31 @@ impl KanidmClient {
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>(
&self,
dest: &str,
@ -457,6 +487,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -517,6 +549,8 @@ impl KanidmClient {
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.
let headers = response.headers();
@ -577,6 +611,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -630,6 +666,8 @@ impl KanidmClient {
.await
.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -681,6 +719,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -721,6 +761,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -766,6 +808,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -816,6 +860,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)
@ -1183,6 +1229,8 @@ impl KanidmClient {
let response = response.send().await.map_err(ClientError::Transport)?;
self.expect_version(&response).await;
let opid = response
.headers()
.get(KOPID)

View file

@ -11,8 +11,8 @@
#![deny(clippy::trivially_copy_pass_by_ref)]
use std::ffi::CString;
use std::os::unix::fs::symlink;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::symlink;
use std::path::Path;
use std::time::Duration;
use std::{fs, io};
@ -81,7 +81,11 @@ fn chown(path: &Path, gid: u32) -> Result<(), String> {
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.
let name = info
.name

View file

@ -211,3 +211,21 @@ impl<State: Clone + Send + Sync + 'static> tide::Middleware<State>
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)
}
}

View file

@ -374,18 +374,20 @@ pub fn create_https_server(
js_files: js_files.to_owned(),
});
// Add middleware?
// Add the logging subsystem.
tserver.with(sketching::middleware::TreeMiddleware::new(
trust_x_forward_for,
));
// We do not force a session ttl, because we validate this elsewhere in usage.
// Add cookie handling.
tserver.with(
// We do not force a session ttl, because we validate this elsewhere in usage.
tide::sessions::SessionMiddleware::new(tide::sessions::CookieStore::new(), cookie_key)
.with_cookie_name("kanidm-session")
.with_same_site_policy(tide::http::cookies::SameSite::Strict),
);
// Strict responses.
tserver.with(StrictResponseMiddleware::default());
// Add routes
@ -467,6 +469,8 @@ pub fn create_https_server(
// ==== Some routes can be cached - these are here:
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());
// 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
let mut appserver = tserver.at("");
// Add our version injector, we only add this to apis.
appserver.with(VersionHeaderMiddleware::default());
appserver.with(NoCacheMiddleware::default());
// let mut well_known = appserver.at("/.well-known");