mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
1812 1813 post axum cleanup (#1817)
This commit is contained in:
parent
9f886b85dd
commit
d1f51f0a84
78
server/core/src/https/extractors/mod.rs
Normal file
78
server/core/src/https/extractors/mod.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use axum::{
|
||||||
|
async_trait,
|
||||||
|
extract::{ConnectInfo, FromRequestParts},
|
||||||
|
http::{header::HeaderName, request::Parts, StatusCode},
|
||||||
|
RequestPartsExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
|
use crate::https::ServerState;
|
||||||
|
|
||||||
|
#[allow(clippy::declare_interior_mutable_const)]
|
||||||
|
const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
|
||||||
|
|
||||||
|
pub struct TrustedClientIp(pub IpAddr);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl FromRequestParts<ServerState> for TrustedClientIp {
|
||||||
|
type Rejection = (StatusCode, &'static str);
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(state))]
|
||||||
|
async fn from_request_parts(
|
||||||
|
parts: &mut Parts,
|
||||||
|
state: &ServerState,
|
||||||
|
) -> Result<Self, Self::Rejection> {
|
||||||
|
if state.trust_x_forward_for {
|
||||||
|
if let Some(x_forward_for) = parts.headers.get(X_FORWARDED_FOR) {
|
||||||
|
// X forward for may be comma separate.
|
||||||
|
let first = x_forward_for
|
||||||
|
.to_str()
|
||||||
|
.map(|s|
|
||||||
|
// Split on an optional comma, return the first result.
|
||||||
|
s.split(',').next().unwrap_or(s))
|
||||||
|
.map_err(|_| {
|
||||||
|
(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"X-Forwarded-For contains invalid data",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
first.parse::<IpAddr>().map(TrustedClientIp).map_err(|_| {
|
||||||
|
(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"X-Forwarded-For contains invalid ip addr",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let ConnectInfo(addr) =
|
||||||
|
parts
|
||||||
|
.extract::<ConnectInfo<SocketAddr>>()
|
||||||
|
.await
|
||||||
|
.map_err(|_| {
|
||||||
|
error!("Connect info contains invalid IP address");
|
||||||
|
(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"connect info contains invalid IP address",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(TrustedClientIp(addr.ip()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ConnectInfo(addr) =
|
||||||
|
parts
|
||||||
|
.extract::<ConnectInfo<SocketAddr>>()
|
||||||
|
.await
|
||||||
|
.map_err(|_| {
|
||||||
|
error!("Connect info contains invalid IP address");
|
||||||
|
(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"connect info contains invalid IP address",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(TrustedClientIp(addr.ip()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use axum::{
|
||||||
http::{self, Request},
|
http::{self, Request},
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::Response,
|
response::Response,
|
||||||
Extension, TypedHeader,
|
TypedHeader,
|
||||||
};
|
};
|
||||||
use axum_sessions::SessionHandle;
|
use axum_sessions::SessionHandle;
|
||||||
use http::HeaderValue;
|
use http::HeaderValue;
|
||||||
|
@ -21,7 +21,6 @@ pub async fn version_middleware<B>(request: Request<B>, next: Next<B>) -> Respon
|
||||||
let mut response = next.run(request).await;
|
let mut response = next.run(request).await;
|
||||||
let headers = response.headers_mut();
|
let headers = response.headers_mut();
|
||||||
headers.insert("X-KANIDM-VERSION", HeaderValue::from_static(KANIDM_VERSION));
|
headers.insert("X-KANIDM-VERSION", HeaderValue::from_static(KANIDM_VERSION));
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,16 +31,9 @@ pub struct KOpId {
|
||||||
pub uat: Option<String>,
|
pub uat: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KOpId {
|
|
||||||
/// Return the event ID as a string
|
|
||||||
pub fn eventid_value(&self) -> String {
|
|
||||||
let res = self.eventid;
|
|
||||||
res.as_hyphenated().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it.
|
/// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it.
|
||||||
pub async fn kopid_start<B>(
|
#[instrument(name = "request", skip_all)]
|
||||||
|
pub async fn kopid_middleware<B>(
|
||||||
auth: Option<TypedHeader<Authorization<Bearer>>>,
|
auth: Option<TypedHeader<Authorization<Bearer>>>,
|
||||||
mut request: Request<B>,
|
mut request: Request<B>,
|
||||||
next: Next<B>,
|
next: Next<B>,
|
||||||
|
@ -66,23 +58,12 @@ pub async fn kopid_start<B>(
|
||||||
|
|
||||||
// insert the extension so we can pull it out later
|
// insert the extension so we can pull it out later
|
||||||
request.extensions_mut().insert(KOpId { eventid, uat });
|
request.extensions_mut().insert(KOpId { eventid, uat });
|
||||||
next.run(request).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This runs at the start of the request, adding an extension with the OperationID
|
|
||||||
pub async fn kopid_end<B>(
|
|
||||||
Extension(kopid): Extension<KOpId>,
|
|
||||||
request: Request<B>,
|
|
||||||
next: Next<B>,
|
|
||||||
) -> Response {
|
|
||||||
// generate the event ID
|
|
||||||
// insert the extension so we can pull it out later
|
|
||||||
let mut response = next.run(request).await;
|
let mut response = next.run(request).await;
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
response.headers_mut().insert(
|
response.headers_mut().insert(
|
||||||
"X-KANIDM-OPID",
|
"X-KANIDM-OPID",
|
||||||
HeaderValue::from_str(&kopid.eventid_value()).unwrap(),
|
HeaderValue::from_str(&eventid.as_hyphenated().to_string()).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
response
|
response
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod extractors;
|
||||||
mod generic;
|
mod generic;
|
||||||
mod javascript;
|
mod javascript;
|
||||||
mod manifest;
|
mod manifest;
|
||||||
|
@ -60,7 +61,7 @@ pub struct ServerState {
|
||||||
pub jws_validator: compact_jwt::JwsValidator,
|
pub jws_validator: compact_jwt::JwsValidator,
|
||||||
// The SHA384 hashes of javascript files we're going to serve to users
|
// The SHA384 hashes of javascript files we're going to serve to users
|
||||||
pub js_files: Vec<JavaScriptFile>,
|
pub js_files: Vec<JavaScriptFile>,
|
||||||
// pub(crate) trust_x_forward_for: bool,
|
pub(crate) trust_x_forward_for: bool,
|
||||||
pub csp_header: HeaderValue,
|
pub csp_header: HeaderValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,8 +134,7 @@ pub fn get_js_files(role: ServerRole) -> Vec<JavaScriptFile> {
|
||||||
|
|
||||||
pub async fn create_https_server(
|
pub async fn create_https_server(
|
||||||
config: Configuration,
|
config: Configuration,
|
||||||
// trust_x_forward_for: bool, // TODO: #1787 make XFF headers work
|
cookie_key: [u8; 64],
|
||||||
cookie_key: [u8; 32],
|
|
||||||
jws_signer: JwsSigner,
|
jws_signer: JwsSigner,
|
||||||
status_ref: &'static StatusActor,
|
status_ref: &'static StatusActor,
|
||||||
qe_w_ref: &'static QueryServerWriteV1,
|
qe_w_ref: &'static QueryServerWriteV1,
|
||||||
|
@ -186,15 +186,16 @@ pub async fn create_https_server(
|
||||||
);
|
);
|
||||||
|
|
||||||
let store = async_session::CookieStore::new();
|
let store = async_session::CookieStore::new();
|
||||||
let secret = format!("{:?}", cookie_key);
|
|
||||||
let secret = secret.as_bytes(); // TODO the cookie/session secret needs to be longer?
|
let session_layer = SessionLayer::new(store, &cookie_key)
|
||||||
let session_layer = SessionLayer::new(store, secret)
|
|
||||||
.with_cookie_name("kanidm-session")
|
.with_cookie_name("kanidm-session")
|
||||||
.with_session_ttl(None)
|
.with_session_ttl(None)
|
||||||
.with_cookie_domain(config.domain)
|
.with_cookie_domain(config.domain)
|
||||||
.with_same_site_policy(SameSite::Strict)
|
.with_same_site_policy(SameSite::Strict)
|
||||||
.with_secure(true);
|
.with_secure(true);
|
||||||
|
|
||||||
|
let trust_x_forward_for = config.trust_x_forward_for;
|
||||||
|
|
||||||
let state = ServerState {
|
let state = ServerState {
|
||||||
status_ref,
|
status_ref,
|
||||||
qe_w_ref,
|
qe_w_ref,
|
||||||
|
@ -202,6 +203,7 @@ pub async fn create_https_server(
|
||||||
jws_signer,
|
jws_signer,
|
||||||
jws_validator,
|
jws_validator,
|
||||||
js_files,
|
js_files,
|
||||||
|
trust_x_forward_for,
|
||||||
csp_header: csp_header.finish(),
|
csp_header: csp_header.finish(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,15 +221,13 @@ pub async fn create_https_server(
|
||||||
ServerRole::WriteReplicaNoUI => Router::new(),
|
ServerRole::WriteReplicaNoUI => Router::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// // == oauth endpoints.
|
|
||||||
// TODO: turn this from a nest into a merge because state things are bad in nested routes
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.nest("/oauth2", oauth2::oauth2_route_setup(state.clone()))
|
|
||||||
.nest("/scim", v1_scim::scim_route_setup())
|
|
||||||
.route("/robots.txt", get(robots_txt))
|
.route("/robots.txt", get(robots_txt))
|
||||||
.nest("/v1", v1::router(state.clone()))
|
.route("/status", get(status))
|
||||||
// Shared account features only - mainly this is for unix-like features.
|
.merge(oauth2::oauth2_route_setup(state.clone()))
|
||||||
.route("/status", get(status));
|
.merge(v1_scim::scim_route_setup())
|
||||||
|
.merge(v1::router(state.clone()));
|
||||||
|
|
||||||
let app = match config.role {
|
let app = match config.role {
|
||||||
ServerRole::WriteReplicaNoUI => app,
|
ServerRole::WriteReplicaNoUI => app,
|
||||||
ServerRole::WriteReplica | ServerRole::ReadOnlyReplica => {
|
ServerRole::WriteReplica | ServerRole::ReadOnlyReplica => {
|
||||||
|
@ -250,11 +250,14 @@ pub async fn create_https_server(
|
||||||
middleware::csp_headers::cspheaders_layer,
|
middleware::csp_headers::cspheaders_layer,
|
||||||
))
|
))
|
||||||
.layer(from_fn(middleware::version_middleware))
|
.layer(from_fn(middleware::version_middleware))
|
||||||
.layer(from_fn(middleware::kopid_end))
|
|
||||||
.layer(from_fn(middleware::kopid_start))
|
|
||||||
.layer(session_layer)
|
.layer(session_layer)
|
||||||
.with_state(state)
|
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
|
// This must be the LAST middleware.
|
||||||
|
// This is because the last middleware here is the first to be entered and the last
|
||||||
|
// to be exited, and this middleware sets up ids' and other bits for for logging
|
||||||
|
// coherence to be maintained.
|
||||||
|
.layer(from_fn(middleware::kopid_middleware))
|
||||||
|
.with_state(state)
|
||||||
// the connect_info bit here lets us pick up the remote address of the client
|
// the connect_info bit here lets us pick up the remote address of the client
|
||||||
.into_make_service_with_connect_info::<SocketAddr>();
|
.into_make_service_with_connect_info::<SocketAddr>();
|
||||||
|
|
||||||
|
@ -301,7 +304,7 @@ async fn server_loop(
|
||||||
let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
|
let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
|
||||||
let mut app = app;
|
let mut app = app;
|
||||||
tls_builder
|
tls_builder
|
||||||
.set_certificate_file(tls_param.chain.clone(), SslFiletype::PEM)
|
.set_certificate_chain_file(tls_param.chain.clone())
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
std::io::Error::new(
|
std::io::Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
|
|
|
@ -59,7 +59,6 @@ pub async fn oauth2_id_get(
|
||||||
Path(rs_name): Path<String>,
|
Path(rs_name): Path<String>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
|
||||||
let filter = oauth2_id(&rs_name);
|
let filter = oauth2_id(&rs_name);
|
||||||
|
|
||||||
let res = state
|
let res = state
|
||||||
|
@ -227,8 +226,6 @@ pub async fn oauth2_id_delete(
|
||||||
// valid Kanidm instance in the topology can handle these request.
|
// valid Kanidm instance in the topology can handle these request.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn oauth2_authorise_post(
|
pub async fn oauth2_authorise_post(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
|
@ -349,8 +346,8 @@ async fn oauth2_authorise(
|
||||||
// we do NOT redirect in an error condition, and just render the error ourselves.
|
// we do NOT redirect in an error condition, and just render the error ourselves.
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
admin_error!(
|
admin_error!(
|
||||||
"Unable to authorise - Error ID: {} error: {}",
|
"Unable to authorise - Error ID: {:?} error: {}",
|
||||||
&kopid.eventid_value(),
|
kopid.eventid,
|
||||||
&e.to_string()
|
&e.to_string()
|
||||||
);
|
);
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
|
@ -514,7 +511,7 @@ async fn oauth2_authorise_reject(
|
||||||
pub async fn oauth2_token_post(
|
pub async fn oauth2_token_post(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
headers: HeaderMap, // TOOD: make this a typed basic auth header
|
headers: HeaderMap, // TODO: make this a typed basic auth header
|
||||||
Form(tok_req): Form<AccessTokenRequest>,
|
Form(tok_req): Form<AccessTokenRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// This is called directly by the resource server, where we then issue
|
// This is called directly by the resource server, where we then issue
|
||||||
|
@ -834,52 +831,58 @@ pub async fn oauth2_token_revoke_post(
|
||||||
|
|
||||||
pub fn oauth2_route_setup(state: ServerState) -> Router<ServerState> {
|
pub fn oauth2_route_setup(state: ServerState) -> Router<ServerState> {
|
||||||
// this has all the openid-related routes
|
// this has all the openid-related routes
|
||||||
let openid_router = Router::new() // appserver.at("/oauth2/openid");
|
let openid_router = Router::new()
|
||||||
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route(
|
.route(
|
||||||
"/:client_id/.well-known/openid-configuration",
|
"/oauth2/openid/:client_id/.well-known/openid-configuration",
|
||||||
get(oauth2_openid_discovery_get),
|
get(oauth2_openid_discovery_get),
|
||||||
)
|
)
|
||||||
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route("/:client_id/userinfo", get(oauth2_openid_userinfo_get))
|
.route(
|
||||||
|
"/oauth2/openid/:client_id/userinfo",
|
||||||
|
get(oauth2_openid_userinfo_get),
|
||||||
|
)
|
||||||
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// // ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// // IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route(
|
.route(
|
||||||
"/:client_id/public_key.jwk",
|
"/oauth2/openid/:client_id/public_key.jwk",
|
||||||
get(oauth2_openid_publickey_get),
|
get(oauth2_openid_publickey_get),
|
||||||
)
|
)
|
||||||
.with_state(state.clone());
|
.with_state(state.clone());
|
||||||
|
|
||||||
Router::new() //= appserver.at("/oauth2");
|
Router::new()
|
||||||
.route("/", get(oauth2_get))
|
.route("/oauth2", get(oauth2_get))
|
||||||
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route(
|
.route(
|
||||||
"/authorise",
|
"/oauth2/authorise",
|
||||||
post(oauth2_authorise_post).get(oauth2_authorise_get),
|
post(oauth2_authorise_post).get(oauth2_authorise_get),
|
||||||
)
|
)
|
||||||
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route(
|
.route(
|
||||||
"/authorise/permit",
|
"/oauth2/authorise/permit",
|
||||||
post(oauth2_authorise_permit_post).get(oauth2_authorise_permit_get),
|
post(oauth2_authorise_permit_post).get(oauth2_authorise_permit_get),
|
||||||
)
|
)
|
||||||
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route(
|
.route(
|
||||||
"/authorise/reject",
|
"/oauth2/authorise/reject",
|
||||||
post(oauth2_authorise_reject_post).get(oauth2_authorise_reject_get),
|
post(oauth2_authorise_reject_post).get(oauth2_authorise_reject_get),
|
||||||
)
|
)
|
||||||
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route("/token", post(oauth2_token_post))
|
.route("/oauth2/token", post(oauth2_token_post))
|
||||||
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
// ⚠️ ⚠️ WARNING ⚠️ ⚠️
|
||||||
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
// IF YOU CHANGE THESE VALUES YOU MUST UPDATE OIDC DISCOVERY URLS
|
||||||
.route("/token/introspect", post(oauth2_token_introspect_post))
|
.route(
|
||||||
.route("/token/revoke", post(oauth2_token_revoke_post))
|
"/oauth2/token/introspect",
|
||||||
.nest("/openid", openid_router)
|
post(oauth2_token_introspect_post),
|
||||||
|
)
|
||||||
|
.route("/oauth2/token/revoke", post(oauth2_token_revoke_post))
|
||||||
|
.merge(openid_router)
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.layer(from_fn(super::middleware::caching::dont_cache_me))
|
.layer(from_fn(super::middleware::caching::dont_cache_me))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::net::SocketAddr;
|
//! The V1 API things!
|
||||||
#[allow(unused_imports)]
|
|
||||||
// //! The V1 API things!
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use axum::extract::{ConnectInfo, Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use axum::headers::{CacheControl, HeaderMapExt};
|
use axum::headers::{CacheControl, HeaderMapExt};
|
||||||
use axum::middleware::from_fn;
|
use axum::middleware::from_fn;
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
|
@ -29,6 +26,7 @@ use kanidmd_lib::value::PartialValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::https::extractors::TrustedClientIp;
|
||||||
use crate::https::to_axum_response;
|
use crate::https::to_axum_response;
|
||||||
|
|
||||||
use super::middleware::caching::dont_cache_me;
|
use super::middleware::caching::dont_cache_me;
|
||||||
|
@ -1244,24 +1242,15 @@ pub async fn applinks_get(
|
||||||
|
|
||||||
pub async fn reauth(
|
pub async fn reauth(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>, // TODO: test x-ff-headers
|
TrustedClientIp(ip_addr): TrustedClientIp,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
session: WritableSession,
|
session: WritableSession,
|
||||||
Json(obj): Json<AuthIssueSession>,
|
Json(obj): Json<AuthIssueSession>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// TODO: xff things check that we can get the remote IP address first, since this doesn't touch the backend at all
|
|
||||||
// let ip_addr = req.get_remote_addr().ok_or_else(|| {
|
|
||||||
// error!("Unable to get remote addr for auth event, refusing to proceed");
|
|
||||||
// tide::Error::from_str(
|
|
||||||
// tide::StatusCode::InternalServerError,
|
|
||||||
// "unable to validate peer address",
|
|
||||||
// )
|
|
||||||
// })?;
|
|
||||||
|
|
||||||
// This may change in the future ...
|
// This may change in the future ...
|
||||||
let inter = state
|
let inter = state
|
||||||
.qe_r_ref
|
.qe_r_ref
|
||||||
.handle_reauth(kopid.uat, obj, kopid.eventid, addr.ip())
|
.handle_reauth(kopid.uat, obj, kopid.eventid, ip_addr)
|
||||||
.await;
|
.await;
|
||||||
debug!("REAuth result: {:?}", inter);
|
debug!("REAuth result: {:?}", inter);
|
||||||
auth_session_state_management(state, inter, session)
|
auth_session_state_management(state, inter, session)
|
||||||
|
@ -1269,28 +1258,16 @@ pub async fn reauth(
|
||||||
|
|
||||||
pub async fn auth(
|
pub async fn auth(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
|
TrustedClientIp(ip_addr): TrustedClientIp,
|
||||||
session: WritableSession,
|
session: WritableSession,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
|
||||||
Json(obj): Json<AuthRequest>,
|
Json(obj): Json<AuthRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
// TODO: check this trusts the x-ff-header
|
|
||||||
let ip_addr = addr.ip();
|
|
||||||
// check that we can get the remote IP address first, since this doesn't touch the backend at all
|
|
||||||
// let ip_addr = req.get_remote_addr().ok_or_else(|| {
|
|
||||||
// error!("Unable to get remote addr for auth event, refusing to proceed");
|
|
||||||
// tide::Error::from_str(
|
|
||||||
// tide::StatusCode::InternalServerError,
|
|
||||||
// "unable to validate peer address",
|
|
||||||
// )
|
|
||||||
// })?;
|
|
||||||
|
|
||||||
// First, deal with some state management.
|
// First, deal with some state management.
|
||||||
// Do anything here first that's needed like getting the session details
|
// Do anything here first that's needed like getting the session details
|
||||||
// out of the req cookie.
|
// out of the req cookie.
|
||||||
|
|
||||||
// TODO
|
|
||||||
let maybe_sessionid = state.get_current_auth_session_id(&headers, &session);
|
let maybe_sessionid = state.get_current_auth_session_id(&headers, &session);
|
||||||
debug!("Session ID: {:?}", maybe_sessionid);
|
debug!("Session ID: {:?}", maybe_sessionid);
|
||||||
// We probably need to know if we allocate the cookie, that this is a
|
// We probably need to know if we allocate the cookie, that this is a
|
||||||
|
@ -1434,281 +1411,295 @@ pub async fn auth_valid(
|
||||||
#[instrument(skip(state))]
|
#[instrument(skip(state))]
|
||||||
pub fn router(state: ServerState) -> Router<ServerState> {
|
pub fn router(state: ServerState) -> Router<ServerState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/oauth2", get(super::oauth2::oauth2_get))
|
.route("/v1/oauth2", get(super::oauth2::oauth2_get))
|
||||||
.route(
|
.route(
|
||||||
"/oauth2/:rs_name",
|
"/v1/oauth2/:rs_name",
|
||||||
get(super::oauth2::oauth2_id_get)
|
get(super::oauth2::oauth2_id_get)
|
||||||
.patch(super::oauth2::oauth2_id_patch)
|
.patch(super::oauth2::oauth2_id_patch)
|
||||||
.delete(super::oauth2::oauth2_id_delete),
|
.delete(super::oauth2::oauth2_id_delete),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/oauth2/:rs_name/_basic_secret",
|
"/v1/oauth2/:rs_name/_basic_secret",
|
||||||
get(super::oauth2::oauth2_id_get_basic_secret),
|
get(super::oauth2::oauth2_id_get_basic_secret),
|
||||||
)
|
)
|
||||||
.route("/oauth2/_basic", post(super::oauth2::oauth2_basic_post))
|
.route("/v1/oauth2/_basic", post(super::oauth2::oauth2_basic_post))
|
||||||
.route(
|
.route(
|
||||||
"/oauth2/:rs_name/_scopemap/:group",
|
"/v1/oauth2/:rs_name/_scopemap/:group",
|
||||||
post(super::oauth2::oauth2_id_scopemap_post)
|
post(super::oauth2::oauth2_id_scopemap_post)
|
||||||
.delete(super::oauth2::oauth2_id_scopemap_delete),
|
.delete(super::oauth2::oauth2_id_scopemap_delete),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/oauth2/:rs_name/_sup_scopemap/:group",
|
"/v1/oauth2/:rs_name/_sup_scopemap/:group",
|
||||||
post(super::oauth2::oauth2_id_sup_scopemap_post)
|
post(super::oauth2::oauth2_id_sup_scopemap_post)
|
||||||
.delete(super::oauth2::oauth2_id_sup_scopemap_delete),
|
.delete(super::oauth2::oauth2_id_sup_scopemap_delete),
|
||||||
)
|
)
|
||||||
.route("/raw/create", post(create))
|
.route("/v1/raw/create", post(create))
|
||||||
.route("/raw/modify", post(v1_modify))
|
.route("/v1/raw/modify", post(v1_modify))
|
||||||
.route("/raw/delete", post(v1_delete))
|
.route("/v1/raw/delete", post(v1_delete))
|
||||||
.route("/raw/search", post(search))
|
.route("/v1/raw/search", post(search))
|
||||||
.route("/schema", get(schema_get))
|
.route("/v1/schema", get(schema_get))
|
||||||
.route(
|
.route(
|
||||||
"/schema/attributetype",
|
"/v1/schema/attributetype",
|
||||||
get(schema_attributetype_get), // post(|| async { "TODO" })
|
get(schema_attributetype_get), // post(|| async { "TODO" })
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/schema/attributetype/:id",
|
"/v1/schema/attributetype/:id",
|
||||||
get(schema_attributetype_get_id),
|
get(schema_attributetype_get_id),
|
||||||
)
|
)
|
||||||
// .route("/schema/attributetype/:id", put(|| async { "TODO" }).patch(|| async { "TODO" }))
|
// .route("/schema/attributetype/:id", put(|| async { "TODO" }).patch(|| async { "TODO" }))
|
||||||
.route(
|
.route(
|
||||||
"/schema/classtype",
|
"/v1/schema/classtype",
|
||||||
get(schema_classtype_get), // .post(|| async { "TODO" })
|
get(schema_classtype_get), // .post(|| async { "TODO" })
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/schema/classtype/:id",
|
"/v1/schema/classtype/:id",
|
||||||
get(schema_classtype_get_id)
|
get(schema_classtype_get_id)
|
||||||
.put(|| async { "TODO" })
|
.put(|| async { "TODO" })
|
||||||
.patch(|| async { "TODO" }),
|
.patch(|| async { "TODO" }),
|
||||||
)
|
)
|
||||||
.route("/self", get(whoami))
|
.route("/v1/self", get(whoami))
|
||||||
.route("/self/_uat", get(whoami_uat))
|
.route("/v1/self/_uat", get(whoami_uat))
|
||||||
.route("/self/_attr/:attr", get(|| async { "TODO" }))
|
.route("/v1/self/_attr/:attr", get(|| async { "TODO" }))
|
||||||
.route("/self/_credential", get(|| async { "TODO" }))
|
.route("/v1/self/_credential", get(|| async { "TODO" }))
|
||||||
.route("/self/_credential/:cid/_lock", get(|| async { "TODO" }))
|
.route("/v1/self/_credential/:cid/_lock", get(|| async { "TODO" }))
|
||||||
.route(
|
.route(
|
||||||
"/self/_radius",
|
"/v1/self/_radius",
|
||||||
get(|| async { "TODO" })
|
get(|| async { "TODO" })
|
||||||
.delete(|| async { "TODO" })
|
.delete(|| async { "TODO" })
|
||||||
.post(|| async { "TODO" }),
|
.post(|| async { "TODO" }),
|
||||||
)
|
)
|
||||||
.route("/self/_radius/_config", post(|| async { "TODO" }))
|
.route("/v1/self/_radius/_config", post(|| async { "TODO" }))
|
||||||
.route("/self/_radius/_config/:token", get(|| async { "TODO" }))
|
.route("/v1/self/_radius/_config/:token", get(|| async { "TODO" }))
|
||||||
.route(
|
.route(
|
||||||
"/self/_radius/_config/:token/apple",
|
"/v1/self/_radius/_config/:token/apple",
|
||||||
get(|| async { "TODO" }),
|
get(|| async { "TODO" }),
|
||||||
)
|
)
|
||||||
// Applinks are the list of apps this account can access.
|
// Applinks are the list of apps this account can access.
|
||||||
.route("/self/_applinks", get(applinks_get))
|
.route("/v1/self/_applinks", get(applinks_get))
|
||||||
// Person routes
|
// Person routes
|
||||||
.route("/person", get(person_get))
|
.route("/v1/person", get(person_get))
|
||||||
.route("/person", post(person_post))
|
.route("/v1/person", post(person_post))
|
||||||
.route(
|
.route(
|
||||||
"/person/:id",
|
"/v1/person/:id",
|
||||||
get(person_id_get)
|
get(person_id_get)
|
||||||
.patch(account_id_patch)
|
.patch(account_id_patch)
|
||||||
.delete(person_account_id_delete),
|
.delete(person_account_id_delete),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_attr/:attr",
|
"/v1/person/:id/_attr/:attr",
|
||||||
get(account_id_get_attr)
|
get(account_id_get_attr)
|
||||||
.put(account_id_put_attr)
|
.put(account_id_put_attr)
|
||||||
.post(account_id_post_attr)
|
.post(account_id_post_attr)
|
||||||
.delete(account_id_delete_attr),
|
.delete(account_id_delete_attr),
|
||||||
)
|
)
|
||||||
.route("/person/:id/_lock", get(|| async { "TODO" }))
|
.route("/v1/person/:id/_lock", get(|| async { "TODO" }))
|
||||||
.route("/person/:id/_credential", get(|| async { "TODO" }))
|
.route("/v1/person/:id/_credential", get(|| async { "TODO" }))
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_credential/_status",
|
"/v1/person/:id/_credential/_status",
|
||||||
get(account_get_id_credential_status),
|
get(account_get_id_credential_status),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_credential/:cid/_lock",
|
"/v1/person/:id/_credential/:cid/_lock",
|
||||||
get(|| async { "TODO" }),
|
get(|| async { "TODO" }),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_credential/_update",
|
"/v1/person/:id/_credential/_update",
|
||||||
get(account_get_id_credential_update),
|
get(account_get_id_credential_update),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_credential/_update_intent/:ttl",
|
"/v1/person/:id/_credential/_update_intent/:ttl",
|
||||||
get(account_get_id_credential_update_intent_ttl),
|
get(account_get_id_credential_update_intent_ttl),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_credential/_update_intent",
|
"/v1/person/:id/_credential/_update_intent",
|
||||||
get(account_get_id_credential_update_intent),
|
get(account_get_id_credential_update_intent),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_ssh_pubkeys",
|
"/v1/person/:id/_ssh_pubkeys",
|
||||||
get(account_get_id_ssh_pubkeys).post(account_post_id_ssh_pubkey),
|
get(account_get_id_ssh_pubkeys).post(account_post_id_ssh_pubkey),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_ssh_pubkeys/:tag",
|
"/v1/person/:id/_ssh_pubkeys/:tag",
|
||||||
get(account_get_id_ssh_pubkey_tag).delete(account_delete_id_ssh_pubkey_tag),
|
get(account_get_id_ssh_pubkey_tag).delete(account_delete_id_ssh_pubkey_tag),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_radius",
|
"/v1/person/:id/_radius",
|
||||||
get(account_get_id_radius)
|
get(account_get_id_radius)
|
||||||
.post(account_post_id_radius_regenerate)
|
.post(account_post_id_radius_regenerate)
|
||||||
.delete(account_delete_id_radius),
|
.delete(account_delete_id_radius),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_radius/_token",
|
"/v1/person/:id/_radius/_token",
|
||||||
get(account_get_id_radius_token),
|
get(account_get_id_radius_token),
|
||||||
) // TODO: make this cacheable
|
) // TODO: make this cacheable
|
||||||
.route("/person/:id/_unix", post(account_post_id_unix))
|
.route("/v1/person/:id/_unix", post(account_post_id_unix))
|
||||||
.route(
|
.route(
|
||||||
"/person/:id/_unix/_credential",
|
"/v1/person/:id/_unix/_credential",
|
||||||
put(account_put_id_unix_credential).delete(account_delete_id_unix_credential),
|
put(account_put_id_unix_credential).delete(account_delete_id_unix_credential),
|
||||||
)
|
)
|
||||||
// Service accounts
|
// Service accounts
|
||||||
.route(
|
.route(
|
||||||
"/service_account",
|
"/v1/service_account",
|
||||||
get(service_account_get).post(service_account_post),
|
get(service_account_get).post(service_account_post),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/",
|
"/v1/service_account/",
|
||||||
get(service_account_get).post(service_account_post),
|
get(service_account_get).post(service_account_post),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id",
|
"/v1/service_account/:id",
|
||||||
get(service_account_id_get).delete(service_account_id_delete),
|
get(service_account_id_get).delete(service_account_id_delete),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_attr/:attr",
|
"/v1/service_account/:id/_attr/:attr",
|
||||||
get(account_id_get_attr)
|
get(account_id_get_attr)
|
||||||
.put(account_id_put_attr)
|
.put(account_id_put_attr)
|
||||||
.post(account_id_post_attr)
|
.post(account_id_post_attr)
|
||||||
.delete(account_id_delete_attr),
|
.delete(account_id_delete_attr),
|
||||||
)
|
)
|
||||||
.route("/service_account/:id/_lock", get(|| async { "TODO" }))
|
.route("/v1/service_account/:id/_lock", get(|| async { "TODO" }))
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_into_person",
|
"/v1/service_account/:id/_into_person",
|
||||||
post(service_account_into_person),
|
post(service_account_into_person),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_api_token",
|
"/v1/service_account/:id/_api_token",
|
||||||
post(service_account_api_token_post).get(service_account_api_token_get),
|
post(service_account_api_token_post).get(service_account_api_token_get),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_api_token/:token_id",
|
"/v1/service_account/:id/_api_token/:token_id",
|
||||||
delete(service_account_api_token_delete),
|
delete(service_account_api_token_delete),
|
||||||
)
|
)
|
||||||
.route("/service_account/:id/_credential", get(|| async { "TODO" }))
|
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_credential/_generate",
|
"/v1/service_account/:id/_credential",
|
||||||
get(service_account_credential_generate),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/service_account/:id/_credential/_status",
|
|
||||||
get(account_get_id_credential_status),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/service_account/:id/_credential/:cid/_lock",
|
|
||||||
get(|| async { "TODO" }),
|
get(|| async { "TODO" }),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_ssh_pubkeys",
|
"/v1/service_account/:id/_credential/_generate",
|
||||||
|
get(service_account_credential_generate),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/v1/service_account/:id/_credential/_status",
|
||||||
|
get(account_get_id_credential_status),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/v1/service_account/:id/_credential/:cid/_lock",
|
||||||
|
get(|| async { "TODO" }),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/v1/service_account/:id/_ssh_pubkeys",
|
||||||
get(account_get_id_ssh_pubkeys).post(account_post_id_ssh_pubkey),
|
get(account_get_id_ssh_pubkeys).post(account_post_id_ssh_pubkey),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/service_account/:id/_ssh_pubkeys/:tag",
|
"/v1/service_account/:id/_ssh_pubkeys/:tag",
|
||||||
get(account_get_id_ssh_pubkey_tag).delete(account_delete_id_ssh_pubkey_tag),
|
get(account_get_id_ssh_pubkey_tag).delete(account_delete_id_ssh_pubkey_tag),
|
||||||
)
|
)
|
||||||
.route("/service_account/:id/_unix", post(account_post_id_unix))
|
.route("/v1/service_account/:id/_unix", post(account_post_id_unix))
|
||||||
.route("/account/:id/_unix/_auth", post(account_post_id_unix_auth))
|
|
||||||
.route(
|
.route(
|
||||||
"/account/:id/_unix/_token",
|
"/v1/account/:id/_unix/_auth",
|
||||||
|
post(account_post_id_unix_auth),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/v1/account/:id/_unix/_token",
|
||||||
post(account_get_id_unix_token).get(account_get_id_unix_token), // TODO: make this cacheable
|
post(account_get_id_unix_token).get(account_get_id_unix_token), // TODO: make this cacheable
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/account/:id/_radius/_token",
|
"/v1/account/:id/_radius/_token",
|
||||||
post(account_get_id_radius_token).get(account_get_id_radius_token), // TODO: make this cacheable
|
post(account_get_id_radius_token).get(account_get_id_radius_token), // TODO: make this cacheable
|
||||||
)
|
)
|
||||||
.route("/account/:id/_ssh_pubkeys", get(account_get_id_ssh_pubkeys))
|
|
||||||
.route(
|
.route(
|
||||||
"/account/:id/_ssh_pubkeys/:tag",
|
"/v1/account/:id/_ssh_pubkeys",
|
||||||
|
get(account_get_id_ssh_pubkeys),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/v1/account/:id/_ssh_pubkeys/:tag",
|
||||||
get(account_get_id_ssh_pubkey_tag),
|
get(account_get_id_ssh_pubkey_tag),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/account/:id/_user_auth_token",
|
"/v1/account/:id/_user_auth_token",
|
||||||
get(account_get_id_user_auth_token),
|
get(account_get_id_user_auth_token),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/account/:id/_user_auth_token/:token_id",
|
"/v1/account/:id/_user_auth_token/:token_id",
|
||||||
delete(account_user_auth_token_delete),
|
delete(account_user_auth_token_delete),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/credential/_exchange_intent",
|
"/v1/credential/_exchange_intent",
|
||||||
post(credential_update_exchange_intent),
|
post(credential_update_exchange_intent),
|
||||||
)
|
)
|
||||||
.route("/credential/_status", post(credential_update_status))
|
.route("/v1/credential/_status", post(credential_update_status))
|
||||||
.route("/credential/_update", post(credential_update_update))
|
.route("/v1/credential/_update", post(credential_update_update))
|
||||||
.route("/credential/_commit", post(credential_update_commit))
|
.route("/v1/credential/_commit", post(credential_update_commit))
|
||||||
.route("/credential/_cancel", post(credential_update_cancel))
|
.route("/v1/credential/_cancel", post(credential_update_cancel))
|
||||||
// domain-things
|
// domain-things
|
||||||
.route("/domain", get(domain_get))
|
.route("/v1/domain", get(domain_get))
|
||||||
.route(
|
.route(
|
||||||
"/domain/_attr/:attr",
|
"/v1/domain/_attr/:attr",
|
||||||
get(domain_get_attr)
|
get(domain_get_attr)
|
||||||
.put(domain_put_attr)
|
.put(domain_put_attr)
|
||||||
.delete(domain_delete_attr),
|
.delete(domain_delete_attr),
|
||||||
)
|
)
|
||||||
.route("/group/:id/_unix/_token", get(group_get_id_unix_token))
|
.route("/v1/group/:id/_unix/_token", get(group_get_id_unix_token))
|
||||||
.route("/group/:id/_unix", post(group_post_id_unix))
|
.route("/v1/group/:id/_unix", post(group_post_id_unix))
|
||||||
.route("/group", get(group_get).post(group_post))
|
.route("/v1/group", get(group_get).post(group_post))
|
||||||
.route("/group/:id", get(group_id_get).delete(group_id_delete))
|
.route("/v1/group/:id", get(group_id_get).delete(group_id_delete))
|
||||||
.route(
|
.route(
|
||||||
"/group/:id/_attr/:attr",
|
"/v1/group/:id/_attr/:attr",
|
||||||
delete(group_id_delete_attr)
|
delete(group_id_delete_attr)
|
||||||
.get(group_id_get_attr)
|
.get(group_id_get_attr)
|
||||||
.put(group_id_put_attr)
|
.put(group_id_put_attr)
|
||||||
.post(group_id_post_attr),
|
.post(group_id_post_attr),
|
||||||
)
|
)
|
||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
.route("/system", get(system_get))
|
.route("/v1/system", get(system_get))
|
||||||
.route(
|
.route(
|
||||||
"/system/_attr/:attr",
|
"/v1/system/_attr/:attr",
|
||||||
get(system_get_attr)
|
get(system_get_attr)
|
||||||
.post(system_post_attr)
|
.post(system_post_attr)
|
||||||
.delete(system_delete_attr),
|
.delete(system_delete_attr),
|
||||||
)
|
)
|
||||||
.route("/recycle_bin", get(recycle_bin_get))
|
.route("/v1/recycle_bin", get(recycle_bin_get))
|
||||||
.route("/recycle_bin/:id", get(recycle_bin_id_get))
|
.route("/v1/recycle_bin/:id", get(recycle_bin_id_get))
|
||||||
.route("/recycle_bin/:id/_revive", post(recycle_bin_revive_id_post))
|
|
||||||
.route("/access_profile", get(|| async { "TODO" }))
|
|
||||||
.route("/access_profile/:id", get(|| async { "TODO" }))
|
|
||||||
.route("/access_profile/:id/_attr/:attr", get(|| async { "TODO" }))
|
|
||||||
.route("/auth", post(auth))
|
|
||||||
.route("/auth/valid", get(auth_valid))
|
|
||||||
.route("/logout", get(logout))
|
|
||||||
.route("/reauth", post(reauth))
|
|
||||||
.route(
|
.route(
|
||||||
"/sync_account",
|
"/v1/recycle_bin/:id/_revive",
|
||||||
|
post(recycle_bin_revive_id_post),
|
||||||
|
)
|
||||||
|
.route("/v1/access_profile", get(|| async { "TODO" }))
|
||||||
|
.route("/v1/access_profile/:id", get(|| async { "TODO" }))
|
||||||
|
.route(
|
||||||
|
"/v1/access_profile/:id/_attr/:attr",
|
||||||
|
get(|| async { "TODO" }),
|
||||||
|
)
|
||||||
|
.route("/v1/auth", post(auth))
|
||||||
|
.route("/v1/auth/valid", get(auth_valid))
|
||||||
|
.route("/v1/logout", get(logout))
|
||||||
|
.route("/v1/reauth", post(reauth))
|
||||||
|
.route(
|
||||||
|
"/v1/sync_account",
|
||||||
get(sync_account_get).post(sync_account_post),
|
get(sync_account_get).post(sync_account_post),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/",
|
"/v1/sync_account/",
|
||||||
get(sync_account_get).post(sync_account_post),
|
get(sync_account_get).post(sync_account_post),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/:id",
|
"/v1/sync_account/:id",
|
||||||
get(sync_account_id_get).patch(sync_account_id_patch),
|
get(sync_account_id_get).patch(sync_account_id_patch),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/:id/_attr/:attr",
|
"/v1/sync_account/:id/_attr/:attr",
|
||||||
get(sync_account_id_get_attr).put(sync_account_id_put_attr),
|
get(sync_account_id_get_attr).put(sync_account_id_put_attr),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/:id/_finalise",
|
"/v1/sync_account/:id/_finalise",
|
||||||
get(sync_account_id_get_finalise),
|
get(sync_account_id_get_finalise),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/:id/_terminate",
|
"/v1/sync_account/:id/_terminate",
|
||||||
get(sync_account_id_get_terminate),
|
get(sync_account_id_get_terminate),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/sync_account/:id/_sync_token",
|
"/v1/sync_account/:id/_sync_token",
|
||||||
// .get(&mut sync_account_token_get)
|
|
||||||
post(sync_account_token_post).delete(sync_account_token_delete),
|
post(sync_account_token_post).delete(sync_account_token_delete),
|
||||||
)
|
)
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
|
|
|
@ -254,6 +254,6 @@ pub fn scim_route_setup() -> Router<ServerState> {
|
||||||
//
|
//
|
||||||
// POST Send a sync update
|
// POST Send a sync update
|
||||||
//
|
//
|
||||||
.route("/v1/Sync", post(scim_sync_post).get(scim_sync_get))
|
.route("/scim/v1/Sync", post(scim_sync_post).get(scim_sync_get))
|
||||||
.route("/v1/Sink", get(scim_sink_get))
|
.route("/scim/v1/Sink", get(scim_sink_get))
|
||||||
}
|
}
|
||||||
|
|
|
@ -762,7 +762,7 @@ pub async fn create_server_core(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let cookie_key: [u8; 32] = idms.get_cookie_key();
|
let cookie_key: [u8; 64] = idms.get_cookie_key();
|
||||||
|
|
||||||
// Any pre-start tasks here.
|
// Any pre-start tasks here.
|
||||||
match &config.integration_test_config {
|
match &config.integration_test_config {
|
||||||
|
|
|
@ -126,21 +126,15 @@ async fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// if they specified it in the environment then that overrides everything
|
// We only allow config file for log level now.
|
||||||
let log_filter = match EnvFilter::try_from_default_env() {
|
let log_filter: EnvFilter = match sconfig.as_ref() {
|
||||||
Ok(val) => val,
|
Some(val) => {
|
||||||
Err(_e) => {
|
let tmp = val.log_level.clone();
|
||||||
// we couldn't get it from the env, so we'll try the config file!
|
tmp.unwrap_or_default()
|
||||||
match sconfig.as_ref() {
|
|
||||||
Some(val) => {
|
|
||||||
let tmp = val.log_level.clone();
|
|
||||||
tmp.unwrap_or_default()
|
|
||||||
}
|
|
||||||
None => LogLevel::Info,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
};
|
None => LogLevel::Info,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
// TODO: only send to stderr when we're not in a TTY
|
// TODO: only send to stderr when we're not in a TTY
|
||||||
tracing_forest::worker_task()
|
tracing_forest::worker_task()
|
||||||
|
|
|
@ -585,7 +585,7 @@ lazy_static! {
|
||||||
"description",
|
"description",
|
||||||
Value::new_utf8s("System (local) info and metadata object.")
|
Value::new_utf8s("System (local) info and metadata object.")
|
||||||
),
|
),
|
||||||
("version", Value::Uint32(12))
|
("version", Value::Uint32(13))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub struct DomainKeys {
|
||||||
pub(crate) uat_jwt_signer: JwsSigner,
|
pub(crate) uat_jwt_signer: JwsSigner,
|
||||||
pub(crate) uat_jwt_validator: JwsValidator,
|
pub(crate) uat_jwt_validator: JwsValidator,
|
||||||
pub(crate) token_enc_key: Fernet,
|
pub(crate) token_enc_key: Fernet,
|
||||||
pub(crate) cookie_key: [u8; 32],
|
pub(crate) cookie_key: [u8; 64],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdmServer {
|
pub struct IdmServer {
|
||||||
|
@ -262,7 +262,7 @@ impl IdmServer {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cookie_key(&self) -> [u8; 32] {
|
pub fn get_cookie_key(&self) -> [u8; 64] {
|
||||||
self.domain_keys.read().cookie_key
|
self.domain_keys.read().cookie_key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@ impl Domain {
|
||||||
|
|
||||||
if !e.attribute_pres("private_cookie_key") {
|
if !e.attribute_pres("private_cookie_key") {
|
||||||
security_info!("regenerating domain cookie key");
|
security_info!("regenerating domain cookie key");
|
||||||
let mut key = [0; 32];
|
let mut key = [0; 64];
|
||||||
let mut rng = StdRng::from_entropy();
|
let mut rng = StdRng::from_entropy();
|
||||||
rng.fill(&mut key);
|
rng.fill(&mut key);
|
||||||
let v = Value::new_privatebinary(&key);
|
let v = Value::new_privatebinary(&key);
|
||||||
|
|
|
@ -99,6 +99,10 @@ impl QueryServer {
|
||||||
if system_info_version < 12 {
|
if system_info_version < 12 {
|
||||||
write_txn.migrate_11_to_12()?;
|
write_txn.migrate_11_to_12()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if system_info_version < 13 {
|
||||||
|
write_txn.migrate_12_to_13()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_txn.reload()?;
|
write_txn.reload()?;
|
||||||
|
@ -391,6 +395,19 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
self.internal_apply_writable(mod_candidates)
|
self.internal_apply_writable(mod_candidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
|
pub fn migrate_12_to_13(&mut self) -> Result<(), OperationError> {
|
||||||
|
admin_warn!("starting 12 to 13 migration.");
|
||||||
|
let filter = filter!(f_and!([
|
||||||
|
f_eq("class", PVCLASS_DOMAIN_INFO.clone()),
|
||||||
|
f_eq("uuid", PVUUID_DOMAIN_INFO.clone()),
|
||||||
|
]));
|
||||||
|
// Delete the existing cookie key to trigger a regeneration.
|
||||||
|
let modlist = ModifyList::new_purge("private_cookie_key");
|
||||||
|
self.internal_modify(&filter, &modlist)
|
||||||
|
// Complete
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> {
|
pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> {
|
||||||
admin_debug!("initialise_schema_core -> start ...");
|
admin_debug!("initialise_schema_core -> start ...");
|
||||||
|
|
|
@ -763,12 +763,12 @@ pub trait QueryServerTransaction<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_domain_cookie_key(&mut self) -> Result<[u8; 32], OperationError> {
|
fn get_domain_cookie_key(&mut self) -> Result<[u8; 64], OperationError> {
|
||||||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||||
.and_then(|e| {
|
.and_then(|e| {
|
||||||
e.get_ava_single_private_binary("private_cookie_key")
|
e.get_ava_single_private_binary("private_cookie_key")
|
||||||
.and_then(|s| {
|
.and_then(|s| {
|
||||||
let mut x = [0; 32];
|
let mut x = [0; 64];
|
||||||
if s.len() == x.len() {
|
if s.len() == x.len() {
|
||||||
x.copy_from_slice(s);
|
x.copy_from_slice(s);
|
||||||
Some(x)
|
Some(x)
|
||||||
|
|
Loading…
Reference in a new issue