diff --git a/server/core/src/https/ui.rs b/server/core/src/https/ui.rs index fd323bf51..9d57466a4 100644 --- a/server/core/src/https/ui.rs +++ b/server/core/src/https/ui.rs @@ -9,12 +9,9 @@ use super::ServerState; use crate::https::extractors::{DomainInfo, DomainInfoRead}; -// when you want to put big text at the top of the page -pub const CSS_PAGE_HEADER: &str = "d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-0 pb-0 mb-3 border-bottom"; - pub const CSS_NAVBAR_NAV: &str = "navbar navbar-expand-md navbar-dark bg-dark mb-4"; -pub const CSS_NAVBAR_BRAND: &str = "navbar-brand navbar-dark"; -pub const CSS_NAVBAR_LINKS_UL: &str = "navbar-nav me-auto mb-2 mb-md-0"; +pub const CSS_NAVBAR_BRAND: &str = "navbar-brand d-flex align-items-center"; +pub const CSS_NAVBAR_LINKS_UL: &str = "navbar-nav"; pub(crate) fn spa_router_user_ui() -> Router { Router::new() diff --git a/server/core/src/https/ui_html.html b/server/core/src/https/ui_html.html index 48d15b0fe..4e872fcda 100644 --- a/server/core/src/https/ui_html.html +++ b/server/core/src/https/ui_html.html @@ -27,7 +27,7 @@ -
+
, Extension(kopid): Extension, VerifiedClientInformation(client_auth_info): VerifiedClientInformation, + DomainInfo(domain_info): DomainInfo, ) -> axum::response::Result { // Because this is the route where the login page can land, we need to actually alter // our response as a result. If the user comes here directly we need to render the full @@ -44,6 +49,7 @@ pub(crate) async fn view_apps_get( ( HxPushUrl(Uri::from_static(Urls::Apps.as_ref())), AppsView { + navbar_ctx: NavbarCtx { domain_info }, apps_partial: AppsPartialView { apps: app_links }, }, ) diff --git a/server/core/src/https/views/mod.rs b/server/core/src/https/views/mod.rs index e1b5a186e..7fb53d472 100644 --- a/server/core/src/https/views/mod.rs +++ b/server/core/src/https/views/mod.rs @@ -18,6 +18,7 @@ mod constants; mod cookies; mod errors; mod login; +mod navbar; mod oauth2; mod profile; mod reset; @@ -94,7 +95,10 @@ pub fn view_router() -> Router { .route("/reset/add_password", post(reset::view_new_pwd)) .route("/reset/change_password", post(reset::view_new_pwd)) .route("/reset/add_passkey", post(reset::view_new_passkey)) + .route("/reset/set_unixcred", post(reset::view_set_unixcred)) .route("/api/delete_alt_creds", post(reset::remove_alt_creds)) + .route("/api/delete_unixcred", post(reset::remove_unixcred)) + .route("/api/add_totp", post(reset::add_totp)) .route("/api/remove_totp", post(reset::remove_totp)) .route("/api/remove_passkey", post(reset::remove_passkey)) .route("/api/finish_passkey", post(reset::finish_passkey)) diff --git a/server/core/src/https/views/navbar.rs b/server/core/src/https/views/navbar.rs new file mode 100644 index 000000000..605ec6e85 --- /dev/null +++ b/server/core/src/https/views/navbar.rs @@ -0,0 +1,5 @@ +use crate::https::extractors::DomainInfoRead; + +pub struct NavbarCtx { + pub domain_info: DomainInfoRead, +} diff --git a/server/core/src/https/views/profile.rs b/server/core/src/https/views/profile.rs index 4ceccd0ff..3953d35eb 100644 --- a/server/core/src/https/views/profile.rs +++ b/server/core/src/https/views/profile.rs @@ -12,10 +12,12 @@ use kanidm_proto::internal::UserAuthToken; use super::constants::{ProfileMenuItems, UiMessage, Urls}; use super::errors::HtmxError; use super::login::{LoginDisplayCtx, Reauth, ReauthPurpose}; +use super::navbar::NavbarCtx; #[derive(Template)] #[template(path = "user_settings.html")] pub(crate) struct ProfileView { + navbar_ctx: NavbarCtx, profile_partial: ProfilePartialView, } @@ -27,13 +29,13 @@ struct ProfilePartialView { account_name: String, display_name: String, email: Option, - posix_enabled: bool, } pub(crate) async fn view_profile_get( State(state): State, Extension(kopid): Extension, VerifiedClientInformation(client_auth_info): VerifiedClientInformation, + DomainInfo(domain_info): DomainInfo, ) -> Result { let uat: UserAuthToken = state .qe_r_ref @@ -45,13 +47,13 @@ pub(crate) async fn view_profile_get( let can_rw = uat.purpose_readwrite_active(time); Ok(ProfileView { + navbar_ctx: NavbarCtx { domain_info }, profile_partial: ProfilePartialView { menu_active_item: ProfileMenuItems::UserProfile, can_rw, account_name: uat.name().to_string(), display_name: uat.displayname.clone(), email: uat.mail_primary.clone(), - posix_enabled: false, }, }) } diff --git a/server/core/src/https/views/reset.rs b/server/core/src/https/views/reset.rs index 3ff7f0f8d..e838a5fef 100644 --- a/server/core/src/https/views/reset.rs +++ b/server/core/src/https/views/reset.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use std::fmt; use std::fmt::{Display, Formatter}; +use std::str::FromStr; use uuid::Uuid; use kanidm_proto::internal::{ @@ -25,6 +26,7 @@ use kanidm_proto::internal::{ }; use super::constants::Urls; +use super::navbar::NavbarCtx; use crate::https::extractors::{DomainInfo, DomainInfoRead, VerifiedClientInformation}; use crate::https::middleware::KOpId; use crate::https::views::constants::ProfileMenuItems; @@ -37,6 +39,7 @@ use super::UnrecoverableErrorView; #[derive(Template)] #[template(path = "user_settings.html")] struct ProfileView { + navbar_ctx: NavbarCtx, profile_partial: CredStatusView, } @@ -62,7 +65,6 @@ struct CredStatusView { menu_active_item: ProfileMenuItems, names: String, credentials_update_partial: CredResetPartialView, - posix_enabled: bool, } #[derive(Template)] @@ -76,6 +78,8 @@ struct CredResetPartialView { attested_passkeys: Vec, passkeys: Vec, primary: Option, + unixcred_state: CUCredState, + unixcred: Option, } #[skip_serializing_none] @@ -91,6 +95,12 @@ struct AddPasswordPartial { check_res: PwdCheckResult, } +#[derive(Template)] +#[template(path = "credential_update_set_unixcred_partial.html")] +struct SetUnixCredPartial { + check_res: PwdCheckResult, +} + #[derive(Serialize, Deserialize, Debug)] enum PwdCheckResult { Success, @@ -111,7 +121,7 @@ pub(crate) struct NewPassword { pub(crate) struct NewTotp { name: String, #[serde(rename = "checkTOTPCode")] - check_totpcode: u32, + check_totpcode: String, #[serde(rename = "ignoreBrokenApp")] ignore_broken_app: bool, } @@ -154,44 +164,28 @@ pub(crate) struct TOTPRemoveData { } #[derive(Serialize, Deserialize, Debug)] -pub(crate) enum TotpCheckResult { - Init { - secret: String, - qr_code_svg: String, - steps: u64, - digits: u8, - algo: TotpAlgo, - uri: String, - }, - Failure { - wrong_code: bool, - broken_app: bool, - warnings: Vec, - }, +pub(crate) struct TotpInit { + secret: String, + qr_code_svg: String, + steps: u64, + digits: u8, + algo: TotpAlgo, + uri: String, } -#[derive(Debug, Deserialize, Serialize)] -pub(crate) enum TotpFeedback { - BlankName, - DuplicateName, -} - -impl Display for TotpFeedback { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - TotpFeedback::BlankName => write!(f, "Please enter a name."), - TotpFeedback::DuplicateName => write!( - f, - "This name already exists, choose another or remove the existing one." - ), - } - } +#[derive(Serialize, Deserialize, Debug, Default)] +pub(crate) struct TotpCheck { + wrong_code: bool, + broken_app: bool, } #[derive(Template)] #[template(path = "credential_update_add_totp_partial.html")] struct AddTotpPartial { - check_res: TotpCheckResult, + totp_init: Option, + totp_name: String, + totp_value: String, + check: TotpCheck, } #[derive(PartialEq, Debug, Serialize, Deserialize)] @@ -285,6 +279,28 @@ pub(crate) async fn remove_alt_creds( Ok(get_cu_partial_response(cu_status)) } +pub(crate) async fn remove_unixcred( + State(state): State, + Extension(kopid): Extension, + HxRequest(_hx_request): HxRequest, + VerifiedClientInformation(_client_auth_info): VerifiedClientInformation, + jar: CookieJar, +) -> axum::response::Result { + let cu_session_token: CUSessionToken = get_cu_session(jar).await?; + + let cu_status = state + .qe_r_ref + .handle_idmcredentialupdate( + cu_session_token, + CURequest::UnixPasswordRemove, + kopid.eventid, + ) + .map_err(|op_err| HtmxError::new(&kopid, op_err)) + .await?; + + Ok(get_cu_partial_response(cu_status)) +} + pub(crate) async fn remove_totp( State(state): State, Extension(kopid): Extension, @@ -428,63 +444,64 @@ pub(crate) async fn view_new_totp( HxRequest(_hx_request): HxRequest, VerifiedClientInformation(_client_auth_info): VerifiedClientInformation, jar: CookieJar, - opt_form: Option>, ) -> axum::response::Result { let cu_session_token = get_cu_session(jar).await?; let push_url = HxPushUrl(Uri::from_static("/ui/reset/add_totp")); - let swapped_handler_trigger = - HxResponseTrigger::after_swap([HxEvent::new("addTotpSwapped".to_string())]); - let new_totp = match opt_form { - // Initial response handling, user is entering the form for first time - None => { - let cu_status = state - .qe_r_ref - .handle_idmcredentialupdate( - cu_session_token, - CURequest::TotpGenerate, - kopid.eventid, - ) - .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))?; + let cu_status = state + .qe_r_ref + .handle_idmcredentialupdate(cu_session_token, CURequest::TotpGenerate, kopid.eventid) + .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))?; - let partial = if let CURegState::TotpCheck(secret) = cu_status.mfaregstate { - let uri = secret.to_uri(); - let svg = match QrCode::new(uri.as_str()) { - Ok(qr) => qr.render::().build(), - Err(qr_err) => { - error!("Failed to create TOTP QR code: {qr_err}"); - "QR Code Generation Failed".to_string() - } - }; + let partial = if let CURegState::TotpCheck(secret) = cu_status.mfaregstate { + let uri = secret.to_uri(); + let svg = match QrCode::new(uri.as_str()) { + Ok(qr) => qr.render::().build(), + Err(qr_err) => { + error!("Failed to create TOTP QR code: {qr_err}"); + "QR Code Generation Failed".to_string() + } + }; - AddTotpPartial { - check_res: TotpCheckResult::Init { - secret: secret.get_secret(), - qr_code_svg: svg, - steps: secret.step, - digits: secret.digits, - algo: secret.algo, - uri, - }, - } - } else { - return Err(ErrorResponse::from(HtmxError::new( - &kopid, - OperationError::CannotStartMFADuringOngoingMFASession, - ))); - }; - - return Ok((swapped_handler_trigger, push_url, partial).into_response()); + AddTotpPartial { + totp_init: Some(TotpInit { + secret: secret.get_secret(), + qr_code_svg: svg, + steps: secret.step, + digits: secret.digits, + algo: secret.algo, + uri, + }), + totp_name: Default::default(), + totp_value: Default::default(), + check: TotpCheck::default(), } - - // User has submitted a totp code - Some(Form(new_totp)) => new_totp, + } else { + return Err(ErrorResponse::from(HtmxError::new( + &kopid, + OperationError::CannotStartMFADuringOngoingMFASession, + ))); }; - let cu_status = if new_totp.ignore_broken_app { + Ok((push_url, partial).into_response()) +} + +pub(crate) async fn add_totp( + State(state): State, + Extension(kopid): Extension, + HxRequest(_hx_request): HxRequest, + VerifiedClientInformation(_client_auth_info): VerifiedClientInformation, + jar: CookieJar, + new_totp_form: Form, +) -> axum::response::Result { + let cu_session_token = get_cu_session(jar).await?; + + let check_totpcode = u32::from_str(&new_totp_form.check_totpcode).unwrap_or_default(); + + let cu_status = if new_totp_form.ignore_broken_app { // Cope with SHA1 apps because the user has intended to do so, their totp code was already verified state.qe_r_ref.handle_idmcredentialupdate( cu_session_token, @@ -495,25 +512,22 @@ pub(crate) async fn view_new_totp( // Validate totp code example state.qe_r_ref.handle_idmcredentialupdate( cu_session_token, - CURequest::TotpVerify(new_totp.check_totpcode, new_totp.name), + CURequest::TotpVerify(check_totpcode, new_totp_form.name.clone()), kopid.eventid, ) } .await .map_err(|op_err| HtmxError::new(&kopid, op_err))?; - let warnings = vec![]; - let check_res = match &cu_status.mfaregstate { + let check = match &cu_status.mfaregstate { CURegState::None => return Ok(get_cu_partial_response(cu_status)), - CURegState::TotpTryAgain => TotpCheckResult::Failure { + CURegState::TotpTryAgain => TotpCheck { wrong_code: true, - broken_app: false, - warnings, + ..Default::default() }, - CURegState::TotpInvalidSha1 => TotpCheckResult::Failure { - wrong_code: false, + CURegState::TotpInvalidSha1 => TotpCheck { broken_app: true, - warnings, + ..Default::default() }, CURegState::TotpCheck(_) | CURegState::BackupCodes(_) @@ -526,10 +540,23 @@ pub(crate) async fn view_new_totp( } }; + let check_totpcode = if check.wrong_code { + String::default() + } else { + new_totp_form.check_totpcode.clone() + }; + + let swapped_handler_trigger = + HxResponseTrigger::after_swap([HxEvent::new("addTotpSwapped".to_string())]); + Ok(( swapped_handler_trigger, - push_url, - AddTotpPartial { check_res }, + AddTotpPartial { + totp_init: None, + totp_name: new_totp_form.name.clone(), + totp_value: check_totpcode, + check, + }, ) .into_response()) } @@ -658,6 +685,66 @@ fn add_cu_cookie( jar.add(token_cookie) } +pub(crate) async fn view_set_unixcred( + State(state): State, + Extension(kopid): Extension, + HxRequest(_hx_request): HxRequest, + VerifiedClientInformation(_client_auth_info): VerifiedClientInformation, + jar: CookieJar, + opt_form: Option>, +) -> axum::response::Result { + let cu_session_token: CUSessionToken = get_cu_session(jar).await?; + let swapped_handler_trigger = + HxResponseTrigger::after_swap([HxEvent::new("addPasswordSwapped".to_string())]); + + let new_passwords = match opt_form { + None => { + return Ok(( + swapped_handler_trigger, + SetUnixCredPartial { + check_res: PwdCheckResult::Init, + }, + ) + .into_response()); + } + Some(Form(new_passwords)) => new_passwords, + }; + + let pwd_equal = new_passwords.new_password == new_passwords.new_password_check; + let (warnings, status) = if pwd_equal { + let res = state + .qe_r_ref + .handle_idmcredentialupdate( + cu_session_token, + CURequest::UnixPassword(new_passwords.new_password), + kopid.eventid, + ) + .await; + match res { + Ok(cu_status) => return Ok(get_cu_partial_response(cu_status)), + Err(OperationError::PasswordQuality(password_feedback)) => { + (password_feedback, StatusCode::UNPROCESSABLE_ENTITY) + } + Err(operr) => return Err(ErrorResponse::from(HtmxError::new(&kopid, operr))), + } + } else { + (vec![], StatusCode::UNPROCESSABLE_ENTITY) + }; + + let check_res = PwdCheckResult::Failure { + pwd_equal, + warnings, + }; + + Ok(( + status, + swapped_handler_trigger, + HxPushUrl(Uri::from_static("/ui/reset/set_unixcred")), + AddPasswordPartial { check_res }, + ) + .into_response()) +} + pub(crate) async fn view_reset_get( State(state): State, Extension(kopid): Extension, @@ -758,6 +845,8 @@ fn get_cu_partial(cu_status: CUStatus) -> CredResetPartialView { passkeys, primary_state, primary, + unixcred_state, + unixcred, .. } = cu_status; @@ -770,6 +859,8 @@ fn get_cu_partial(cu_status: CUStatus) -> CredResetPartialView { passkeys, primary_state, primary, + unixcred_state, + unixcred, } } @@ -799,16 +890,15 @@ fn get_cu_response( if is_logged_in { let cred_status_view = CredStatusView { menu_active_item: ProfileMenuItems::Credentials, - domain_info, + domain_info: domain_info.clone(), names, credentials_update_partial, - // TODO: fill in posix enabled - posix_enabled: false, }; ( HxPushUrl(Uri::from_static(Urls::UpdateCredentials.as_ref())), ProfileView { + navbar_ctx: NavbarCtx { domain_info }, profile_partial: cred_status_view, }, ) diff --git a/server/core/static/img/logo.svg b/server/core/static/img/logo.svg new file mode 100644 index 000000000..ee630f407 --- /dev/null +++ b/server/core/static/img/logo.svg @@ -0,0 +1,1113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PASSPORT + AUTHSTRALIA + KANIDM + + + + + + + + + + + + + diff --git a/server/core/static/modules/cred_update.mjs b/server/core/static/modules/cred_update.mjs index 22605d69e..701cc8ec0 100644 --- a/server/core/static/modules/cred_update.mjs +++ b/server/core/static/modules/cred_update.mjs @@ -69,7 +69,7 @@ function onPasskeyCreated(assertion) { document.getElementById("passkey-create-data").value = JSON.stringify(creationData) // Make the name input visible and hide the "Begin Passkey Enrollment" button - document.getElementById("passkeyNamingSafariBtn").classList.add("d-none") + document.getElementById("passkeyNamingSafariPre").classList.add("d-none") document.getElementById("passkeyNamingForm").classList.remove("d-none") document.getElementById("passkeyNamingSubmitBtn").classList.remove("d-none") } catch (e) { diff --git a/server/core/static/style.css b/server/core/static/style.css index 9e0accf46..30fa44684 100644 --- a/server/core/static/style.css +++ b/server/core/static/style.css @@ -1,124 +1,52 @@ +:root { + --totp-width-and-height: 30px; + --totp-stroke-width: 60px; +} + html, body { height: 100%; } -.input-hidden { - display: none; -} - .form-cred-reset-body { - width: 100%; max-width: 500px; - padding: 15px; - margin: auto; } -#settings-window:has(.form-cred-reset-body) .form-cred-reset-body { +#settings-window .form-cred-reset-body { max-width: unset; - padding: unset; -} - -.form-signin-body { - display: flex; - align-items: center; - padding-top: 40px; - padding-bottom: 40px; - background-color: #f5f5f5; } .form-signin { max-width: 680px; - margin: auto; -} - -.form-signin .checkbox { - font-weight: 400; -} - -.form-signin .form-floating:focus-within { - z-index: 2; -} - -.form-signin input[type="email"] { - margin-bottom: -1px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.form-signin input[type="password"] { - margin-bottom: 10px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -/* DASHBOARD */ - -.dash-body { - font-size: 0.875rem; -} - -.feather { - width: 16px; - height: 16px; - vertical-align: text-bottom; } /* * Sidebar */ -.sidebar { - position: fixed; - top: 0; - /* rtl:raw: - right: 0; - */ - bottom: 0; - /* rtl:remove */ - left: 0; - z-index: 100; /* Behind the navbar */ - padding: 48px 0 0; /* Height of navbar */ - box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1); +.side-menu { + min-width: 180px; } -@media (max-width: 767.98px) { - .sidebar { - top: 5rem; +.side-menu-item { + --icon-size: 24px; + padding: .4rem .7rem; + text-decoration: none; + + &.active { + font-weight: 600; + } + + &:hover, &.active { + background-color: var(--bs-gray-300); } -} -.sidebar-sticky { - position: relative; - top: 0; - height: calc(100vh - 48px); - padding-top: 0.5rem; - overflow-x: hidden; - overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ -} - -.sidebar .nav-link { - font-weight: 500; - color: #333; -} - -.sidebar .nav-link .feather { - margin-right: 4px; - color: #727272; -} - -.sidebar .nav-link.active { - color: #2470dc; -} - -.sidebar .nav-link:hover .feather, -.sidebar .nav-link.active .feather { - color: inherit; -} - -.sidebar-heading { - font-size: 0.75rem; - text-transform: uppercase; + .icon-container img { + filter: invert(40%); + width: 100%; + height: 100%; + object-fit: contain; + } } /* @@ -130,49 +58,6 @@ body { * Navbar */ -.navbar-brand { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 1rem; - padding-right: 2rem; - font-size: 1.25rem; -} - -.navbar .navbar-toggler { - top: 0.25rem; - right: 1rem; -} - -.navbar-toggler-img { - width: 50px; - height: 50px; - padding: 0px; - margin: 0px; -} - -.navbar .form-control { - padding: 0.75rem 1rem; - border-width: 0; - border-radius: 0; -} - -.form-control-dark { - color: #fff; - background-color: rgba(255, 255, 255, 0.1); - border-color: rgba(255, 255, 255, 0.1); -} - -.form-control-dark:focus { - border-color: transparent; - box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.25); -} - -.vert-center { - height: 80%; - display: flex; - align-items: center; -} - .kanidm_logo { width: 12em; height: 12em; @@ -186,11 +71,6 @@ body { margin: auto; } -:root { - --totp-width-and-height: 30px; - --totp-stroke-width: 60px; -} - .totp-display-container { padding: 5px 10px; display: flex; @@ -264,15 +144,25 @@ body { min-height: 150px; } -#graph-container svg { - max-width: 100%; - height: fit-content; +.btn-tiny { + --bs-btn-padding-y: .05rem; + --bs-btn-padding-x: .4rem; + --bs-btn-font-size: .75rem; } #cred-update-commit-bar { display: block; + /* position: fixed; + bottom: .5rem; left: 50%; transform: translateX(-50%); + */ background: white; } + +.icon-container { + padding: 2px; + width: var(--icon-size); + height: var(--icon-size); +} diff --git a/server/core/templates/apps_partial.html b/server/core/templates/apps_partial.html index e79c7676d..4bad4f153 100644 --- a/server/core/templates/apps_partial.html +++ b/server/core/templates/apps_partial.html @@ -1,9 +1,10 @@ -
-
+
+

Applications list

+
(% if apps.is_empty() %) -
No linked applications available
+

No linked applications available

(% else %)
(% for app in apps %) diff --git a/server/core/templates/base_htmx_with_nav.html b/server/core/templates/base_htmx_with_nav.html index fa82b91ee..2acdd7a5b 100644 --- a/server/core/templates/base_htmx_with_nav.html +++ b/server/core/templates/base_htmx_with_nav.html @@ -5,3 +5,4 @@ (% block main %)(% endblock %) (% include "signout_modal.html" %) (% endblock %) + diff --git a/server/core/templates/credential_update_add_passkey_partial.html b/server/core/templates/credential_update_add_passkey_partial.html index c887f28d7..94c45ace5 100644 --- a/server/core/templates/credential_update_add_passkey_partial.html +++ b/server/core/templates/credential_update_add_passkey_partial.html @@ -3,7 +3,12 @@ - +
+ +
+ +
+
Adding a new passkey diff --git a/server/core/templates/credential_update_add_password_partial.html b/server/core/templates/credential_update_add_password_partial.html index 567a4066e..ba166f5e5 100644 --- a/server/core/templates/credential_update_add_password_partial.html +++ b/server/core/templates/credential_update_add_password_partial.html @@ -54,7 +54,7 @@
- + - (% if broken_app %) - - (% else %) - - (% endif %) + +
+ + (% if check.broken_app %) + + (% else %) + + (% endif %) +
diff --git a/server/core/templates/credential_update_set_unixcred_partial.html b/server/core/templates/credential_update_set_unixcred_partial.html new file mode 100644 index 000000000..cea1a66ab --- /dev/null +++ b/server/core/templates/credential_update_set_unixcred_partial.html @@ -0,0 +1,64 @@ +
+
+ + (% let potentially_invalid_input_class = "" %) + (% let potentially_invalid_reinput_class = "" %) + (% let pwd_equal = true %) + + (% if let PwdCheckResult::Failure with { pwd_equal, warnings } = check_res %) + (% let pwd_equal = pwd_equal.clone() %) + (% if !warnings.is_empty() %) + (% let potentially_invalid_input_class = "is-invalid" %) + (% endif %) + (% if pwd_equal %) + (% let potentially_invalid_reinput_class = "is-invalid" %) + (% endif %) + (% endif %) + + + + + (% if let PwdCheckResult::Failure with { pwd_equal, warnings } = check_res %) +
+
    + (% for warn in warnings %) +
  • (( warn ))
  • + (% endfor %) +
+
+ (% endif %) + + + +
+
  • Passwords don't match
+
+
+
+ + +
+
+ diff --git a/server/core/templates/credentials_reset.html b/server/core/templates/credentials_reset.html index 21006c631..5028b9706 100644 --- a/server/core/templates/credentials_reset.html +++ b/server/core/templates/credentials_reset.html @@ -5,14 +5,13 @@ (% endblock %) (% block body %) -
-
-
-

Updating Credentials

-

(( names ))

-

(( domain_info.display_name() ))

-
- (( credentials_update_partial|safe )) -
-
+
+
+

Updating Credentials

+

User: (( names ))

+

Kanidm domain: (( domain_info.display_name() ))

+
+
+ (( credentials_update_partial|safe )) +
(% endblock %) diff --git a/server/core/templates/credentials_reset_form.html b/server/core/templates/credentials_reset_form.html index 4e25e606c..ff119f17a 100644 --- a/server/core/templates/credentials_reset_form.html +++ b/server/core/templates/credentials_reset_form.html @@ -12,7 +12,7 @@ (% endblock %) (% block body %) -
+
-
-
-

User: (( names ))

-

Kanidm domain: (( domain_info.display_name() ))

-
- (( credentials_update_partial|safe )) +
+
+

User: (( names ))

+

Kanidm domain: (( domain_info.display_name() ))

+ (( credentials_update_partial|safe ))
(% endblock %) diff --git a/server/core/templates/credentials_update_attested_passkeys.html b/server/core/templates/credentials_update_attested_passkeys.html index 7e7834d54..5c7024b02 100644 --- a/server/core/templates/credentials_update_attested_passkeys.html +++ b/server/core/templates/credentials_update_attested_passkeys.html @@ -1,16 +1,20 @@

Attested Passkeys

Passkeys originating from a signed authenticator.

-(% for passkey in attested_passkeys %) -
-
(( passkey.tag ))
-
- + Remove + +
-
-(% endfor %) \ No newline at end of file + + (% endfor %) + \ No newline at end of file diff --git a/server/core/templates/credentials_update_partial.html b/server/core/templates/credentials_update_partial.html index 94eb7bafb..e96ac73f5 100644 --- a/server/core/templates/credentials_update_partial.html +++ b/server/core/templates/credentials_update_partial.html @@ -5,24 +5,23 @@ src="/pkg/external/base64.js?v=((crate::https::cache_buster::get_cache_buster_key()))" async> -
(% match ext_cred_portal %) (% when CUExtPortal::None %) (% when CUExtPortal::Hidden %) -

This account is externally managed. Some features may not be available.

- (% when CUExtPortal::Some(url) %)
+ (% when CUExtPortal::Some(url) %)

This account is externally managed. Some features may not be available.

Visit the external account portal +
(% endmatch %) (% if warnings.len() > 0 %) -
(% for warning in warnings %) (% let is_danger = [CURegWarning::WebauthnAttestationUnsatisfiable, CURegWarning::Unsatisfiable].contains(warning) %) @@ -57,6 +56,7 @@ (% endif %)
(% endfor %) +
(% endif %) @@ -81,15 +81,15 @@ (% when CUCredState::Modifiable %) (% include "credentials_update_passkeys.html" %) -
- @@ -112,9 +112,38 @@ (% let primary_state = primary_state %) (% include "credentials_update_primary.html" %) + (% match unixcred_state %) + (% when CUCredState::Modifiable %)
-