feat: self cred update flow (#2995)

This commit is contained in:
Merlijn 2024-08-23 06:05:32 +02:00 committed by GitHub
parent b53c4ba62a
commit 87b20d22d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 66 additions and 10 deletions

View file

@ -36,6 +36,7 @@ pub fn view_router() -> Router<ServerState> {
.route("/", get(|| async { Redirect::permanent("/ui/login") }))
.route("/apps", get(apps::view_apps_get))
.route("/reset", get(reset::view_reset_get))
.route("/update_credentials", get(reset::view_self_reset_get))
.route("/profile", get(profile::view_profile_get))
.route("/profile/unlock", get(profile::view_profile_unlock_get))
.route("/logout", get(login::view_logout_get))

View file

@ -21,7 +21,7 @@ use uuid::Uuid;
use kanidm_proto::internal::{
CUCredState, CUExtPortal, CUIntentToken, CURegState, CURegWarning, CURequest, CUSessionToken,
CUStatus, CredentialDetail, OperationError, PasskeyDetail, PasswordFeedback, TotpAlgo,
COOKIE_CU_SESSION_TOKEN,
UserAuthToken, COOKIE_CU_SESSION_TOKEN,
};
use crate::https::extractors::VerifiedClientInformation;
@ -565,6 +565,65 @@ pub(crate) async fn view_new_pwd(
.into_response())
}
// Allows authenticated users to get a (cred update) or (reauth into cred update) page, depending on whether they have read write access or not respectively.
pub(crate) async fn view_self_reset_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
HxRequest(_hx_request): HxRequest,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
mut jar: CookieJar,
) -> 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 time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0);
let can_rw = uat.purpose_readwrite_active(time);
if can_rw {
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))
.await?;
let domain_display_name = state
.qe_r_ref
.get_domain_display_name(kopid.eventid)
.await
.unwrap_or_default();
let cu_resp = get_cu_response(domain_display_name, cu_status);
jar = add_cu_cookie(jar, &state, cu_session_token);
Ok((jar, cu_resp).into_response())
} else {
super::login::view_reauth_get(
state,
client_auth_info,
kopid,
jar,
"/ui/update_credentials",
)
.await
}
}
// Adds the COOKIE_CU_SESSION_TOKEN to the jar and returns the result
fn add_cu_cookie(
jar: CookieJar,
state: &ServerState,
cu_session_token: CUSessionToken,
) -> CookieJar {
let mut token_cookie = Cookie::new(COOKIE_CU_SESSION_TOKEN, cu_session_token.token);
token_cookie.set_secure(state.secure_cookies);
token_cookie.set_same_site(SameSite::Strict);
token_cookie.set_http_only(true);
jar.add(token_cookie)
}
pub(crate) async fn view_reset_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
@ -622,12 +681,7 @@ pub(crate) async fn view_reset_get(
Ok((cu_session_token, cu_status)) => {
let cu_resp = get_cu_response(domain_display_name, cu_status);
let mut token_cookie = Cookie::new(COOKIE_CU_SESSION_TOKEN, cu_session_token.token);
token_cookie.set_secure(state.secure_cookies);
token_cookie.set_same_site(SameSite::Strict);
token_cookie.set_http_only(true);
jar = jar.add(token_cookie);
jar = add_cu_cookie(jar, &state, cu_session_token);
Ok((jar, cu_resp).into_response())
}
Err(OperationError::SessionExpired) | Err(OperationError::Wait(_)) => {
@ -709,7 +763,7 @@ fn get_cu_response(domain: String, cu_status: CUStatus) -> Response {
async fn get_cu_session(jar: CookieJar) -> Result<CUSessionToken, Response> {
let cookie = jar.get(COOKIE_CU_SESSION_TOKEN);
return if let Some(cookie) = cookie {
if let Some(cookie) = cookie {
let cu_session_token = cookie.value();
let cu_session_token = CUSessionToken {
token: cu_session_token.into(),
@ -717,5 +771,5 @@ async fn get_cu_session(jar: CookieJar) -> Result<CUSessionToken, Response> {
Ok(cu_session_token)
} else {
Err((StatusCode::FORBIDDEN, Redirect::to("/ui/reset")).into_response())
};
}
}

View file

@ -15,7 +15,8 @@
<path d="M9 13a1 1 0 0 1 1-1v-1a2 2 0 1 1 4 0v1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm3-3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1"/>
</svg>UNIX Password</a>
(% endif %)
<a href="/ui/add_new_dev" class="list-group-item list-group-item-action"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-shield-fill-plus me-3" viewBox="0 0 16 16">
<a href="/ui/update_credentials" class="list-group-item list-group-item-action"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-shield-fill-plus me-3" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0c-.69 0-1.843.265-2.928.56-1.11.3-2.229.655-2.887.87a1.54 1.54 0 0 0-1.044 1.262c-.596 4.477.787 7.795 2.465 9.99a11.8 11.8 0 0 0 2.517 2.453c.386.273.744.482 1.048.625.28.132.581.24.829.24s.548-.108.829-.24a7 7 0 0 0 1.048-.625 11.8 11.8 0 0 0 2.517-2.453c1.678-2.195 3.061-5.513 2.465-9.99a1.54 1.54 0 0 0-1.044-1.263 63 63 0 0 0-2.887-.87C9.843.266 8.69 0 8 0m-.5 5a.5.5 0 0 1 1 0v1.5H10a.5.5 0 0 1 0 1H8.5V9a.5.5 0 0 1-1 0V7.5H6a.5.5 0 0 1 0-1h1.5z"/>
</svg>Add another device</a>
</div>