mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-10 11:03:55 +02:00
fix: unrecoverable error page doesn't include logo or domain name (#3352)
This commit is contained in:
parent
2d439c508f
commit
419c4a1827
server
|
@ -88,6 +88,7 @@ webauthn-rs = { workspace = true, features = [
|
|||
[dev-dependencies]
|
||||
walkdir = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
kanidmd_lib = { workspace = true, features = ["test"] }
|
||||
|
||||
[build-dependencies]
|
||||
kanidm_build_profiles = { workspace = true }
|
||||
|
|
|
@ -43,7 +43,7 @@ pub(crate) async fn view_apps_get(
|
|||
.qe_r_ref
|
||||
.handle_list_applinks(client_auth_info, kopid.eventid)
|
||||
.await
|
||||
.map_err(|old| HtmxError::new(&kopid, old))?;
|
||||
.map_err(|old| HtmxError::new(&kopid, old, domain_info.clone()))?;
|
||||
|
||||
Ok({
|
||||
(
|
||||
|
|
|
@ -50,7 +50,7 @@ pub(crate) async fn view_enrol_get(
|
|||
.qe_r_ref
|
||||
.handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
|
||||
.await
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))?;
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))?;
|
||||
|
||||
let time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0);
|
||||
let can_rw = uat.purpose_readwrite_active(time);
|
||||
|
@ -87,7 +87,7 @@ pub(crate) async fn view_enrol_get(
|
|||
kopid.eventid,
|
||||
)
|
||||
.await
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))?;
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))?;
|
||||
|
||||
let secret = cu_intent.token;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Redirect, Response};
|
||||
use axum_htmx::{HxReswap, HxRetarget, SwapOption};
|
||||
use kanidmd_lib::idm::server::DomainInfoRead;
|
||||
use utoipa::ToSchema;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -21,19 +22,19 @@ use crate::https::views::UnrecoverableErrorView;
|
|||
#[derive(Debug, ToSchema)]
|
||||
pub(crate) enum HtmxError {
|
||||
/// Something went wrong when doing things.
|
||||
OperationError(Uuid, OperationError),
|
||||
OperationError(Uuid, OperationError, DomainInfoRead),
|
||||
}
|
||||
|
||||
impl HtmxError {
|
||||
pub(crate) fn new(kopid: &KOpId, operr: OperationError) -> Self {
|
||||
HtmxError::OperationError(kopid.eventid, operr)
|
||||
pub(crate) fn new(kopid: &KOpId, operr: OperationError, domain_info: DomainInfoRead) -> Self {
|
||||
HtmxError::OperationError(kopid.eventid, operr, domain_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for HtmxError {
|
||||
fn into_response(self) -> Response {
|
||||
match self {
|
||||
HtmxError::OperationError(kopid, inner) => {
|
||||
HtmxError::OperationError(kopid, inner, domain_info) => {
|
||||
let body = serde_json::to_string(&inner).unwrap_or(inner.to_string());
|
||||
match &inner {
|
||||
OperationError::NotAuthenticated
|
||||
|
@ -58,6 +59,7 @@ impl IntoResponse for HtmxError {
|
|||
UnrecoverableErrorView {
|
||||
err_code: inner,
|
||||
operation_id: kopid,
|
||||
domain_info,
|
||||
},
|
||||
)
|
||||
.into_response(),
|
||||
|
|
|
@ -48,6 +48,7 @@ struct SessionContext {
|
|||
after_auth_loc: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ReauthPurpose {
|
||||
ProfileSettings,
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ impl fmt::Display for ReauthPurpose {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LoginError {
|
||||
InvalidUsername,
|
||||
}
|
||||
|
@ -71,16 +72,17 @@ impl fmt::Display for LoginError {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Reauth {
|
||||
pub username: String,
|
||||
pub purpose: ReauthPurpose,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Oauth2Ctx {
|
||||
pub client_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LoginDisplayCtx {
|
||||
pub domain_info: DomainInfoRead,
|
||||
// We only need this on the first re-auth screen to indicate what we are doing
|
||||
|
@ -160,6 +162,7 @@ pub async fn view_logout_get(
|
|||
State(state): State<ServerState>,
|
||||
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
mut jar: CookieJar,
|
||||
) -> Response {
|
||||
let response = if let Err(err_code) = state
|
||||
|
@ -170,6 +173,7 @@ pub async fn view_logout_get(
|
|||
UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response()
|
||||
} else {
|
||||
|
@ -232,7 +236,7 @@ pub async fn view_reauth_get(
|
|||
ar,
|
||||
client_auth_info,
|
||||
session_context,
|
||||
display_ctx,
|
||||
display_ctx.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -241,6 +245,7 @@ pub async fn view_reauth_get(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.clone().domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -249,6 +254,7 @@ pub async fn view_reauth_get(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -275,6 +281,7 @@ pub async fn view_reauth_get(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -358,6 +365,7 @@ pub async fn view_index_get(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -420,7 +428,7 @@ pub async fn view_login_begin_post(
|
|||
};
|
||||
|
||||
let mut display_ctx = LoginDisplayCtx {
|
||||
domain_info,
|
||||
domain_info: domain_info.clone(),
|
||||
oauth2: None,
|
||||
reauth: None,
|
||||
error: None,
|
||||
|
@ -445,6 +453,7 @@ pub async fn view_login_begin_post(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -463,6 +472,7 @@ pub async fn view_login_begin_post(
|
|||
_ => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
},
|
||||
|
@ -503,7 +513,7 @@ pub async fn view_login_mech_choose_post(
|
|||
.await;
|
||||
|
||||
let display_ctx = LoginDisplayCtx {
|
||||
domain_info,
|
||||
domain_info: domain_info.clone(),
|
||||
oauth2: None,
|
||||
reauth: None,
|
||||
error: None,
|
||||
|
@ -528,6 +538,7 @@ pub async fn view_login_mech_choose_post(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -536,6 +547,7 @@ pub async fn view_login_mech_choose_post(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -662,7 +674,7 @@ pub async fn view_login_passkey_post(
|
|||
}
|
||||
Err(e) => {
|
||||
error!(err = ?e, "Unable to deserialize credential submission");
|
||||
HtmxError::new(&kopid, OperationError::SerdeJsonError).into_response()
|
||||
HtmxError::new(&kopid, OperationError::SerdeJsonError, domain_info).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -692,7 +704,7 @@ async fn credential_step(
|
|||
.unwrap_or_default();
|
||||
|
||||
let display_ctx = LoginDisplayCtx {
|
||||
domain_info,
|
||||
domain_info: domain_info.clone(),
|
||||
oauth2: None,
|
||||
reauth: None,
|
||||
error: None,
|
||||
|
@ -720,7 +732,7 @@ async fn credential_step(
|
|||
ar,
|
||||
client_auth_info,
|
||||
session_context,
|
||||
display_ctx,
|
||||
display_ctx.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -729,6 +741,7 @@ async fn credential_step(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -737,6 +750,7 @@ async fn credential_step(
|
|||
Err(err_code) => UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
}
|
||||
|
@ -785,6 +799,7 @@ async fn view_login_step(
|
|||
UnrecoverableErrorView {
|
||||
err_code: OperationError::InvalidState,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.domain_info,
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
|
@ -845,6 +860,7 @@ async fn view_login_step(
|
|||
UnrecoverableErrorView {
|
||||
err_code: OperationError::InvalidState,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info: display_ctx.domain_info,
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ use axum::{
|
|||
use axum_htmx::HxRequestGuardLayer;
|
||||
|
||||
use constants::Urls;
|
||||
use kanidmd_lib::prelude::{OperationError, Uuid};
|
||||
use kanidmd_lib::{
|
||||
idm::server::DomainInfoRead,
|
||||
prelude::{OperationError, Uuid},
|
||||
};
|
||||
|
||||
use crate::https::ServerState;
|
||||
|
||||
|
@ -29,6 +32,8 @@ mod reset;
|
|||
struct UnrecoverableErrorView {
|
||||
err_code: OperationError,
|
||||
operation_id: Uuid,
|
||||
// This is an option because it's not always present in an "unrecoverable" situation
|
||||
domain_info: DomainInfoRead,
|
||||
}
|
||||
|
||||
pub fn view_router() -> Router<ServerState> {
|
||||
|
@ -130,3 +135,29 @@ where
|
|||
.map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use askama_axum::IntoResponse;
|
||||
|
||||
use super::*;
|
||||
#[tokio::test]
|
||||
async fn test_unrecoverableerrorview() {
|
||||
let domain_info = kanidmd_lib::server::DomainInfo::new_test();
|
||||
|
||||
let view = UnrecoverableErrorView {
|
||||
err_code: OperationError::InvalidState,
|
||||
operation_id: Uuid::new_v4(),
|
||||
domain_info: domain_info.read(),
|
||||
};
|
||||
|
||||
let error_html = view.render().expect("Failed to render");
|
||||
|
||||
assert!(error_html.contains(domain_info.read().display_name()));
|
||||
|
||||
let response = view.into_response();
|
||||
|
||||
// TODO: this really should be an error code :(
|
||||
assert_eq!(response.status(), 200);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ async fn oauth2_auth_req(
|
|||
UnrecoverableErrorView {
|
||||
err_code: OperationError::UI0003InvalidOauth2Resume,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
},
|
||||
)
|
||||
.into_response();
|
||||
|
@ -184,6 +185,7 @@ async fn oauth2_auth_req(
|
|||
UnrecoverableErrorView {
|
||||
err_code,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
},
|
||||
)
|
||||
.into_response(),
|
||||
|
@ -221,6 +223,7 @@ async fn oauth2_auth_req(
|
|||
UnrecoverableErrorView {
|
||||
err_code: OperationError::InvalidState,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
},
|
||||
)
|
||||
.into_response()
|
||||
|
@ -240,6 +243,7 @@ pub async fn view_consent_post(
|
|||
State(server_state): State<ServerState>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Form(consent_form): Form<ConsentForm>,
|
||||
) -> Result<Response, UnrecoverableErrorView> {
|
||||
|
@ -291,6 +295,7 @@ pub async fn view_consent_post(
|
|||
Err(UnrecoverableErrorView {
|
||||
err_code: OperationError::InvalidState,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ pub(crate) async fn view_profile_unlock_get(
|
|||
.qe_r_ref
|
||||
.handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
|
||||
.await
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))?;
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))?;
|
||||
|
||||
let display_ctx = LoginDisplayCtx {
|
||||
domain_info,
|
||||
|
|
|
@ -209,6 +209,7 @@ pub(crate) async fn commit(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token: CUSessionToken = get_cu_session(&jar).await?;
|
||||
|
@ -216,7 +217,7 @@ pub(crate) async fn commit(
|
|||
state
|
||||
.qe_w_ref
|
||||
.handle_idmcredentialupdatecommit(cu_session_token, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info))
|
||||
.await?;
|
||||
|
||||
// No longer need the cookie jar.
|
||||
|
@ -230,6 +231,7 @@ pub(crate) async fn cancel_cred_update(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token: CUSessionToken = get_cu_session(&jar).await?;
|
||||
|
@ -237,7 +239,7 @@ pub(crate) async fn cancel_cred_update(
|
|||
state
|
||||
.qe_w_ref
|
||||
.handle_idmcredentialupdatecancel(cu_session_token, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info))
|
||||
.await?;
|
||||
|
||||
// No longer need the cookie jar.
|
||||
|
@ -256,6 +258,7 @@ pub(crate) async fn cancel_mfareg(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token: CUSessionToken = get_cu_session(&jar).await?;
|
||||
|
@ -263,7 +266,7 @@ pub(crate) async fn cancel_mfareg(
|
|||
let cu_status = state
|
||||
.qe_r_ref
|
||||
.handle_idmcredentialupdate(cu_session_token, CURequest::CancelMFAReg, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -274,6 +277,7 @@ pub(crate) async fn remove_alt_creds(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token: CUSessionToken = get_cu_session(&jar).await?;
|
||||
|
@ -281,7 +285,7 @@ pub(crate) async fn remove_alt_creds(
|
|||
let cu_status = state
|
||||
.qe_r_ref
|
||||
.handle_idmcredentialupdate(cu_session_token, CURequest::PrimaryRemove, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -292,6 +296,7 @@ pub(crate) async fn remove_unixcred(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token: CUSessionToken = get_cu_session(&jar).await?;
|
||||
|
@ -303,7 +308,7 @@ pub(crate) async fn remove_unixcred(
|
|||
CURequest::UnixPasswordRemove,
|
||||
kopid.eventid,
|
||||
)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -314,6 +319,7 @@ pub(crate) async fn remove_totp(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Form(totp): Form<TOTPRemoveData>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -326,7 +332,7 @@ pub(crate) async fn remove_totp(
|
|||
CURequest::TotpRemove(totp.name),
|
||||
kopid.eventid,
|
||||
)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -337,6 +343,7 @@ pub(crate) async fn remove_passkey(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Form(passkey): Form<PasskeyRemoveData>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -349,7 +356,7 @@ pub(crate) async fn remove_passkey(
|
|||
CURequest::PasskeyRemove(passkey.uuid),
|
||||
kopid.eventid,
|
||||
)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -358,8 +365,7 @@ pub(crate) async fn remove_passkey(
|
|||
pub(crate) async fn finish_passkey(
|
||||
State(state): State<ServerState>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Form(passkey_create): Form<PasskeyCreateForm>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -377,7 +383,7 @@ pub(crate) async fn finish_passkey(
|
|||
let cu_status = state
|
||||
.qe_r_ref
|
||||
.handle_idmcredentialupdate(cu_session_token, cu_request, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
Ok(get_cu_partial_response(cu_status))
|
||||
|
@ -386,7 +392,7 @@ pub(crate) async fn finish_passkey(
|
|||
error!("Bad request for passkey creation: {e}");
|
||||
Ok((
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
HtmxError::new(&kopid, OperationError::Backend).into_response(),
|
||||
HtmxError::new(&kopid, OperationError::Backend, domain_info).into_response(),
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
|
@ -398,6 +404,7 @@ pub(crate) async fn view_new_passkey(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
Form(init_form): Form<PasskeyInitForm>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -410,7 +417,7 @@ pub(crate) async fn view_new_passkey(
|
|||
let cu_status: CUStatus = state
|
||||
.qe_r_ref
|
||||
.handle_idmcredentialupdate(cu_session_token, cu_req, kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
let response = match cu_status.mfaregstate {
|
||||
|
@ -425,6 +432,7 @@ pub(crate) async fn view_new_passkey(
|
|||
UnrecoverableErrorView {
|
||||
err_code: OperationError::UI0001ChallengeSerialisation,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
|
@ -432,6 +440,7 @@ pub(crate) async fn view_new_passkey(
|
|||
_ => UnrecoverableErrorView {
|
||||
err_code: OperationError::UI0002InvalidState,
|
||||
operation_id: kopid.eventid,
|
||||
domain_info,
|
||||
}
|
||||
.into_response(),
|
||||
};
|
||||
|
@ -449,8 +458,7 @@ pub(crate) async fn view_new_passkey(
|
|||
pub(crate) async fn view_new_totp(
|
||||
State(state): State<ServerState>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
) -> axum::response::Result<Response> {
|
||||
let cu_session_token = get_cu_session(&jar).await?;
|
||||
|
@ -462,7 +470,7 @@ pub(crate) async fn view_new_totp(
|
|||
.await
|
||||
// TODO: better handling for invalid mfaregstate state, can be invalid if certain mfa flows were interrupted
|
||||
// TODO: We should maybe automatically cancel the other MFA reg
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))?;
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))?;
|
||||
|
||||
let partial = if let CURegState::TotpCheck(secret) = cu_status.mfaregstate {
|
||||
let uri = secret.to_uri();
|
||||
|
@ -491,6 +499,7 @@ pub(crate) async fn view_new_totp(
|
|||
return Err(ErrorResponse::from(HtmxError::new(
|
||||
&kopid,
|
||||
OperationError::CannotStartMFADuringOngoingMFASession,
|
||||
domain_info,
|
||||
)));
|
||||
};
|
||||
|
||||
|
@ -502,6 +511,7 @@ pub(crate) async fn add_totp(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
new_totp_form: Form<NewTotp>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -525,7 +535,7 @@ pub(crate) async fn add_totp(
|
|||
)
|
||||
}
|
||||
.await
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))?;
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))?;
|
||||
|
||||
let check = match &cu_status.mfaregstate {
|
||||
CURegState::None => return Ok(get_cu_partial_response(cu_status)),
|
||||
|
@ -544,6 +554,7 @@ pub(crate) async fn add_totp(
|
|||
return Err(ErrorResponse::from(HtmxError::new(
|
||||
&kopid,
|
||||
OperationError::InvalidState,
|
||||
domain_info,
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
@ -574,6 +585,7 @@ pub(crate) async fn view_new_pwd(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
opt_form: Option<Form<NewPassword>>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -609,7 +621,13 @@ pub(crate) async fn view_new_pwd(
|
|||
Err(OperationError::PasswordQuality(password_feedback)) => {
|
||||
(password_feedback, StatusCode::UNPROCESSABLE_ENTITY)
|
||||
}
|
||||
Err(operr) => return Err(ErrorResponse::from(HtmxError::new(&kopid, operr))),
|
||||
Err(operr) => {
|
||||
return Err(ErrorResponse::from(HtmxError::new(
|
||||
&kopid,
|
||||
operr,
|
||||
domain_info,
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(vec![], StatusCode::UNPROCESSABLE_ENTITY)
|
||||
|
@ -641,7 +659,7 @@ pub(crate) async fn view_self_reset_get(
|
|||
let uat: UserAuthToken = state
|
||||
.qe_r_ref
|
||||
.handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
let time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0);
|
||||
|
@ -651,7 +669,7 @@ pub(crate) async fn view_self_reset_get(
|
|||
let (cu_session_token, cu_status) = state
|
||||
.qe_w_ref
|
||||
.handle_idmcredentialupdate(client_auth_info, uat.uuid.to_string(), kopid.eventid)
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err))
|
||||
.map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
|
||||
.await?;
|
||||
|
||||
let cu_resp = get_cu_response(domain_info, cu_status, true);
|
||||
|
@ -698,6 +716,7 @@ pub(crate) async fn view_set_unixcred(
|
|||
Extension(kopid): Extension<KOpId>,
|
||||
HxRequest(_hx_request): HxRequest,
|
||||
VerifiedClientInformation(_client_auth_info): VerifiedClientInformation,
|
||||
DomainInfo(domain_info): DomainInfo,
|
||||
jar: CookieJar,
|
||||
opt_form: Option<Form<NewPassword>>,
|
||||
) -> axum::response::Result<Response> {
|
||||
|
@ -733,7 +752,13 @@ pub(crate) async fn view_set_unixcred(
|
|||
Err(OperationError::PasswordQuality(password_feedback)) => {
|
||||
(password_feedback, StatusCode::UNPROCESSABLE_ENTITY)
|
||||
}
|
||||
Err(operr) => return Err(ErrorResponse::from(HtmxError::new(&kopid, operr))),
|
||||
Err(operr) => {
|
||||
return Err(ErrorResponse::from(HtmxError::new(
|
||||
&kopid,
|
||||
operr,
|
||||
domain_info,
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(vec![], StatusCode::UNPROCESSABLE_ENTITY)
|
||||
|
@ -796,7 +821,9 @@ pub(crate) async fn view_reset_get(
|
|||
}
|
||||
return Ok((jar, Redirect::to(Urls::CredReset.as_ref())).into_response());
|
||||
}
|
||||
Err(op_err) => return Ok(HtmxError::new(&kopid, op_err).into_response()),
|
||||
Err(op_err) => {
|
||||
return Ok(HtmxError::new(&kopid, op_err, domain_info.clone()).into_response())
|
||||
}
|
||||
};
|
||||
|
||||
// CU Session cookie is okay
|
||||
|
@ -827,7 +854,7 @@ pub(crate) async fn view_reset_get(
|
|||
.into_response())
|
||||
}
|
||||
Err(op_err) => Err(ErrorResponse::from(
|
||||
HtmxError::new(&kopid, op_err).into_response(),
|
||||
HtmxError::new(&kopid, op_err, domain_info).into_response(),
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -6,12 +6,24 @@
|
|||
(% endblock %)
|
||||
|
||||
(% block body %)
|
||||
<main id="main" class="m-auto align-items-center d-flex flex-column">
|
||||
(% if domain_info.image().is_some() %)
|
||||
<img src="/ui/images/domain"
|
||||
alt="(( domain_info.display_name() ))" class="kanidm_logo" />
|
||||
(% else %)
|
||||
<img
|
||||
src="/pkg/img/logo-square.svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||
alt="(( domain_info.display_name() ))" class="kanidm_logo" />
|
||||
(% endif %)
|
||||
<h3>(( domain_info.display_name() ))</h3>
|
||||
|
||||
|
||||
<h2>Error</h2>
|
||||
<main id="main">
|
||||
<p>An unrecoverable error occurred. Please contact your administrator with the details below.</p>
|
||||
<p>Operation ID: (( operation_id ))</p>
|
||||
<p>Error Code: (( err_code ))</p>
|
||||
<a href=((Urls::Ui))>Return</a>
|
||||
</main>
|
||||
(% endblock %)
|
||||
|
||||
(% endblock %)
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ default = []
|
|||
dhat-heap = ["dep:dhat"]
|
||||
dhat-ad-hoc = ["dep:dhat"]
|
||||
dev-oauth2-device-flow = [] # still-in-development oauth2 device flow support
|
||||
test = [] # Enable this for cross-package test features.
|
||||
|
||||
[dependencies]
|
||||
base64 = { workspace = true }
|
||||
|
|
|
@ -108,6 +108,21 @@ impl DomainInfo {
|
|||
pub fn allow_easter_eggs(&self) -> bool {
|
||||
self.d_allow_easter_eggs
|
||||
}
|
||||
|
||||
#[cfg(feature = "test")]
|
||||
pub fn new_test() -> CowCell<Self> {
|
||||
concread::cowcell::CowCell::new(Self {
|
||||
d_uuid: Uuid::new_v4(),
|
||||
d_name: "test domain".to_string(),
|
||||
d_display: "Test Domain".to_string(),
|
||||
d_vers: 1,
|
||||
d_patch_level: 0,
|
||||
d_devel_taint: false,
|
||||
d_ldap_allow_unix_pw_bind: false,
|
||||
d_allow_easter_eggs: false,
|
||||
d_image: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
|
|
Loading…
Reference in a new issue