mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
feat: add unix passwod reset to security web ui (#1014)
* feat: add unix passwod reset to security web ui * refactor: fetch profile info in ViewsApp prevents constant re-fetching of the profile page and allows every view to access the current_user property * refactor: move unix password change to component * docs: add @theSuess to contributors * fix: further specify kind of password updated * refactor: perform validity check before submit * chore: regenerate vendored wasm package
This commit is contained in:
parent
b793ec6e79
commit
8416069c61
|
@ -18,6 +18,7 @@
|
|||
* Kellin (kellinm)
|
||||
* Carla Schroder (cjschroder)
|
||||
* Thomas Sanchez (daedric)
|
||||
* Dominik Süß (theSuess)
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ impl fmt::Display for AuthType {
|
|||
///
|
||||
/// It's likely that this must have a relationship to the server's user structure
|
||||
/// and to the Entry so that filters or access controls can be applied.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct UserAuthToken {
|
||||
pub session_id: Uuid,
|
||||
|
@ -1023,7 +1023,7 @@ impl WhoamiRequest {
|
|||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct WhoamiResponse {
|
||||
// Should we just embed the entry? Or destructure it?
|
||||
pub youare: Entry,
|
||||
|
|
|
@ -50,9 +50,11 @@ features = [
|
|||
"Element",
|
||||
"Event",
|
||||
"FocusEvent",
|
||||
"FormData",
|
||||
"Headers",
|
||||
"HtmlButtonElement",
|
||||
"HtmlDocument",
|
||||
"HtmlFormElement",
|
||||
"Navigator",
|
||||
"PublicKeyCredential",
|
||||
"PublicKeyCredentialCreationOptions",
|
||||
|
|
|
@ -224,7 +224,7 @@ function addBorrowedObject(obj) {
|
|||
}
|
||||
function __wbg_adapter_30(arg0, arg1, arg2) {
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h059700c1260af691(arg0, arg1, addBorrowedObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hea78f67aa49cdca6(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
|
@ -252,11 +252,11 @@ function makeClosure(arg0, arg1, dtor, f) {
|
|||
return real;
|
||||
}
|
||||
function __wbg_adapter_33(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hba0e2d91e3f361c8(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5d377d592c5210bc(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wbg_adapter_36(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hd75fa9c9bfc3f75e(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5f33dcf69dc6b3de(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -380,17 +380,13 @@ function getImports() {
|
|||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_json_parse = function(arg0, arg1) {
|
||||
const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_new = function(arg0) {
|
||||
const ret = arg0;
|
||||
return addHeapObject(ret);
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
|
@ -398,6 +394,10 @@ function getImports() {
|
|||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_new = function(arg0) {
|
||||
const ret = arg0;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_693216e109162396 = function() {
|
||||
const ret = new Error();
|
||||
return addHeapObject(ret);
|
||||
|
@ -416,17 +416,17 @@ function getImports() {
|
|||
wasm.__wbindgen_free(arg0, arg1);
|
||||
}
|
||||
};
|
||||
imports.wbg.__wbg_debug_5a27eb2cb0d074ba = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_debug_f058a17401150b46 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.debug(...v0);
|
||||
};
|
||||
imports.wbg.__wbg_error_1a35d3879f286b52 = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_error_726977b4dd084e24 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.error(...v0);
|
||||
};
|
||||
imports.wbg.__wbg_warn_2aa0e7178e1d35f6 = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_warn_921059440157e870 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.warn(...v0);
|
||||
|
@ -487,6 +487,30 @@ function getImports() {
|
|||
const ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_value_eb32f706ae6bfab2 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).value;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_3dd349be116107ce = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_get_aab8f8a9b87125ad = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_b5c36262f65fae92 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlFormElement_cf35f564c31c7e40 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof HTMLFormElement;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_getItem_1db55b1eb4116c1e = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -500,66 +524,6 @@ function getImports() {
|
|||
imports.wbg.__wbg_setItem_1f474989a35f4c9f = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_aab8f8a9b87125ad = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_b5c36262f65fae92 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_8ed2fc02f98aeaaf = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).pathname;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_new_d1d1300265e34170 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_value_eb32f706ae6bfab2 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).value;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_3dd349be116107ce = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_headers_0aeca08d4e61e2e7 = function(arg0) {
|
||||
const ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithstrandinit_de7c409ec8538105 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_add_a1fa1336c6b306df = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_remove_dce5eca3c9fcea70 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_create_2c518207ce8a6157 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).create(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_5ec74cdfbbefe775 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_cae04ee9562fc683 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).href;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlInputElement_3fad42774bc62388 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof HTMLInputElement;
|
||||
return ret;
|
||||
|
@ -577,6 +541,46 @@ function getImports() {
|
|||
imports.wbg.__wbg_setvalue_7b7950dacc5eb607 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_add_a1fa1336c6b306df = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_remove_dce5eca3c9fcea70 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_8ed2fc02f98aeaaf = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).pathname;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_new_d1d1300265e34170 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_create_2c518207ce8a6157 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).create(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_5ec74cdfbbefe775 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_cae04ee9562fc683 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).href;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_headers_0aeca08d4e61e2e7 = function(arg0) {
|
||||
const ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithstrandinit_de7c409ec8538105 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_Element_1714e50f9bda1d15 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof Element;
|
||||
return ret;
|
||||
|
@ -608,6 +612,14 @@ function getImports() {
|
|||
imports.wbg.__wbg_focus_66bb7c767837cd51 = function() { return handleError(function (arg0) {
|
||||
getObject(arg0).focus();
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_credentials_c3a25c2c25bfa304 = function(arg0) {
|
||||
const ret = getObject(arg0).credentials;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getClientExtensionResults_93d06fddc73f65f9 = function(arg0) {
|
||||
const ret = getObject(arg0).getClientExtensionResults();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Event_8c74064684d03e14 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof Event;
|
||||
return ret;
|
||||
|
@ -623,12 +635,12 @@ function getImports() {
|
|||
imports.wbg.__wbg_preventDefault_b4d36c8196fbe491 = function(arg0) {
|
||||
getObject(arg0).preventDefault();
|
||||
};
|
||||
imports.wbg.__wbg_credentials_c3a25c2c25bfa304 = function(arg0) {
|
||||
const ret = getObject(arg0).credentials;
|
||||
imports.wbg.__wbg_newwithform_e0bf0bf59a04cc42 = function() { return handleError(function (arg0) {
|
||||
const ret = new FormData(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getClientExtensionResults_93d06fddc73f65f9 = function(arg0) {
|
||||
const ret = getObject(arg0).getClientExtensionResults();
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_7d2187aabf8b90b6 = function(arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_ec92ea1297eefdfc = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
|
@ -813,16 +825,16 @@ function getImports() {
|
|||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper4903 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1268, __wbg_adapter_30);
|
||||
imports.wbg.__wbindgen_closure_wrapper4401 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1059, __wbg_adapter_30);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper5008 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1301, __wbg_adapter_33);
|
||||
imports.wbg.__wbindgen_closure_wrapper4577 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1084, __wbg_adapter_33);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper5273 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1350, __wbg_adapter_36);
|
||||
imports.wbg.__wbindgen_closure_wrapper4718 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1120, __wbg_adapter_36);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
|
Binary file not shown.
302
kanidmd_web_ui/src/components/change_unix_password.rs
Normal file
302
kanidmd_web_ui/src/components/change_unix_password.rs
Normal file
|
@ -0,0 +1,302 @@
|
|||
use compact_jwt::{Jws, JwsUnverified};
|
||||
use kanidm_proto::v1::{SingleStringRequest, UserAuthToken};
|
||||
use std::str::FromStr;
|
||||
use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{FormData, HtmlFormElement};
|
||||
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum PwCheck {
|
||||
Init,
|
||||
Valid,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
pub struct ChangeUnixPassword {
|
||||
state: State,
|
||||
pw_check: PwCheck,
|
||||
pw_val: String,
|
||||
pw_check_val: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct FormValues {
|
||||
password_input: String,
|
||||
password_repeat_input: String,
|
||||
}
|
||||
|
||||
impl From<FormData> for FormValues {
|
||||
fn from(data: FormData) -> Self {
|
||||
Self {
|
||||
password_input: data.get("password_input").as_string().unwrap(),
|
||||
password_repeat_input: data.get("password_repeat_input").as_string().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
Submit(FormData),
|
||||
Error { emsg: String, kopid: Option<String> },
|
||||
Success,
|
||||
PasswordCheck,
|
||||
}
|
||||
|
||||
impl From<FetchError> for Msg {
|
||||
fn from(fe: FetchError) -> Self {
|
||||
Msg::Error {
|
||||
emsg: fe.as_string(),
|
||||
kopid: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum State {
|
||||
Init,
|
||||
Error { emsg: String, kopid: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Properties)]
|
||||
pub struct ChangeUnixPasswordProps {
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
impl Component for ChangeUnixPassword {
|
||||
type Message = Msg;
|
||||
type Properties = ChangeUnixPasswordProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
state: State::Init,
|
||||
pw_check: PwCheck::Init,
|
||||
pw_val: "".to_string(),
|
||||
pw_check_val: "".to_string(),
|
||||
}
|
||||
}
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::Submit(data) => {
|
||||
let fd: FormValues = data.into();
|
||||
if fd.password_input != fd.password_repeat_input {
|
||||
return self.update(
|
||||
ctx,
|
||||
Msg::Error {
|
||||
emsg: "Password fields did not match".to_string(),
|
||||
kopid: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
let tk = ctx.props().token.clone();
|
||||
ctx.link().send_future(async {
|
||||
match Self::update_unix_password(tk, fd.password_input).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
Msg::Error { emsg, kopid } => {
|
||||
self.reset();
|
||||
self.state = State::Error { emsg, kopid };
|
||||
self.pw_check = PwCheck::Init;
|
||||
true
|
||||
}
|
||||
Msg::Success => {
|
||||
self.reset();
|
||||
utils::modal_hide_by_id(crate::constants::ID_UNIX_PASSWORDCHANGE);
|
||||
self.state = State::Init;
|
||||
true
|
||||
}
|
||||
Msg::PasswordCheck => {
|
||||
let pw = utils::get_value_from_element_id("password_input")
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
let check = utils::get_value_from_element_id("password_repeat_input")
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
|
||||
if pw == check {
|
||||
self.pw_check = PwCheck::Valid
|
||||
} else {
|
||||
self.pw_check = PwCheck::Invalid
|
||||
}
|
||||
self.pw_val = pw;
|
||||
self.pw_check_val = check;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let flash = match &self.state {
|
||||
State::Error { emsg, kopid } => {
|
||||
let message = match kopid {
|
||||
Some(k) => format!("An error occured - {} - {}", emsg, k),
|
||||
None => format!("An error occured - {} - No Operation ID", emsg),
|
||||
};
|
||||
html! {
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
{ message }
|
||||
<button type="button" class="btn btn-close" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
_ => html! { <></> },
|
||||
};
|
||||
|
||||
let submit_enabled = self.pw_check == PwCheck::Valid;
|
||||
|
||||
let pw_val = self.pw_val.clone();
|
||||
let pw_check_val = self.pw_check_val.clone();
|
||||
let pw_check_class = match &self.pw_check {
|
||||
PwCheck::Init | PwCheck::Valid => classes!("form-control"),
|
||||
PwCheck::Invalid => classes!("form-control", "is-invalid"),
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<button type="button" class="btn btn-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={format!("#{}", crate::constants::ID_UNIX_PASSWORDCHANGE)}
|
||||
>
|
||||
{ "Update your Unix Password" }
|
||||
</button>
|
||||
<div class="modal" tabindex="-1" role="dialog" id={crate::constants::ID_UNIX_PASSWORDCHANGE}>
|
||||
<div class="modal-dialog" role="document">
|
||||
<form
|
||||
onsubmit={
|
||||
ctx.link().callback(|e: FocusEvent| {
|
||||
e.prevent_default();
|
||||
let form = e.target().and_then(|t| t.dyn_into::<HtmlFormElement>().ok()).unwrap();
|
||||
Msg::Submit(FormData::new_with_form(&form).unwrap())
|
||||
})
|
||||
}
|
||||
>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{"Update your unix password"}</h5>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<p> { "This password is used when logging into a unix-like system as well as applications utilizing LDAP" } </p>
|
||||
{ flash }
|
||||
<div class="form-group">
|
||||
<label for="password_input"> {"New Password" }</label>
|
||||
<input
|
||||
autofocus=true
|
||||
class="autofocus form-control"
|
||||
name="password_input"
|
||||
id="password_input"
|
||||
type="password"
|
||||
value={ pw_val }
|
||||
oninput={
|
||||
ctx.link()
|
||||
.callback(move |_| {
|
||||
Msg::PasswordCheck
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password_repeat_input"> {"Repeat Password" }</label>
|
||||
<input
|
||||
class={ pw_check_class }
|
||||
name="password_repeat_input"
|
||||
id="password_repeat_input"
|
||||
type="password"
|
||||
value={ pw_check_val }
|
||||
oninput={
|
||||
ctx.link()
|
||||
.callback(move |_| {
|
||||
Msg::PasswordCheck
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div class="invalid-feedback">
|
||||
{ "Passwords do not match." }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-success" disabled={ !submit_enabled }>{ "Update Password" }</button>
|
||||
<button type="button" class="btn btn-secondary"
|
||||
onclick={
|
||||
ctx.link().callback(|_e| {
|
||||
Msg::Success
|
||||
})
|
||||
}
|
||||
>{"Cancel"}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
||||
}
|
||||
|
||||
impl ChangeUnixPassword {
|
||||
async fn update_unix_password(token: String, new_password: String) -> Result<Msg, FetchError> {
|
||||
let jwtu = JwsUnverified::from_str(&token).expect_throw("Invalid UAT, unable to parse");
|
||||
|
||||
let uat: Jws<UserAuthToken> = jwtu
|
||||
.unsafe_release_without_verification()
|
||||
.expect_throw("Unvalid UAT, unable to release ");
|
||||
|
||||
let id = uat.inner.uuid.to_string();
|
||||
let changereq_jsvalue = serde_json::to_string(&SingleStringRequest {
|
||||
value: new_password,
|
||||
})
|
||||
.map(|s| JsValue::from(&s))
|
||||
.expect_throw("Failed to change request");
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("PUT");
|
||||
opts.mode(RequestMode::SameOrigin);
|
||||
opts.body(Some(&changereq_jsvalue));
|
||||
|
||||
let uri = format!("/v1/person/{}/_unix/_credential", id);
|
||||
|
||||
let request = Request::new_with_str_and_init(uri.as_str(), &opts)?;
|
||||
|
||||
request
|
||||
.headers()
|
||||
.set("content-type", "application/json")
|
||||
.expect_throw("failed to set header");
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", format!("Bearer {}", token).as_str())
|
||||
.expect_throw("failed to set header");
|
||||
|
||||
let window = utils::window();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
||||
let status = resp.status();
|
||||
|
||||
if status == 200 {
|
||||
Ok(Msg::Success)
|
||||
} else {
|
||||
let headers = resp.headers();
|
||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
||||
Ok(Msg::Error { emsg, kopid })
|
||||
}
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.pw_val = "".to_string();
|
||||
self.pw_check_val = "".to_string();
|
||||
self.pw_check = PwCheck::Init;
|
||||
}
|
||||
}
|
1
kanidmd_web_ui/src/components/mod.rs
Normal file
1
kanidmd_web_ui/src/components/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod change_unix_password;
|
|
@ -6,6 +6,8 @@ pub const CSS_CLASSES_BODY_FORM: &[&str] = &["flex-column", "d-flex", "h-100"];
|
|||
// the HTML element ID that the signout modal dialogue box has
|
||||
pub const ID_SIGNOUTMODAL: &str = "signoutModal";
|
||||
|
||||
// the HTML element ID that the unix password dialog box has
|
||||
pub const ID_UNIX_PASSWORDCHANGE: &str = "unixPasswordModal";
|
||||
// classes for buttons
|
||||
pub const CLASS_BUTTON_DARK: &str = "btn btn-dark";
|
||||
pub const CLASS_BUTTON_SUCCESS: &str = "btn btn-success";
|
||||
|
|
|
@ -301,6 +301,11 @@ impl Component for PwModalApp {
|
|||
type="password"
|
||||
value={ pw_check_val }
|
||||
/>
|
||||
if !submit_enabled {
|
||||
<div class="invalid-feedback">
|
||||
{ "Passwords do not match." }
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
|
@ -29,6 +29,8 @@ mod oauth2;
|
|||
mod utils;
|
||||
mod views;
|
||||
|
||||
mod components;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
|
||||
pub fn run_app() -> Result<(), JsValue> {
|
||||
yew::start_app::<manager::ManagerApp>();
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use crate::error::*;
|
||||
use crate::models;
|
||||
use crate::utils;
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::manager::Route;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use kanidm_proto::v1::WhoamiResponse;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
@ -49,15 +50,18 @@ enum State {
|
|||
#[derive(PartialEq, Eq, Properties)]
|
||||
pub struct ViewProps {
|
||||
pub token: String,
|
||||
pub current_user: Option<WhoamiResponse>,
|
||||
}
|
||||
|
||||
pub struct ViewsApp {
|
||||
state: State,
|
||||
current_user: Option<WhoamiResponse>,
|
||||
}
|
||||
|
||||
pub enum ViewsMsg {
|
||||
Verified(String),
|
||||
Logout,
|
||||
ProfileInfoRecieved(WhoamiResponse),
|
||||
Error { emsg: String, kopid: Option<String> },
|
||||
}
|
||||
|
||||
|
@ -70,24 +74,6 @@ impl From<FetchError> for ViewsMsg {
|
|||
}
|
||||
}
|
||||
|
||||
fn switch(route: &ViewRoute) -> Html {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::switch");
|
||||
|
||||
// safety - can't panic because to get to this location we MUST be authenticated!
|
||||
let token =
|
||||
models::get_bearer_token().expect_throw("Invalid state, bearer token must be present!");
|
||||
|
||||
match route {
|
||||
ViewRoute::Apps => html! { <AppsApp /> },
|
||||
ViewRoute::Profile => html! { <ProfileApp token={ token } /> },
|
||||
ViewRoute::Security => html! { <SecurityApp token={ token } /> },
|
||||
ViewRoute::NotFound => html! {
|
||||
<Redirect<Route> to={Route::NotFound}/>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ViewsApp {
|
||||
type Message = ViewsMsg;
|
||||
type Properties = ();
|
||||
|
@ -114,7 +100,10 @@ impl Component for ViewsApp {
|
|||
None => State::LoginRequired,
|
||||
};
|
||||
|
||||
ViewsApp { state }
|
||||
ViewsApp {
|
||||
state,
|
||||
current_user: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
|
@ -123,12 +112,20 @@ impl Component for ViewsApp {
|
|||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::update");
|
||||
match msg {
|
||||
ViewsMsg::Verified(token) => {
|
||||
let tk = token.clone();
|
||||
self.state = State::Authenticated(token);
|
||||
// Populate the user profile
|
||||
ctx.link().send_future(async {
|
||||
match Self::fetch_user_data(tk).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
true
|
||||
}
|
||||
ViewsMsg::Logout => {
|
||||
|
@ -136,6 +133,10 @@ impl Component for ViewsApp {
|
|||
self.state = State::LoginRequired;
|
||||
true
|
||||
}
|
||||
ViewsMsg::ProfileInfoRecieved(profile) => {
|
||||
self.current_user = Some(profile);
|
||||
true
|
||||
}
|
||||
ViewsMsg::Error { emsg, kopid } => {
|
||||
self.state = State::Error { emsg, kopid };
|
||||
true
|
||||
|
@ -207,6 +208,8 @@ impl Component for ViewsApp {
|
|||
impl ViewsApp {
|
||||
/// The base page for the user dashboard
|
||||
fn view_authenticated(&self, ctx: &Context<Self>) -> Html {
|
||||
let current_user = self.current_user.clone();
|
||||
|
||||
// WARN set dash-body against body here?
|
||||
html! {
|
||||
<>
|
||||
|
@ -278,12 +281,24 @@ impl ViewsApp {
|
|||
</div>
|
||||
</div>
|
||||
<main class="p-3 x-auto">
|
||||
<Switch<ViewRoute> render={ Switch::render(switch) } />
|
||||
<Switch<ViewRoute> render={ Switch::render(move |route: &ViewRoute| {
|
||||
// safety - can't panic because to get to this location we MUST be authenticated!
|
||||
let token =
|
||||
models::get_bearer_token().expect_throw("Invalid state, bearer token must be present!");
|
||||
|
||||
match route {
|
||||
ViewRoute::Apps => html! { <AppsApp /> },
|
||||
ViewRoute::Profile => html! { <ProfileApp token={ token } current_user={ current_user.clone() } /> },
|
||||
ViewRoute::Security => html! { <SecurityApp token={ token } current_user={ current_user.clone() } /> },
|
||||
ViewRoute::NotFound => html! {
|
||||
<Redirect<Route> to={Route::NotFound}/>
|
||||
},
|
||||
}
|
||||
})} />
|
||||
</main>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_token_valid(token: String) -> Result<ViewsMsg, FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
|
@ -318,4 +333,42 @@ impl ViewsApp {
|
|||
Ok(ViewsMsg::Error { emsg, kopid })
|
||||
}
|
||||
}
|
||||
async fn fetch_user_data(token: String) -> Result<ViewsMsg, FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::SameOrigin);
|
||||
|
||||
let request = Request::new_with_str_and_init("/v1/self", &opts)?;
|
||||
request
|
||||
.headers()
|
||||
.set("content-type", "application/json")
|
||||
.expect_throw("failed to set header");
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", format!("Bearer {}", token).as_str())
|
||||
.expect_throw("failed to set header");
|
||||
|
||||
let window = utils::window();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
||||
let status = resp.status();
|
||||
let headers = resp.headers();
|
||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||
|
||||
if status == 200 {
|
||||
let jsval = JsFuture::from(resp.json()?).await?;
|
||||
let whoamiresponse: WhoamiResponse = jsval
|
||||
.into_serde()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("serde error getting user data -> {:?}", e);
|
||||
console::error!(e_msg.as_str());
|
||||
})
|
||||
.expect_throw("Invalid response type");
|
||||
Ok(ViewsMsg::ProfileInfoRecieved(whoamiresponse))
|
||||
} else {
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
||||
Ok(ViewsMsg::Error { emsg, kopid })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,130 +1,35 @@
|
|||
use crate::error::FetchError;
|
||||
// use crate::error::*;
|
||||
// use crate::models;
|
||||
use crate::utils;
|
||||
use crate::views::ViewProps;
|
||||
|
||||
// use compact_jwt::{Jws, JwsUnverified};
|
||||
use gloo::console;
|
||||
use kanidm_proto::v1::WhoamiResponse;
|
||||
// use kanidm_proto::v1::{UserAuthToken,WhoamiResponse};
|
||||
use std::fmt::Debug;
|
||||
// use std::str::FromStr;
|
||||
use wasm_bindgen::JsCast;
|
||||
// use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
use yew::prelude::*;
|
||||
// use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
|
||||
pub enum Msg {
|
||||
TokenValid(String),
|
||||
TokenInvalid,
|
||||
Error { emsg: String, kopid: Option<String> },
|
||||
ProfileInfoRecieved(WhoamiResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ProfileAppState {
|
||||
Loading,
|
||||
Loaded,
|
||||
}
|
||||
|
||||
impl From<FetchError> for Msg {
|
||||
fn from(fe: FetchError) -> Self {
|
||||
Msg::Error {
|
||||
emsg: fe.as_string(),
|
||||
kopid: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// User Profile UI
|
||||
pub struct ProfileApp {
|
||||
state: ProfileAppState,
|
||||
token: Option<String>,
|
||||
user: Option<WhoamiResponse>,
|
||||
}
|
||||
pub struct ProfileApp {}
|
||||
|
||||
impl Component for ProfileApp {
|
||||
type Message = Msg;
|
||||
type Message = ();
|
||||
type Properties = ViewProps;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::create");
|
||||
|
||||
// Submit a req to init the session.
|
||||
// The uuid we want to submit against - hint, it's us.
|
||||
let token = ctx.props().token.clone();
|
||||
#[cfg(debug)]
|
||||
console::debug!("token: ", &token);
|
||||
|
||||
ctx.link().send_future(async {
|
||||
match Self::fetch_token_valid(token).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
|
||||
ProfileApp {
|
||||
state: ProfileAppState::Loading,
|
||||
token: None,
|
||||
user: None,
|
||||
}
|
||||
ProfileApp {}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::changed");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::update");
|
||||
match msg {
|
||||
Msg::Error { emsg, kopid } => {
|
||||
console::error!(format!(
|
||||
"Failed to do something {:?} - kopid {:?}",
|
||||
emsg, kopid
|
||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
||||
console::debug!(format!(
|
||||
"views::profile::changed current_user: {:?}",
|
||||
ctx.props().current_user,
|
||||
));
|
||||
true
|
||||
}
|
||||
Msg::TokenInvalid => {
|
||||
// TODO redirect off to login
|
||||
let location = utils::window().location();
|
||||
|
||||
match location.replace("/") {
|
||||
// No need to redraw, we are leaving.
|
||||
Ok(_) => return false,
|
||||
Err(e) => {
|
||||
// Something went bang, opps.
|
||||
console::error!(format!("{:?}", e).as_str());
|
||||
// self.state = State::ErrInvalidRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
Msg::TokenValid(token) => {
|
||||
// nothin' much
|
||||
self.token = Some(token.clone());
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("Token is valid! ({})", token));
|
||||
|
||||
ctx.link().send_future(async {
|
||||
match Self::fetch_user_data(token).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
}
|
||||
Msg::ProfileInfoRecieved(data) => {
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("ProfileInfoRecieved({:?})", data));
|
||||
self.state = ProfileAppState::Loaded;
|
||||
self.user = Some(data);
|
||||
}
|
||||
}
|
||||
fn update(&mut self, ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
console::debug!(format!(
|
||||
"views::profile::update current_user: {:?}",
|
||||
ctx.props().current_user,
|
||||
));
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -134,25 +39,17 @@ impl Component for ProfileApp {
|
|||
}
|
||||
|
||||
/// UI view for the user profile
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!(
|
||||
"views::profile::starting view state: {:?}",
|
||||
&self.state
|
||||
));
|
||||
|
||||
let pagecontent = match self.state {
|
||||
ProfileAppState::Loading => {
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let pagecontent = match &ctx.props().current_user {
|
||||
None => {
|
||||
html! {
|
||||
<h2>
|
||||
{"Loading user info..."}
|
||||
</h2>
|
||||
}
|
||||
}
|
||||
ProfileAppState::Loaded => {
|
||||
Some(userinfo) => {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let userinfo = self.user.as_ref().unwrap();
|
||||
|
||||
let mail_primary = match userinfo.uat.mail_primary.as_ref() {
|
||||
Some(email_address) => {
|
||||
html! {
|
||||
|
@ -238,77 +135,3 @@ impl Component for ProfileApp {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProfileApp {
|
||||
async fn fetch_token_valid(token: String) -> Result<Msg, FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::SameOrigin);
|
||||
let request = Request::new_with_str_and_init("/v1/auth/valid", &opts)?;
|
||||
|
||||
request
|
||||
.headers()
|
||||
.set("content-type", "application/json")
|
||||
.expect_throw("failed to set header");
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", format!("Bearer {}", token).as_str())
|
||||
.expect_throw("failed to set header");
|
||||
|
||||
let window = crate::utils::window();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
||||
let status = resp.status();
|
||||
|
||||
if status == 200 {
|
||||
Ok(Msg::TokenValid(token))
|
||||
} else if status == 401 {
|
||||
Ok(Msg::TokenInvalid)
|
||||
} else {
|
||||
let headers = resp.headers();
|
||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
||||
Ok(Msg::Error { emsg, kopid })
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_user_data(token: String) -> Result<Msg, FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::SameOrigin);
|
||||
|
||||
let request = Request::new_with_str_and_init("/v1/self", &opts)?;
|
||||
request
|
||||
.headers()
|
||||
.set("content-type", "application/json")
|
||||
.expect_throw("failed to set header");
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", format!("Bearer {}", token).as_str())
|
||||
.expect_throw("failed to set header");
|
||||
|
||||
let window = utils::window();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
||||
let status = resp.status();
|
||||
let headers = resp.headers();
|
||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||
|
||||
if status == 200 {
|
||||
let jsval = JsFuture::from(resp.json()?).await?;
|
||||
let whoamiresponse: WhoamiResponse = jsval
|
||||
.into_serde()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("serde error getting user data -> {:?}", e);
|
||||
console::error!(e_msg.as_str());
|
||||
})
|
||||
.expect_throw("Invalid response type");
|
||||
Ok(Msg::ProfileInfoRecieved(whoamiresponse))
|
||||
} else {
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
||||
Ok(Msg::Error { emsg, kopid })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::error::*;
|
|||
use crate::models;
|
||||
use crate::utils;
|
||||
|
||||
use crate::components::change_unix_password::ChangeUnixPassword;
|
||||
use crate::manager::Route;
|
||||
use crate::views::{ViewProps, ViewRoute};
|
||||
|
||||
|
@ -65,7 +66,7 @@ impl Component for SecurityApp {
|
|||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::security::changed");
|
||||
false
|
||||
true
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
|
@ -127,7 +128,7 @@ impl Component for SecurityApp {
|
|||
State::Waiting => false,
|
||||
};
|
||||
|
||||
let error = match &self.state {
|
||||
let flash = match &self.state {
|
||||
State::Error { emsg, kopid } => {
|
||||
let message = match kopid {
|
||||
Some(k) => format!("An error occured - {} - {}", emsg, k),
|
||||
|
@ -136,19 +137,21 @@ impl Component for SecurityApp {
|
|||
html! {
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
{ message }
|
||||
<button type="button" class="btn btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
<button type="button" class="btn btn-close" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
_ => html! { <></> },
|
||||
};
|
||||
|
||||
let current_user = ctx.props().current_user.clone();
|
||||
|
||||
html! {
|
||||
<>
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h2>{ "Security" }</h2>
|
||||
</div>
|
||||
{ error }
|
||||
{ flash }
|
||||
<div>
|
||||
<p>
|
||||
<button type="button" class="btn btn-primary"
|
||||
|
@ -164,6 +167,16 @@ impl Component for SecurityApp {
|
|||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
if let Some(user) = current_user {
|
||||
if user.youare.attrs.get("class").map(|x| x.contains(&String::from("posixaccount"))).unwrap_or(true) {
|
||||
<div>
|
||||
<p>
|
||||
<ChangeUnixPassword token={ctx.props().token.clone()}></ChangeUnixPassword>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +188,7 @@ impl SecurityApp {
|
|||
opts.method("GET");
|
||||
opts.mode(RequestMode::SameOrigin);
|
||||
|
||||
let uri = format!("/v1/account/{}/_credential/_update", id);
|
||||
let uri = format!("/v1/person/{}/_credential/_update", id);
|
||||
|
||||
let request = Request::new_with_str_and_init(uri.as_str(), &opts)?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue