mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
parent
8b4d0d6ead
commit
48cd6638fe
|
@ -23,6 +23,7 @@ use kanidmd_lib::idm::AuthState;
|
||||||
use kanidmd_lib::prelude::OperationError;
|
use kanidmd_lib::prelude::OperationError;
|
||||||
use kanidmd_lib::prelude::*;
|
use kanidmd_lib::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use webauthn_rs::prelude::PublicKeyCredential;
|
use webauthn_rs::prelude::PublicKeyCredential;
|
||||||
|
|
||||||
|
@ -45,10 +46,33 @@ struct SessionContext {
|
||||||
after_auth_loc: Option<String>,
|
after_auth_loc: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ReauthPurpose {
|
||||||
|
ProfileSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ReauthPurpose {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ReauthPurpose::ProfileSettings => write!(f, "Profile and Settings"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Reauth {
|
||||||
|
pub username: String,
|
||||||
|
pub purpose: ReauthPurpose,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoginDisplayCtx {
|
||||||
|
pub domain_info: DomainInfoRead,
|
||||||
|
// We only need this on the first re-auth screen to indicate what we are doing
|
||||||
|
pub reauth: Option<Reauth>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login.html")]
|
#[template(path = "login.html")]
|
||||||
struct LoginView {
|
struct LoginView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
username: String,
|
username: String,
|
||||||
remember_me: bool,
|
remember_me: bool,
|
||||||
}
|
}
|
||||||
|
@ -61,7 +85,7 @@ pub struct Mech<'a> {
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_mech_choose.html")]
|
#[template(path = "login_mech_choose.html")]
|
||||||
struct LoginMechView<'a> {
|
struct LoginMechView<'a> {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
mechs: Vec<Mech<'a>>,
|
mechs: Vec<Mech<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +96,10 @@ enum LoginTotpError {
|
||||||
Syntax,
|
Syntax,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template, Default)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_totp.html")]
|
#[template(path = "login_totp.html")]
|
||||||
struct LoginTotpView {
|
struct LoginTotpView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
totp: String,
|
totp: String,
|
||||||
errors: LoginTotpError,
|
errors: LoginTotpError,
|
||||||
}
|
}
|
||||||
|
@ -83,30 +107,30 @@ struct LoginTotpView {
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_password.html")]
|
#[template(path = "login_password.html")]
|
||||||
struct LoginPasswordView {
|
struct LoginPasswordView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_backupcode.html")]
|
#[template(path = "login_backupcode.html")]
|
||||||
struct LoginBackupCodeView {
|
struct LoginBackupCodeView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_webauthn.html")]
|
#[template(path = "login_webauthn.html")]
|
||||||
struct LoginWebauthnView {
|
struct LoginWebauthnView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
// Control if we are rendering in security key or passkey mode.
|
// Control if we are rendering in security key or passkey mode.
|
||||||
passkey: bool,
|
passkey: bool,
|
||||||
// chal: RequestChallengeResponse,
|
// chal: RequestChallengeResponse,
|
||||||
chal: String,
|
chal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template, Default)]
|
#[derive(Template)]
|
||||||
#[template(path = "login_denied.html")]
|
#[template(path = "login_denied.html")]
|
||||||
struct LoginDeniedView {
|
struct LoginDeniedView {
|
||||||
domain_custom_image: bool,
|
display_ctx: LoginDisplayCtx,
|
||||||
reason: String,
|
reason: String,
|
||||||
operation_id: Uuid,
|
operation_id: Uuid,
|
||||||
}
|
}
|
||||||
|
@ -142,7 +166,7 @@ pub async fn view_reauth_get(
|
||||||
kopid: KOpId,
|
kopid: KOpId,
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
return_location: &str,
|
return_location: &str,
|
||||||
domain_info: DomainInfoRead,
|
display_ctx: LoginDisplayCtx,
|
||||||
) -> axum::response::Result<Response> {
|
) -> axum::response::Result<Response> {
|
||||||
let session_valid_result = state
|
let session_valid_result = state
|
||||||
.qe_r_ref
|
.qe_r_ref
|
||||||
|
@ -179,7 +203,7 @@ pub async fn view_reauth_get(
|
||||||
ar,
|
ar,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
session_context,
|
session_context,
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -209,10 +233,8 @@ pub async fn view_reauth_get(
|
||||||
|
|
||||||
let remember_me = !username.is_empty();
|
let remember_me = !username.is_empty();
|
||||||
|
|
||||||
let domain_custom_image = domain_info.image().is_some();
|
|
||||||
|
|
||||||
HtmlTemplate(LoginView {
|
HtmlTemplate(LoginView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
username,
|
username,
|
||||||
remember_me,
|
remember_me,
|
||||||
})
|
})
|
||||||
|
@ -255,10 +277,13 @@ pub async fn view_index_get(
|
||||||
|
|
||||||
let remember_me = !username.is_empty();
|
let remember_me = !username.is_empty();
|
||||||
|
|
||||||
let domain_custom_image = domain_info.image().is_some();
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: None,
|
||||||
|
};
|
||||||
|
|
||||||
HtmlTemplate(LoginView {
|
HtmlTemplate(LoginView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
username,
|
username,
|
||||||
remember_me,
|
remember_me,
|
||||||
})
|
})
|
||||||
|
@ -328,6 +353,11 @@ pub async fn view_login_begin_post(
|
||||||
after_auth_loc: None,
|
after_auth_loc: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: None,
|
||||||
|
};
|
||||||
|
|
||||||
// Now process the response if ok.
|
// Now process the response if ok.
|
||||||
match inter {
|
match inter {
|
||||||
Ok(ar) => {
|
Ok(ar) => {
|
||||||
|
@ -338,7 +368,7 @@ pub async fn view_login_begin_post(
|
||||||
ar,
|
ar,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
session_context,
|
session_context,
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -393,6 +423,11 @@ pub async fn view_login_mech_choose_post(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: None,
|
||||||
|
};
|
||||||
|
|
||||||
// Now process the response if ok.
|
// Now process the response if ok.
|
||||||
match inter {
|
match inter {
|
||||||
Ok(ar) => {
|
Ok(ar) => {
|
||||||
|
@ -403,7 +438,7 @@ pub async fn view_login_mech_choose_post(
|
||||||
ar,
|
ar,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
session_context,
|
session_context,
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -440,10 +475,14 @@ pub async fn view_login_totp_post(
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// trim leading and trailing white space.
|
// trim leading and trailing white space.
|
||||||
let Ok(totp) = u32::from_str(login_totp_form.totp.trim()) else {
|
let Ok(totp) = u32::from_str(login_totp_form.totp.trim()) else {
|
||||||
let domain_custom_image = domain_info.image().is_some();
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: None,
|
||||||
|
};
|
||||||
|
|
||||||
// If not an int, we need to re-render with an error
|
// If not an int, we need to re-render with an error
|
||||||
return HtmlTemplate(LoginTotpView {
|
return HtmlTemplate(LoginTotpView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
totp: String::default(),
|
totp: String::default(),
|
||||||
errors: LoginTotpError::Syntax,
|
errors: LoginTotpError::Syntax,
|
||||||
})
|
})
|
||||||
|
@ -540,6 +579,11 @@ async fn credential_step(
|
||||||
cookies::get_signed::<SessionContext>(&state, &jar, COOKIE_AUTH_SESSION_ID)
|
cookies::get_signed::<SessionContext>(&state, &jar, COOKIE_AUTH_SESSION_ID)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: None,
|
||||||
|
};
|
||||||
|
|
||||||
let inter = state // This may change in the future ...
|
let inter = state // This may change in the future ...
|
||||||
.qe_r_ref
|
.qe_r_ref
|
||||||
.handle_auth(
|
.handle_auth(
|
||||||
|
@ -562,7 +606,7 @@ async fn credential_step(
|
||||||
ar,
|
ar,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
session_context,
|
session_context,
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -591,7 +635,7 @@ async fn view_login_step(
|
||||||
auth_result: AuthResult,
|
auth_result: AuthResult,
|
||||||
client_auth_info: ClientAuthInfo,
|
client_auth_info: ClientAuthInfo,
|
||||||
mut session_context: SessionContext,
|
mut session_context: SessionContext,
|
||||||
domain_info: DomainInfoRead,
|
display_ctx: LoginDisplayCtx,
|
||||||
) -> Result<Response, OperationError> {
|
) -> Result<Response, OperationError> {
|
||||||
trace!(?auth_result);
|
trace!(?auth_result);
|
||||||
|
|
||||||
|
@ -601,8 +645,7 @@ async fn view_login_step(
|
||||||
} = auth_result;
|
} = auth_result;
|
||||||
session_context.id = Some(sessionid);
|
session_context.id = Some(sessionid);
|
||||||
|
|
||||||
let domain_custom_image = domain_info.image().is_some();
|
// This lets us break out the loop incase of a fault. Take that halting problem!
|
||||||
|
|
||||||
let mut safety = 3;
|
let mut safety = 3;
|
||||||
|
|
||||||
// Unlike the api version, only set the cookie.
|
// Unlike the api version, only set the cookie.
|
||||||
|
@ -662,11 +705,7 @@ async fn view_login_step(
|
||||||
name: m,
|
name: m,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
HtmlTemplate(LoginMechView {
|
HtmlTemplate(LoginMechView { display_ctx, mechs }).into_response()
|
||||||
domain_custom_image,
|
|
||||||
mechs,
|
|
||||||
})
|
|
||||||
.into_response()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// break acts as return in a loop.
|
// break acts as return in a loop.
|
||||||
|
@ -693,24 +732,24 @@ async fn view_login_step(
|
||||||
|
|
||||||
match auth_allowed {
|
match auth_allowed {
|
||||||
AuthAllowed::Totp => HtmlTemplate(LoginTotpView {
|
AuthAllowed::Totp => HtmlTemplate(LoginTotpView {
|
||||||
|
display_ctx,
|
||||||
totp: session_context.totp.clone().unwrap_or_default(),
|
totp: session_context.totp.clone().unwrap_or_default(),
|
||||||
..Default::default()
|
errors: LoginTotpError::default(),
|
||||||
})
|
})
|
||||||
.into_response(),
|
.into_response(),
|
||||||
AuthAllowed::Password => HtmlTemplate(LoginPasswordView {
|
AuthAllowed::Password => HtmlTemplate(LoginPasswordView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
password: session_context.password.clone().unwrap_or_default(),
|
password: session_context.password.clone().unwrap_or_default(),
|
||||||
})
|
})
|
||||||
.into_response(),
|
.into_response(),
|
||||||
AuthAllowed::BackupCode => HtmlTemplate(LoginBackupCodeView {
|
AuthAllowed::BackupCode => {
|
||||||
domain_custom_image,
|
HtmlTemplate(LoginBackupCodeView { display_ctx }).into_response()
|
||||||
})
|
}
|
||||||
.into_response(),
|
|
||||||
AuthAllowed::SecurityKey(chal) => {
|
AuthAllowed::SecurityKey(chal) => {
|
||||||
let chal_json = serde_json::to_string(&chal)
|
let chal_json = serde_json::to_string(&chal)
|
||||||
.map_err(|_| OperationError::SerdeJsonError)?;
|
.map_err(|_| OperationError::SerdeJsonError)?;
|
||||||
HtmlTemplate(LoginWebauthnView {
|
HtmlTemplate(LoginWebauthnView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
passkey: false,
|
passkey: false,
|
||||||
chal: chal_json,
|
chal: chal_json,
|
||||||
})
|
})
|
||||||
|
@ -720,7 +759,7 @@ async fn view_login_step(
|
||||||
let chal_json = serde_json::to_string(&chal)
|
let chal_json = serde_json::to_string(&chal)
|
||||||
.map_err(|_| OperationError::SerdeJsonError)?;
|
.map_err(|_| OperationError::SerdeJsonError)?;
|
||||||
HtmlTemplate(LoginWebauthnView {
|
HtmlTemplate(LoginWebauthnView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
passkey: true,
|
passkey: true,
|
||||||
chal: chal_json,
|
chal: chal_json,
|
||||||
})
|
})
|
||||||
|
@ -798,7 +837,7 @@ async fn view_login_step(
|
||||||
jar = jar.remove(Cookie::from(COOKIE_AUTH_SESSION_ID));
|
jar = jar.remove(Cookie::from(COOKIE_AUTH_SESSION_ID));
|
||||||
|
|
||||||
break HtmlTemplate(LoginDeniedView {
|
break HtmlTemplate(LoginDeniedView {
|
||||||
domain_custom_image,
|
display_ctx,
|
||||||
reason,
|
reason,
|
||||||
operation_id: kopid.eventid,
|
operation_id: kopid.eventid,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::https::extractors::{DomainInfo, VerifiedClientInformation};
|
use crate::https::extractors::{DomainInfo, VerifiedClientInformation};
|
||||||
use crate::https::middleware::KOpId;
|
use crate::https::middleware::KOpId;
|
||||||
use crate::https::views::errors::HtmxError;
|
use crate::https::views::errors::HtmxError;
|
||||||
|
use crate::https::views::login::{LoginDisplayCtx, Reauth, ReauthPurpose};
|
||||||
use crate::https::views::HtmlTemplate;
|
use crate::https::views::HtmlTemplate;
|
||||||
use crate::https::ServerState;
|
use crate::https::ServerState;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
@ -81,13 +82,27 @@ pub(crate) async fn view_profile_unlock_get(
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
) -> axum::response::Result<Response> {
|
) -> axum::response::Result<Response> {
|
||||||
|
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))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: Some(Reauth {
|
||||||
|
username: uat.spn,
|
||||||
|
purpose: ReauthPurpose::ProfileSettings,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
super::login::view_reauth_get(
|
super::login::view_reauth_get(
|
||||||
state,
|
state,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
kopid,
|
kopid,
|
||||||
jar,
|
jar,
|
||||||
"/ui/profile",
|
"/ui/profile",
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::https::extractors::{DomainInfo, DomainInfoRead, VerifiedClientInforma
|
||||||
use crate::https::middleware::KOpId;
|
use crate::https::middleware::KOpId;
|
||||||
use crate::https::views::constants::ProfileMenuItems;
|
use crate::https::views::constants::ProfileMenuItems;
|
||||||
use crate::https::views::errors::HtmxError;
|
use crate::https::views::errors::HtmxError;
|
||||||
|
use crate::https::views::login::{LoginDisplayCtx, Reauth, ReauthPurpose};
|
||||||
use crate::https::ServerState;
|
use crate::https::ServerState;
|
||||||
|
|
||||||
use super::{HtmlTemplate, UnrecoverableErrorView};
|
use super::{HtmlTemplate, UnrecoverableErrorView};
|
||||||
|
@ -612,13 +613,21 @@ pub(crate) async fn view_self_reset_get(
|
||||||
jar = add_cu_cookie(jar, &state, cu_session_token);
|
jar = add_cu_cookie(jar, &state, cu_session_token);
|
||||||
Ok((jar, cu_resp).into_response())
|
Ok((jar, cu_resp).into_response())
|
||||||
} else {
|
} else {
|
||||||
|
let display_ctx = LoginDisplayCtx {
|
||||||
|
domain_info,
|
||||||
|
reauth: Some(Reauth {
|
||||||
|
username: uat.spn,
|
||||||
|
purpose: ReauthPurpose::ProfileSettings,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
super::login::view_reauth_get(
|
super::login::view_reauth_get(
|
||||||
state,
|
state,
|
||||||
client_auth_info,
|
client_auth_info,
|
||||||
kopid,
|
kopid,
|
||||||
jar,
|
jar,
|
||||||
"/ui/update_credentials",
|
"/ui/update_credentials",
|
||||||
domain_info,
|
display_ctx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,7 +271,6 @@ body {
|
||||||
#cred-update-commit-bar {
|
#cred-update-commit-bar {
|
||||||
display: block;
|
display: block;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 5%;
|
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
background: white;
|
background: white;
|
||||||
|
|
|
@ -112,10 +112,12 @@
|
||||||
(% let primary_state = primary_state %)
|
(% let primary_state = primary_state %)
|
||||||
(% include "credentials_update_primary.html" %)
|
(% include "credentials_update_primary.html" %)
|
||||||
|
|
||||||
|
<hr class="my-4" />
|
||||||
<div id="cred-update-commit-bar" class="toast" role="alert"
|
<div id="cred-update-commit-bar" class="toast" role="alert"
|
||||||
aria-live="assertive" aria-atomic="true">
|
aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
<span class="d-flex align-items-center">
|
<span class="d-flex align-items-center">
|
||||||
|
<div>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16"
|
<svg xmlns="http://www.w3.org/2000/svg" width="16"
|
||||||
height="16" fill="currentColor"
|
height="16" fill="currentColor"
|
||||||
class="bi bi-floppy2-fill" viewBox="0 0 16 16">
|
class="bi bi-floppy2-fill" viewBox="0 0 16 16">
|
||||||
|
@ -123,15 +125,15 @@
|
||||||
<path
|
<path
|
||||||
d="M1.5 0A1.5 1.5 0 0 0 0 1.5v13A1.5 1.5 0 0 0 1.5 16h13a1.5 1.5 0 0 0 1.5-1.5V2.914a1.5 1.5 0 0 0-.44-1.06L14.147.439A1.5 1.5 0 0 0 13.086 0zM4 6a1 1 0 0 1-1-1V1h10v4a1 1 0 0 1-1 1zM3 9h10a1 1 0 0 1 1 1v5H2v-5a1 1 0 0 1 1-1" />
|
d="M1.5 0A1.5 1.5 0 0 0 0 1.5v13A1.5 1.5 0 0 0 1.5 16h13a1.5 1.5 0 0 0 1.5-1.5V2.914a1.5 1.5 0 0 0-.44-1.06L14.147.439A1.5 1.5 0 0 0 13.086 0zM4 6a1 1 0 0 1-1-1V1h10v4a1 1 0 0 1-1 1zM3 9h10a1 1 0 0 1 1 1v5H2v-5a1 1 0 0 1 1-1" />
|
||||||
</svg>
|
</svg>
|
||||||
<b class="px-1">Careful</b>- save when you're done:
|
<b>Careful</b> - Unsaved changes will be lost</div>
|
||||||
</span>
|
</span>
|
||||||
<div class="mt-2 pt-2 border-top">
|
<div class="mt-2 pt-2 border-top">
|
||||||
<button class="btn btn-danger"
|
<button class="btn btn-danger"
|
||||||
hx-post="/ui/api/cu_cancel"
|
hx-post="/ui/api/cu_cancel"
|
||||||
hx-target="body">Cancel</button>
|
hx-target="body">Discard Changes</button>
|
||||||
<span class="d-inline-block" tabindex="0"
|
<span class="d-inline-block" tabindex="0"
|
||||||
data-bs-toggle="tooltip"
|
data-bs-toggle="tooltip"
|
||||||
data-bs-title="Resolve the warnings at the top.">
|
data-bs-title="Unresolved warnings">
|
||||||
<button
|
<button
|
||||||
class="btn btn-success"
|
class="btn btn-success"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
(% block body %)
|
(% block body %)
|
||||||
<main id="main" class="flex-shrink-0 form-signin">
|
<main id="main" class="flex-shrink-0 form-signin">
|
||||||
<center>
|
<center>
|
||||||
(% if domain_custom_image %)
|
(% if display_ctx.domain_info.image().is_some() %)
|
||||||
<img src="/ui/images/domain"
|
<img src="/ui/images/domain"
|
||||||
alt="Kanidm" class="kanidm_logo" />
|
alt="Kanidm" class="kanidm_logo" />
|
||||||
(% else %)
|
(% else %)
|
||||||
|
@ -17,6 +17,9 @@
|
||||||
alt="Kanidm" class="kanidm_logo" />
|
alt="Kanidm" class="kanidm_logo" />
|
||||||
(% endif %)
|
(% endif %)
|
||||||
<h3>Kanidm</h3>
|
<h3>Kanidm</h3>
|
||||||
|
(% if let Some(reauth) = display_ctx.reauth %)
|
||||||
|
<h5>Reauthenticating as (( reauth.username )) to access (( reauth.purpose ))</h5>
|
||||||
|
(% endif %)
|
||||||
</center>
|
</center>
|
||||||
<div id="login-form-container" class="container">
|
<div id="login-form-container" class="container">
|
||||||
(% block logincontainer %)
|
(% block logincontainer %)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
src="/pkg/pkhtml.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
src="/pkg/pkhtml.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
defer></script>
|
defer></script>
|
||||||
|
|
||||||
|
<div class="identity-verification-container">
|
||||||
(% if passkey %)
|
(% if passkey %)
|
||||||
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
||||||
<input hidden="hidden" name="cred" id="cred">
|
<input hidden="hidden" name="cred" id="cred">
|
||||||
|
@ -27,5 +28,6 @@
|
||||||
>Use Security Key</button>
|
>Use Security Key</button>
|
||||||
</form>
|
</form>
|
||||||
(% endif %)
|
(% endif %)
|
||||||
|
</div>
|
||||||
|
|
||||||
(% endblock %)
|
(% endblock %)
|
||||||
|
|
Loading…
Reference in a new issue