mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Oauth2 ui flows (#527)
This commit is contained in:
parent
18aa4e0525
commit
1791f12adf
103
Cargo.lock
generated
103
Cargo.lock
generated
|
@ -590,12 +590,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-match"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8100e46ff92eb85bf6dc2930c73f2a4f7176393c84a9446b3d501e1b354e7b34"
|
||||
|
||||
[[package]]
|
||||
name = "checked_int_cast"
|
||||
version = "1.0.0"
|
||||
|
@ -1947,11 +1941,15 @@ dependencies = [
|
|||
"anyhow",
|
||||
"js-sys",
|
||||
"kanidm_proto",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webauthn-rs",
|
||||
"yew",
|
||||
"yew-router",
|
||||
"yew-services",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2847,6 +2845,12 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e"
|
||||
|
||||
[[package]]
|
||||
name = "route-recognizer"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824172f0afccf3773c3905f5550ac94572144efe0deaf49a1f22bbca188d193e"
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "5.0.1"
|
||||
|
@ -3436,7 +3440,7 @@ dependencies = [
|
|||
"kv-log-macro",
|
||||
"log",
|
||||
"pin-project-lite 0.2.7",
|
||||
"route-recognizer",
|
||||
"route-recognizer 0.2.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
@ -3947,6 +3951,28 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weblog"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eec0958e0181982bc8b4577b1363f21300a670603615c58643f172c92c47357"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"weblog-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weblog-proc-macro"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d9abb8ee84ede5408a346721d72fb216a27f53a539ff3c83ed1bf7625af7104"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.4"
|
||||
|
@ -4014,25 +4040,18 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
|||
|
||||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.17.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d8703eb5b883e816cd74c65e2f6dd4144eeedb77c1b3e0284e8f3f593b80ab1"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/yewstack/yew?rev=1280dfd8315b893e96727b5ca5ad852919563823#1280dfd8315b893e96727b5ca5ad852919563823"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anymap",
|
||||
"bincode",
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-match",
|
||||
"console_error_panic_hook",
|
||||
"futures",
|
||||
"gloo",
|
||||
"http",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"log",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slab",
|
||||
|
@ -4045,18 +4064,62 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yew-macro"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a9a452e63b6222b28b426dafbc6b207192e0127cdb93324cc7407b8c7e1768"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/yewstack/yew?rev=1280dfd8315b893e96727b5ca5ad852919563823#1280dfd8315b893e96727b5ca5ad852919563823"
|
||||
dependencies = [
|
||||
"boolinator",
|
||||
"lazy_static",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-router"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/yewstack/yew?rev=1280dfd8315b893e96727b5ca5ad852919563823#1280dfd8315b893e96727b5ca5ad852919563823"
|
||||
dependencies = [
|
||||
"gloo",
|
||||
"js-sys",
|
||||
"route-recognizer 0.3.0",
|
||||
"serde",
|
||||
"serde_urlencoded",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"weblog",
|
||||
"yew",
|
||||
"yew-router-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-router-macro"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/yewstack/yew?rev=1280dfd8315b893e96727b5ca5ad852919563823#1280dfd8315b893e96727b5ca5ad852919563823"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-services"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/yewstack/yew?rev=1280dfd8315b893e96727b5ca5ad852919563823#1280dfd8315b893e96727b5ca5ad852919563823"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gloo",
|
||||
"http",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.4.1"
|
||||
|
|
|
@ -721,6 +721,10 @@ impl KanidmAsyncClient {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn auth_valid(&self) -> Result<(), ClientError> {
|
||||
self.perform_get_request("/v1/auth/valid").await
|
||||
}
|
||||
|
||||
pub async fn whoami(&self) -> Result<Option<(Entry, UserAuthToken)>, ClientError> {
|
||||
let whoami_dest = [self.addr.as_str(), "/v1/self"].concat();
|
||||
// format!("{}/v1/self", self.addr);
|
||||
|
|
|
@ -476,6 +476,10 @@ impl KanidmClient {
|
|||
tokio_block_on(self.asclient.auth_webauthn_begin(ident))
|
||||
}
|
||||
|
||||
pub fn auth_valid(&self) -> Result<(), ClientError> {
|
||||
tokio_block_on(self.asclient.auth_valid())
|
||||
}
|
||||
|
||||
pub fn auth_webauthn_complete(&self, pkc: PublicKeyCredential) -> Result<(), ClientError> {
|
||||
tokio_block_on(self.asclient.auth_webauthn_complete(pkc))
|
||||
}
|
||||
|
|
|
@ -84,6 +84,11 @@ fn test_server_whoami_anonymous() {
|
|||
};
|
||||
debug!("{}", uat);
|
||||
assert!(uat.spn == "anonymous@example.com");
|
||||
|
||||
// Do a check of the auth/valid endpoint, tells us if our token
|
||||
// is okay.
|
||||
let res = rsclient.auth_valid();
|
||||
assert!(res.is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1009,6 +1009,36 @@ impl QueryServerReadV1 {
|
|||
res
|
||||
}
|
||||
|
||||
pub async fn handle_auth_valid(
|
||||
&self,
|
||||
uat: Option<String>,
|
||||
eventid: Uuid,
|
||||
) -> Result<(), OperationError> {
|
||||
let mut audit =
|
||||
AuditScope::new("auth_valid", eventid, self.log_level);
|
||||
let ct = duration_from_epoch_now();
|
||||
let idms_prox_read = self.idms.proxy_read_async().await;
|
||||
|
||||
let res = lperf_op_segment!(
|
||||
&mut audit,
|
||||
"actors::v1_read::handle<AuthValid>",
|
||||
|| {
|
||||
idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid token: {:?}", e);
|
||||
e
|
||||
})
|
||||
}
|
||||
);
|
||||
self.log.send(audit).map_err(|_| {
|
||||
error!("CRITICAL: UNABLE TO COMMIT LOGS");
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
res
|
||||
}
|
||||
|
||||
pub async fn handle_ldaprequest(
|
||||
&self,
|
||||
eventid: Uuid,
|
||||
|
|
|
@ -160,11 +160,6 @@ async fn index_view(_req: tide::Request<AppState>) -> tide::Result {
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Kanidm</title>
|
||||
<link rel="stylesheet" href="/pkg/external/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T">
|
||||
<script src="/pkg/external/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"></script>
|
||||
<script src="/pkg/external/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"></script>
|
||||
<script src="/pkg/external/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"></script>
|
||||
<script src="/pkg/external/confetti.js"></script>
|
||||
<script src="/pkg/bundle.js" defer></script>
|
||||
</head>
|
||||
|
||||
|
@ -1023,6 +1018,13 @@ pub async fn auth(mut req: tide::Request<AppState>) -> tide::Result {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn auth_valid(req: tide::Request<AppState>) -> tide::Result {
|
||||
let uat = req.get_current_uat();
|
||||
let (eventid, hvalue) = req.new_eventid();
|
||||
let res = req.state().qe_r_ref.handle_auth_valid(uat, eventid).await;
|
||||
to_tide_response(res, hvalue)
|
||||
}
|
||||
|
||||
pub async fn idm_account_set_password(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
let uat = req.get_current_uat();
|
||||
let obj: SingleStringRequest = req.body_json().await?;
|
||||
|
@ -1210,6 +1212,8 @@ pub fn create_https_server(
|
|||
}
|
||||
|
||||
tserver.at("/").get(index_view);
|
||||
tserver.at("/ui/").get(index_view);
|
||||
tserver.at("/ui/*").get(index_view);
|
||||
tserver
|
||||
.at("/pkg")
|
||||
.serve_dir(env!("KANIDM_WEB_UI_PKG_PATH"))
|
||||
|
@ -1232,9 +1236,13 @@ pub fn create_https_server(
|
|||
// == oauth endpoints.
|
||||
|
||||
let mut oauth2_process = tserver.at("/oauth2");
|
||||
oauth2_process.at("/authorise").get(oauth2_authorise_get);
|
||||
oauth2_process
|
||||
.at("/authorise")
|
||||
.post(oauth2_authorise_post)
|
||||
.get(oauth2_authorise_get);
|
||||
oauth2_process
|
||||
.at("/authorise/permit")
|
||||
.post(oauth2_authorise_permit_post)
|
||||
.get(oauth2_authorise_permit_get);
|
||||
oauth2_process.at("/token").post(oauth2_token_post);
|
||||
/*
|
||||
|
@ -1250,6 +1258,7 @@ pub fn create_https_server(
|
|||
raw_route.at("/search").post(search);
|
||||
|
||||
tserver.at("/v1/auth").post(auth);
|
||||
tserver.at("/v1/auth/valid").get(auth_valid);
|
||||
|
||||
let mut schema_route = tserver.at("/v1/schema");
|
||||
schema_route.at("/").get(schema_get);
|
||||
|
|
|
@ -147,6 +147,11 @@ pub async fn oauth2_id_delete(req: tide::Request<AppState>) -> tide::Result {
|
|||
// valid Kanidm instance in the topology can handle these request.
|
||||
//
|
||||
|
||||
pub async fn oauth2_authorise_post(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
let auth_req: AuthorisationRequest = req.body_json().await?;
|
||||
oauth2_authorise(req, auth_req).await
|
||||
}
|
||||
|
||||
pub async fn oauth2_authorise_get(req: tide::Request<AppState>) -> tide::Result {
|
||||
// Start the oauth2 authorisation flow to present to the user.
|
||||
debug!("Request Query - {:?}", req.url().query());
|
||||
|
@ -159,6 +164,13 @@ pub async fn oauth2_authorise_get(req: tide::Request<AppState>) -> tide::Result
|
|||
)
|
||||
})?;
|
||||
|
||||
oauth2_authorise(req, auth_req).await
|
||||
}
|
||||
|
||||
async fn oauth2_authorise(
|
||||
req: tide::Request<AppState>,
|
||||
auth_req: AuthorisationRequest,
|
||||
) -> tide::Result {
|
||||
let uat = req.get_current_uat();
|
||||
let (eventid, hvalue) = req.new_eventid();
|
||||
|
||||
|
@ -209,6 +221,17 @@ pub async fn oauth2_authorise_get(req: tide::Request<AppState>) -> tide::Result
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn oauth2_authorise_permit_post(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
let consent_req: String = req.body_json().await?;
|
||||
oauth2_authorise_permit(req, consent_req)
|
||||
.await
|
||||
.map(|mut res| {
|
||||
// in post, we need the redirect not to be issued, so we mask 302 to 200
|
||||
res.set_status(200);
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct ConsentRequestData {
|
||||
token: String,
|
||||
|
@ -226,13 +249,20 @@ pub async fn oauth2_authorise_permit_get(req: tide::Request<AppState>) -> tide::
|
|||
)
|
||||
})?;
|
||||
|
||||
oauth2_authorise_permit(req, consent_req.token).await
|
||||
}
|
||||
|
||||
async fn oauth2_authorise_permit(
|
||||
req: tide::Request<AppState>,
|
||||
consent_req: String,
|
||||
) -> tide::Result {
|
||||
let uat = req.get_current_uat();
|
||||
let (eventid, hvalue) = req.new_eventid();
|
||||
|
||||
let res = req
|
||||
.state()
|
||||
.qe_r_ref
|
||||
.handle_oauth2_authorise_permit(uat, consent_req.token, eventid)
|
||||
.handle_oauth2_authorise_permit(uat, consent_req, eventid)
|
||||
.await;
|
||||
|
||||
let mut res = match res {
|
||||
|
@ -248,6 +278,8 @@ pub async fn oauth2_authorise_permit_get(req: tide::Request<AppState>) -> tide::
|
|||
.append_pair("state", &state.to_string())
|
||||
.append_pair("code", &code);
|
||||
res.insert_header("Location", redirect_uri.as_str());
|
||||
// I think the client server needs this
|
||||
// res.insert_header("Access-Control-Allow-Origin", redirect_uri.origin().ascii_serialization());
|
||||
res
|
||||
}
|
||||
Err(_e) => {
|
||||
|
|
|
@ -13,14 +13,22 @@ repository = "https://github.com/kanidm/kanidm/"
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = { version = "0.4" }
|
||||
yew = "0.17"
|
||||
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha" }
|
||||
anyhow = "1"
|
||||
|
||||
webauthn-rs = { version = "0.3.0-alpha.9", default-features = false, features = ["wasm"] }
|
||||
|
||||
# Waiting on 0.19 to be released for the router rewrite. :)
|
||||
# yewdux = "^0.6"
|
||||
yew-router = { git = "https://github.com/yewstack/yew", rev = "1280dfd8315b893e96727b5ca5ad852919563823" }
|
||||
yew = {git = "https://github.com/yewstack/yew", rev = "1280dfd8315b893e96727b5ca5ad852919563823"}
|
||||
yew-services = {git = "https://github.com/yewstack/yew", rev = "1280dfd8315b893e96727b5ca5ad852919563823"}
|
||||
|
||||
web-sys = { version = "0.3", features = ["PublicKeyCredentialUserEntity", "CredentialCreationOptions", "Navigator", "Window", "CredentialsContainer", "PublicKeyCredentialRpEntity", "PublicKeyCredentialCreationOptions", "PublicKeyCredential", "AuthenticatorResponse", "AuthenticationExtensionsClientOutputs", "CredentialRequestOptions"] }
|
||||
js-sys = "0.3"
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#!/bin/sh
|
||||
wasm-pack build --no-typescript --release --target web && \
|
||||
rollup ./src/main.js --format iife --file ./pkg/bundle.js && \
|
||||
cp ./src/style.css ./pkg/style.css && \
|
||||
rm ./pkg/.gitignore
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,15 @@
|
|||
return ret;
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
@ -39,35 +48,6 @@
|
|||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetFloat64Memory0 = null;
|
||||
function getFloat64Memory0() {
|
||||
if (cachegetFloat64Memory0 === null || cachegetFloat64Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetFloat64Memory0 = new Float64Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetFloat64Memory0;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
@ -123,6 +103,18 @@
|
|||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
function debugString(val) {
|
||||
// primitive types
|
||||
const type = typeof val;
|
||||
|
@ -222,21 +214,28 @@
|
|||
}
|
||||
function __wbg_adapter_28(arg0, arg1, arg2) {
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h12db7a557fe366a6(arg0, arg1, addBorrowedObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h52eb73d2ac354d39(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function __wbg_adapter_31(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hdf3e77ed17bfc63a(arg0, arg1, addHeapObject(arg2));
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h886f07612e3c7ab0(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function __wbg_adapter_34(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h71e4e4fa4a361250(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }
|
||||
/**
|
||||
*/
|
||||
function run_login_app() {
|
||||
wasm.run_login_app();
|
||||
function run_app() {
|
||||
wasm.run_app();
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
|
@ -287,7 +286,6 @@
|
|||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_startConfetti_1e3412426d93bcf0 = typeof startConfetti == 'function' ? startConfetti : notDefined('startConfetti');
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
|
@ -297,6 +295,10 @@
|
|||
var ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
var ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
|
@ -309,15 +311,11 @@
|
|||
var ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_Window_6f26ab8994cdec9b = function(arg0) {
|
||||
imports.wbg.__wbg_Window_249a3ebd6d38d83a = function(arg0) {
|
||||
var ret = getObject(arg0).Window;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_WorkerGlobalScope_65696f271e05e492 = function(arg0) {
|
||||
imports.wbg.__wbg_WorkerGlobalScope_4eca747bcad790ad = function(arg0) {
|
||||
var ret = getObject(arg0).WorkerGlobalScope;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
@ -347,18 +345,18 @@
|
|||
var ret = getObject(arg0).document;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_location_05eee59b82ccc208 = function(arg0) {
|
||||
var ret = getObject(arg0).location;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_history_dacf8adbb87ea89a = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).history;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_navigator_5c90643c2a2b6cda = function(arg0) {
|
||||
var ret = getObject(arg0).navigator;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_innerWidth_8c5001da2fdd6a9e = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).innerWidth;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_innerHeight_03d3f1d9eb5f7034 = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).innerHeight;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_localStorage_b787eb9a4418c2b1 = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).localStorage;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
|
@ -371,6 +369,18 @@
|
|||
var ret = getObject(arg0).fetch(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_documentElement_8380e824421596e0 = function(arg0) {
|
||||
var ret = getObject(arg0).documentElement;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_location_679cc810ad30fa6e = function(arg0) {
|
||||
var ret = getObject(arg0).location;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_body_525168d9e773c3f8 = function(arg0) {
|
||||
var ret = getObject(arg0).body;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_createElement_ac65a6ce60c4812c = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
|
@ -383,10 +393,18 @@
|
|||
var ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getElementById_b180ea4ada06a837 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_querySelector_be2ba71c84a94384 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrsequencesequence_e87760babcd72b35 = function() { return handleError(function (arg0) {
|
||||
var ret = new Headers(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_status_1a7d875f6e1318cd = function(arg0) {
|
||||
var ret = getObject(arg0).status;
|
||||
return ret;
|
||||
|
@ -403,6 +421,17 @@
|
|||
var ret = getObject(arg0).text();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_424883ec901747fb = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).pathname;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_new_f55f190117032894 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlTextAreaElement_244fe1b35f3576f5 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLTextAreaElement;
|
||||
return ret;
|
||||
|
@ -425,6 +454,13 @@
|
|||
var ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_d80c1e89667410f5 = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).href;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlButtonElement_da6b54269a93893e = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLButtonElement;
|
||||
return ret;
|
||||
|
@ -432,23 +468,10 @@
|
|||
imports.wbg.__wbg_settype_985173fd488717c8 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).type = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_signal_f64265959762debe = function(arg0) {
|
||||
var ret = getObject(arg0).signal;
|
||||
imports.wbg.__wbg_getClientExtensionResults_593a22cc55be1c72 = function(arg0) {
|
||||
var ret = getObject(arg0).getClientExtensionResults();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_11305633f0032f66 = function() { return handleError(function () {
|
||||
var ret = new AbortController();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_abort_ee85c4a280b33235 = function(arg0) {
|
||||
getObject(arg0).abort();
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_6d9a78a5d277bdaf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_09c628abeb1c283b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlInputElement_df6fbc606ba24e20 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLInputElement;
|
||||
return ret;
|
||||
|
@ -485,14 +508,52 @@
|
|||
imports.wbg.__wbg_log_9a99fb1af846153b = function(arg0) {
|
||||
console.log(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_warn_b39e749f1dc02058 = function(arg0) {
|
||||
console.warn(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlElement_835072e813858ac0 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLElement;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_focus_2fac919cca20d33b = function() { return handleError(function (arg0) {
|
||||
getObject(arg0).focus();
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_fetch_0eb960ce3c5dd138 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).fetch(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_signal_f64265959762debe = function(arg0) {
|
||||
var ret = getObject(arg0).signal;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_11305633f0032f66 = function() { return handleError(function () {
|
||||
var ret = new AbortController();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_abort_ee85c4a280b33235 = function(arg0) {
|
||||
getObject(arg0).abort();
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_6d9a78a5d277bdaf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_dispatchEvent_bacbc3eeb0d17fcd = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg0).dispatchEvent(getObject(arg1));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_09c628abeb1c283b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_credentials_d60560b46767931f = function(arg0) {
|
||||
var ret = getObject(arg0).credentials;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_d110e781a7944595 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = new Event(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pushState_bf225f37e812ce05 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_lastChild_60bd092ff114802e = function(arg0) {
|
||||
var ret = getObject(arg0).lastChild;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
|
@ -519,6 +580,23 @@
|
|||
var ret = getObject(arg0).removeChild(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_dc16eee3971da398 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg1).pathname;
|
||||
var ptr0 = 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_search_046d97b9a2173122 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg1).search;
|
||||
var ptr0 = 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_replace_331a5a7dcd9e0a39 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).replace(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_getItem_1c87352132d0b415 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
var ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -526,13 +604,12 @@
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeItem_1a25c42d013b0303 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).removeItem(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_setItem_103b1e46491c9b0e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrsequencesequence_e87760babcd72b35 = function() { return handleError(function (arg0) {
|
||||
var ret = new Headers(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_b7bbf50adcc94294 = function(arg0, arg1) {
|
||||
var ret = getObject(arg0)[arg1 >>> 0];
|
||||
return addHeapObject(ret);
|
||||
|
@ -653,12 +730,6 @@
|
|||
var ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
|
@ -667,6 +738,11 @@
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
var ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
var ret = debugString(getObject(arg1));
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -681,12 +757,16 @@
|
|||
var ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper650 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 254, __wbg_adapter_28);
|
||||
imports.wbg.__wbindgen_closure_wrapper450 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 161, __wbg_adapter_28);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper1501 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 338, __wbg_adapter_31);
|
||||
imports.wbg.__wbindgen_closure_wrapper1231 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 497, __wbg_adapter_31);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper2127 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 540, __wbg_adapter_34);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
@ -706,7 +786,7 @@
|
|||
|
||||
async function main() {
|
||||
await init('/pkg/kanidmd_web_ui_bg.wasm');
|
||||
run_login_app();
|
||||
run_app();
|
||||
}
|
||||
main();
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@ function takeObject(idx) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
@ -37,35 +46,6 @@ function getStringFromWasm0(ptr, len) {
|
|||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetFloat64Memory0 = null;
|
||||
function getFloat64Memory0() {
|
||||
if (cachegetFloat64Memory0 === null || cachegetFloat64Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetFloat64Memory0 = new Float64Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetFloat64Memory0;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
@ -121,6 +101,18 @@ function passStringToWasm0(arg, malloc, realloc) {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
function debugString(val) {
|
||||
// primitive types
|
||||
const type = typeof val;
|
||||
|
@ -220,21 +212,28 @@ function addBorrowedObject(obj) {
|
|||
}
|
||||
function __wbg_adapter_28(arg0, arg1, arg2) {
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h12db7a557fe366a6(arg0, arg1, addBorrowedObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h52eb73d2ac354d39(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function __wbg_adapter_31(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hdf3e77ed17bfc63a(arg0, arg1, addHeapObject(arg2));
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h886f07612e3c7ab0(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function __wbg_adapter_34(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h71e4e4fa4a361250(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }
|
||||
/**
|
||||
*/
|
||||
export function run_login_app() {
|
||||
wasm.run_login_app();
|
||||
export function run_app() {
|
||||
wasm.run_app();
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
|
@ -285,7 +284,6 @@ async function init(input) {
|
|||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_startConfetti_1e3412426d93bcf0 = typeof startConfetti == 'function' ? startConfetti : notDefined('startConfetti');
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
|
@ -295,6 +293,10 @@ async function init(input) {
|
|||
var ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
var ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
|
@ -307,15 +309,11 @@ async function init(input) {
|
|||
var ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_Window_6f26ab8994cdec9b = function(arg0) {
|
||||
imports.wbg.__wbg_Window_249a3ebd6d38d83a = function(arg0) {
|
||||
var ret = getObject(arg0).Window;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_WorkerGlobalScope_65696f271e05e492 = function(arg0) {
|
||||
imports.wbg.__wbg_WorkerGlobalScope_4eca747bcad790ad = function(arg0) {
|
||||
var ret = getObject(arg0).WorkerGlobalScope;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
@ -345,18 +343,18 @@ async function init(input) {
|
|||
var ret = getObject(arg0).document;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_location_05eee59b82ccc208 = function(arg0) {
|
||||
var ret = getObject(arg0).location;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_history_dacf8adbb87ea89a = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).history;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_navigator_5c90643c2a2b6cda = function(arg0) {
|
||||
var ret = getObject(arg0).navigator;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_innerWidth_8c5001da2fdd6a9e = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).innerWidth;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_innerHeight_03d3f1d9eb5f7034 = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).innerHeight;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_localStorage_b787eb9a4418c2b1 = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).localStorage;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
|
@ -369,6 +367,18 @@ async function init(input) {
|
|||
var ret = getObject(arg0).fetch(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_documentElement_8380e824421596e0 = function(arg0) {
|
||||
var ret = getObject(arg0).documentElement;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_location_679cc810ad30fa6e = function(arg0) {
|
||||
var ret = getObject(arg0).location;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_body_525168d9e773c3f8 = function(arg0) {
|
||||
var ret = getObject(arg0).body;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_createElement_ac65a6ce60c4812c = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
|
@ -381,10 +391,18 @@ async function init(input) {
|
|||
var ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getElementById_b180ea4ada06a837 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_querySelector_be2ba71c84a94384 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrsequencesequence_e87760babcd72b35 = function() { return handleError(function (arg0) {
|
||||
var ret = new Headers(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_status_1a7d875f6e1318cd = function(arg0) {
|
||||
var ret = getObject(arg0).status;
|
||||
return ret;
|
||||
|
@ -401,6 +419,17 @@ async function init(input) {
|
|||
var ret = getObject(arg0).text();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_424883ec901747fb = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).pathname;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_new_f55f190117032894 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlTextAreaElement_244fe1b35f3576f5 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLTextAreaElement;
|
||||
return ret;
|
||||
|
@ -423,6 +452,13 @@ async function init(input) {
|
|||
var ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_d80c1e89667410f5 = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).href;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlButtonElement_da6b54269a93893e = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLButtonElement;
|
||||
return ret;
|
||||
|
@ -430,23 +466,10 @@ async function init(input) {
|
|||
imports.wbg.__wbg_settype_985173fd488717c8 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).type = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_signal_f64265959762debe = function(arg0) {
|
||||
var ret = getObject(arg0).signal;
|
||||
imports.wbg.__wbg_getClientExtensionResults_593a22cc55be1c72 = function(arg0) {
|
||||
var ret = getObject(arg0).getClientExtensionResults();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_11305633f0032f66 = function() { return handleError(function () {
|
||||
var ret = new AbortController();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_abort_ee85c4a280b33235 = function(arg0) {
|
||||
getObject(arg0).abort();
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_6d9a78a5d277bdaf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_09c628abeb1c283b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlInputElement_df6fbc606ba24e20 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLInputElement;
|
||||
return ret;
|
||||
|
@ -483,14 +506,52 @@ async function init(input) {
|
|||
imports.wbg.__wbg_log_9a99fb1af846153b = function(arg0) {
|
||||
console.log(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_warn_b39e749f1dc02058 = function(arg0) {
|
||||
console.warn(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlElement_835072e813858ac0 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof HTMLElement;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_focus_2fac919cca20d33b = function() { return handleError(function (arg0) {
|
||||
getObject(arg0).focus();
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_fetch_0eb960ce3c5dd138 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).fetch(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_signal_f64265959762debe = function(arg0) {
|
||||
var ret = getObject(arg0).signal;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_11305633f0032f66 = function() { return handleError(function () {
|
||||
var ret = new AbortController();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_abort_ee85c4a280b33235 = function(arg0) {
|
||||
getObject(arg0).abort();
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_6d9a78a5d277bdaf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_dispatchEvent_bacbc3eeb0d17fcd = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg0).dispatchEvent(getObject(arg1));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_09c628abeb1c283b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_credentials_d60560b46767931f = function(arg0) {
|
||||
var ret = getObject(arg0).credentials;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_d110e781a7944595 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = new Event(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pushState_bf225f37e812ce05 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_lastChild_60bd092ff114802e = function(arg0) {
|
||||
var ret = getObject(arg0).lastChild;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
|
@ -517,6 +578,23 @@ async function init(input) {
|
|||
var ret = getObject(arg0).removeChild(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_dc16eee3971da398 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg1).pathname;
|
||||
var ptr0 = 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_search_046d97b9a2173122 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg1).search;
|
||||
var ptr0 = 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_replace_331a5a7dcd9e0a39 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).replace(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_getItem_1c87352132d0b415 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
var ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -524,13 +602,12 @@ async function init(input) {
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeItem_1a25c42d013b0303 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).removeItem(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_setItem_103b1e46491c9b0e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrsequencesequence_e87760babcd72b35 = function() { return handleError(function (arg0) {
|
||||
var ret = new Headers(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_b7bbf50adcc94294 = function(arg0, arg1) {
|
||||
var ret = getObject(arg0)[arg1 >>> 0];
|
||||
return addHeapObject(ret);
|
||||
|
@ -651,12 +728,6 @@ async function init(input) {
|
|||
var ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
|
@ -665,6 +736,11 @@ async function init(input) {
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
var ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
var ret = debugString(getObject(arg1));
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -679,12 +755,16 @@ async function init(input) {
|
|||
var ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper650 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 254, __wbg_adapter_28);
|
||||
imports.wbg.__wbindgen_closure_wrapper450 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 161, __wbg_adapter_28);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper1501 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 338, __wbg_adapter_31);
|
||||
imports.wbg.__wbindgen_closure_wrapper1231 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 497, __wbg_adapter_31);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper2127 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 540, __wbg_adapter_34);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
|
Binary file not shown.
|
@ -4,7 +4,7 @@
|
|||
"William Brown <william@blackhats.net.au>"
|
||||
],
|
||||
"description": "Kanidm Server Web User Interface",
|
||||
"version": "1.1.0-alpha.4",
|
||||
"version": "1.1.0-alpha.5",
|
||||
"license": "MPL-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
141
kanidmd_web_ui/pkg/style.css
Normal file
141
kanidmd_web_ui/pkg/style.css
Normal file
|
@ -0,0 +1,141 @@
|
|||
.html-body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.form-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
width: 100%;
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
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: .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, .1);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
top: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-sticky {
|
||||
position: relative;
|
||||
top: 0;
|
||||
height: calc(100vh - 48px);
|
||||
padding-top: .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: .75rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/*
|
||||
* Navbar
|
||||
*/
|
||||
|
||||
.navbar-brand {
|
||||
padding-top: .75rem;
|
||||
padding-bottom: .75rem;
|
||||
font-size: 1rem;
|
||||
background-color: rgba(0, 0, 0, .25);
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
.navbar .navbar-toggler {
|
||||
top: .25rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.navbar .form-control {
|
||||
padding: .75rem 1rem;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.form-control-dark {
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
border-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.form-control-dark:focus {
|
||||
border-color: transparent;
|
||||
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||
}
|
|
@ -1,12 +1,18 @@
|
|||
#![recursion_limit = "256"]
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
// use yew::prelude::*;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod login;
|
||||
mod manager;
|
||||
mod models;
|
||||
mod oauth2;
|
||||
mod views;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run_login_app() -> Result<(), JsValue> {
|
||||
yew::start_app::<login::LoginApp>();
|
||||
pub fn run_app() -> Result<(), JsValue> {
|
||||
yew::start_app_as_body::<manager::ManagerApp>();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use anyhow::Error;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||||
use yew::format::Json;
|
||||
use yew::prelude::*;
|
||||
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
|
||||
use yew::services::{ConsoleService, StorageService};
|
||||
use yew_services::fetch::{FetchService, FetchTask, Request, Response};
|
||||
use yew_services::ConsoleService;
|
||||
|
||||
use crate::manager::Route;
|
||||
use crate::models;
|
||||
|
||||
use kanidm_proto::v1::{
|
||||
AuthAllowed, AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep,
|
||||
|
@ -20,7 +24,6 @@ extern "C" {
|
|||
pub struct LoginApp {
|
||||
link: ComponentLink<Self>,
|
||||
inputvalue: String,
|
||||
lstorage: StorageService,
|
||||
ft: Option<FetchTask>,
|
||||
session_id: String,
|
||||
state: LoginState,
|
||||
|
@ -35,9 +38,6 @@ enum TotpState {
|
|||
|
||||
enum LoginState {
|
||||
Init(bool),
|
||||
|
||||
// MechChoice
|
||||
// CredChoice
|
||||
Password(bool),
|
||||
BackupCode(bool),
|
||||
Totp(TotpState),
|
||||
|
@ -126,6 +126,7 @@ impl LoginApp {
|
|||
}
|
||||
|
||||
fn view_state(&self) -> Html {
|
||||
let inputvalue = self.inputvalue.clone();
|
||||
match &self.state {
|
||||
LoginState::Init(enable) => {
|
||||
html! {
|
||||
|
@ -136,10 +137,23 @@ impl LoginApp {
|
|||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div>
|
||||
<input id="username" type="text" class="form-control" value=self.inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=!enable />
|
||||
<button type="button" class="btn btn-dark" onclick=self.link.callback(|_| LoginAppMsg::Begin) disabled=!enable >{" Begin "}</button>
|
||||
</div>
|
||||
<form
|
||||
onsubmit=self.link.callback(|_| LoginAppMsg::Begin)
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<input id="autofocus"
|
||||
type="text"
|
||||
class="form-control"
|
||||
value=inputvalue
|
||||
disabled=!enable
|
||||
oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value))
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-dark"
|
||||
disabled=!enable
|
||||
>{" Begin "}</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@ -153,10 +167,13 @@ impl LoginApp {
|
|||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div>
|
||||
<input id="password" type="password" class="form-control" value=self.inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=!enable />
|
||||
<button type="button" class="btn btn-dark" onclick=self.link.callback(|_| LoginAppMsg::PasswordSubmit) disabled=!enable >{" Submit "}</button>
|
||||
</div>
|
||||
<form
|
||||
onsubmit=self.link.callback(|_| LoginAppMsg::PasswordSubmit)
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<input id="autofocus" type="password" class="form-control" value=inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=!enable />
|
||||
<button type="submit" class="btn btn-dark" disabled=!enable >{" Submit "}</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@ -170,10 +187,13 @@ impl LoginApp {
|
|||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div>
|
||||
<input id="backupcode" type="text" class="form-control" value=self.inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=!enable />
|
||||
<button type="button" class="btn btn-dark" onclick=self.link.callback(|_| LoginAppMsg::BackupCodeSubmit) disabled=!enable >{" Submit "}</button>
|
||||
</div>
|
||||
<form
|
||||
onsubmit=self.link.callback(|_| LoginAppMsg::BackupCodeSubmit)
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<input id="autofocus" type="text" class="form-control" value=inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=!enable />
|
||||
<button type="submit" class="btn btn-dark" disabled=!enable >{" Submit "}</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@ -188,10 +208,13 @@ impl LoginApp {
|
|||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div>
|
||||
<input id="totp" type="text" class="form-control" value=self.inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=state==&TotpState::Disabled />
|
||||
<button type="button" class="btn btn-dark" onclick=self.link.callback(|_| LoginAppMsg::TotpSubmit) disabled=state==&TotpState::Disabled >{" Submit "}</button>
|
||||
</div>
|
||||
<form
|
||||
onsubmit=self.link.callback(|_| LoginAppMsg::TotpSubmit)
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<input id="autofocus" type="text" class="form-control" value=inputvalue oninput=self.link.callback(|e: InputData| LoginAppMsg::Input(e.value)) disabled=state==&TotpState::Disabled />
|
||||
<button type="submit" class="btn btn-dark" disabled=state==&TotpState::Disabled >{" Submit "}</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@ -236,6 +259,9 @@ impl LoginApp {
|
|||
}
|
||||
}
|
||||
LoginState::Authenticated => {
|
||||
let loc: Route = models::pop_return_location().into();
|
||||
// redirect
|
||||
yew_router::push_route(loc);
|
||||
html! {
|
||||
<div class="container">
|
||||
<p>
|
||||
|
@ -283,23 +309,32 @@ impl Component for LoginApp {
|
|||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
ConsoleService::log("create");
|
||||
|
||||
// First we need to work out what state we are in.
|
||||
let lstorage = StorageService::new(yew::services::storage::Area::Local).unwrap();
|
||||
// Assume we are here for a good reason.
|
||||
models::clear_bearer_token();
|
||||
let state = LoginState::Init(true);
|
||||
|
||||
/*
|
||||
// Get any previous sessions?
|
||||
let prev_session: Result<String, _> = lstorage.restore("kanidm_bearer_token");
|
||||
let state = if let Some(prev_session) = models::get_bearer_token() {
|
||||
// Are they still valid?
|
||||
|
||||
ConsoleService::log(format!("prev_session -> {:?}", prev_session).as_str());
|
||||
// If so set loginState to LoginState::Authenticated, and pop
|
||||
// our return location (if possible).
|
||||
LoginState::Authenticated
|
||||
} else {
|
||||
// Init a new login session.
|
||||
LoginState::Init(true)
|
||||
};
|
||||
*/
|
||||
|
||||
// Are they still valid?
|
||||
// startConfetti();
|
||||
|
||||
LoginApp {
|
||||
link,
|
||||
inputvalue: "".to_string(),
|
||||
lstorage,
|
||||
ft: None,
|
||||
session_id: "".to_string(),
|
||||
state: LoginState::Init(true),
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,7 +390,7 @@ impl Component for LoginApp {
|
|||
LoginAppMsg::TotpSubmit => {
|
||||
ConsoleService::log("totp");
|
||||
// Disable the button?
|
||||
match u32::from_str_radix(&self.inputvalue, 10) {
|
||||
match self.inputvalue.parse::<u32>() {
|
||||
Ok(totp) => {
|
||||
self.state = LoginState::Totp(TotpState::Disabled);
|
||||
let authreq = AuthRequest {
|
||||
|
@ -464,9 +499,8 @@ impl Component for LoginApp {
|
|||
}
|
||||
AuthState::Success(bearer_token) => {
|
||||
// Store the bearer here!
|
||||
self.lstorage.store("kanidm_bearer_token", Ok(bearer_token));
|
||||
models::set_bearer_token(bearer_token);
|
||||
self.state = LoginState::Authenticated;
|
||||
startConfetti();
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -483,6 +517,7 @@ impl Component for LoginApp {
|
|||
|
||||
fn view(&self) -> Html {
|
||||
// How do we add a top level theme?
|
||||
/*
|
||||
let (width, height): (u32, u32) = if let Some(win) = web_sys::window() {
|
||||
let w = win.inner_width().unwrap();
|
||||
let h = win.inner_height().unwrap();
|
||||
|
@ -493,25 +528,33 @@ impl Component for LoginApp {
|
|||
ConsoleService::log("Unable to access document window");
|
||||
(0, 0)
|
||||
};
|
||||
let (width, height) = (width.to_string(), height.to_string());
|
||||
*/
|
||||
|
||||
// <canvas id="confetti-canvas" style="position:absolute" width=width height=height></canvas>
|
||||
html! {
|
||||
<div>
|
||||
<canvas id="confetti-canvas" style="position:absolute" width=width height=height></canvas>
|
||||
<div id="content" class="container">
|
||||
<div class="row d-flex justify-content-center align-items-center" style="min-height: 100vh;">
|
||||
<div class="col">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="container">
|
||||
<h2>{ "Kanidm Alpha 🦀 " }</h2>
|
||||
</div>
|
||||
{ self.view_state() }
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
<body class="html-body form-body">
|
||||
<main class="form-signin">
|
||||
<div class="container">
|
||||
<h2>{ "Kanidm Alpha 🦀 " }</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ self.view_state() }
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _first_render: bool) {
|
||||
// Once rendered if an element with id autofocus exists, focus it.
|
||||
let doc = yew::utils::document();
|
||||
if let Some(element) = doc.get_element_by_id("autofocus") {
|
||||
if let Ok(htmlelement) = element.dyn_into::<web_sys::HtmlElement>() {
|
||||
if htmlelement.focus().is_err() {
|
||||
ConsoleService::log("unable to autofocus.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleService::log("login::rendered");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import init, { run_login_app } from '../pkg/kanidmd_web_ui.js';
|
||||
import init, { run_app } from '../pkg/kanidmd_web_ui.js';
|
||||
async function main() {
|
||||
await init('/pkg/kanidmd_web_ui_bg.wasm');
|
||||
run_login_app();
|
||||
run_app();
|
||||
}
|
||||
main()
|
||||
|
|
121
kanidmd_web_ui/src/manager.rs
Normal file
121
kanidmd_web_ui/src/manager.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
//! This is the top level router of the web ui for kanidm. It decides based on the incoming
|
||||
//! request, where to direct this too, and if the requirements for that request have been
|
||||
//! met before rendering. For example, if you land here with an oauth request, but you are
|
||||
//! not atuhenticated, this will determine that and send you to authentication first, then
|
||||
//! will allow you to proceed with the oauth flow.
|
||||
|
||||
use yew::prelude::*;
|
||||
use yew_services::ConsoleService;
|
||||
|
||||
use yew_router::prelude::*;
|
||||
use yew_router::router::Router;
|
||||
|
||||
use crate::login::LoginApp;
|
||||
use crate::oauth2::Oauth2App;
|
||||
use crate::views::ViewsApp;
|
||||
|
||||
// router to decide on state.
|
||||
#[derive(Routable, PartialEq, Clone, Debug)]
|
||||
pub enum Route {
|
||||
#[at("/")]
|
||||
Landing,
|
||||
|
||||
#[at("/ui/view")]
|
||||
Index,
|
||||
|
||||
#[at("/ui/login")]
|
||||
Login,
|
||||
|
||||
#[at("/ui/oauth2")]
|
||||
Oauth2,
|
||||
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
fn switch(routes: &Route) -> Html {
|
||||
ConsoleService::log("manager::switch");
|
||||
match routes {
|
||||
Route::Landing => {
|
||||
yew_router::push_route(Route::Index);
|
||||
html! { <body></body> }
|
||||
}
|
||||
Route::Index => html! { <ViewsApp /> },
|
||||
Route::Login => html! { <LoginApp /> },
|
||||
Route::Oauth2 => html! { <Oauth2App /> },
|
||||
Route::NotFound => {
|
||||
html! {
|
||||
<body>
|
||||
<h1>{ "404" }</h1>
|
||||
<Link<Route> route=Route::Index>
|
||||
{ "Home" }
|
||||
</Link<Route>>
|
||||
</body>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ManagerApp {
|
||||
link: ComponentLink<Self>,
|
||||
is_ready: bool,
|
||||
}
|
||||
|
||||
impl Component for ManagerApp {
|
||||
type Message = bool;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
ConsoleService::log("manager::create");
|
||||
ManagerApp {
|
||||
link,
|
||||
is_ready: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||
ConsoleService::log("manager::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
ConsoleService::log("manager::update");
|
||||
self.is_ready = msg;
|
||||
true
|
||||
}
|
||||
|
||||
fn rendered(&mut self, first_render: bool) {
|
||||
ConsoleService::log("manager::rendered");
|
||||
if first_render {
|
||||
// Can only access the current_route AFTER it renders.
|
||||
// ConsoleService::log(format!("{:?}", yew_router::current_route::<Route>()).as_str())
|
||||
self.link.send_message(first_render)
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>{ "Kanidm" }</title>
|
||||
<link rel="stylesheet" href="/pkg/external/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"/>
|
||||
<link rel="stylesheet" href="/pkg/style.css"/>
|
||||
<script src="/pkg/external/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"></script>
|
||||
<script src="/pkg/external/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"></script>
|
||||
<script src="/pkg/external/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"></script>
|
||||
<script src="/pkg/external/confetti.js"></script>
|
||||
</head>
|
||||
|
||||
{
|
||||
if self.is_ready {
|
||||
html! {<Router<Route> render=Router::render(switch) /> }
|
||||
} else {
|
||||
html! { <body></body> }
|
||||
}
|
||||
}
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
89
kanidmd_web_ui/src/models/mod.rs
Normal file
89
kanidmd_web_ui/src/models/mod.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use yew::format::Json;
|
||||
use yew_services::storage;
|
||||
use yew_services::{ConsoleService, StorageService};
|
||||
|
||||
use kanidm_proto::oauth2::AuthorisationRequest;
|
||||
|
||||
use crate::manager::Route;
|
||||
|
||||
fn get_persistent_storage() -> StorageService {
|
||||
StorageService::new(storage::Area::Local)
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("lstorage error -> {:?}", e);
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn get_bearer_token() -> Option<String> {
|
||||
let lstorage = get_persistent_storage();
|
||||
|
||||
let prev_session: Result<String, _> = lstorage.restore("kanidm_bearer_token");
|
||||
ConsoleService::log(format!("prev_session -> {:?}", prev_session).as_str());
|
||||
|
||||
prev_session.ok()
|
||||
}
|
||||
|
||||
pub fn set_bearer_token(bearer_token: String) {
|
||||
let mut lstorage = get_persistent_storage();
|
||||
lstorage.store("kanidm_bearer_token", Ok(bearer_token));
|
||||
}
|
||||
|
||||
pub fn clear_bearer_token() {
|
||||
let mut lstorage = get_persistent_storage();
|
||||
lstorage.remove("kanidm_bearer_token");
|
||||
}
|
||||
|
||||
fn get_temporary_storage() -> StorageService {
|
||||
StorageService::new(storage::Area::Session)
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("tstorage error -> {:?}", e);
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum Location {
|
||||
Oauth2,
|
||||
Views,
|
||||
}
|
||||
|
||||
impl From<Location> for Route {
|
||||
fn from(l: Location) -> Self {
|
||||
match l {
|
||||
Location::Views => Route::Index,
|
||||
Location::Oauth2 => Route::Oauth2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_return_location(l: Location) {
|
||||
let mut tstorage = get_temporary_storage();
|
||||
tstorage.store("return_location", Json(&l));
|
||||
}
|
||||
|
||||
pub fn pop_return_location() -> Location {
|
||||
let mut tstorage = get_temporary_storage();
|
||||
|
||||
let l: Json<Result<Location, _>> = tstorage.restore("return_location");
|
||||
ConsoleService::log(format!("return_location -> {:?}", l).as_str());
|
||||
tstorage.remove("return_location");
|
||||
|
||||
l.into_inner().ok().unwrap_or(Location::Views)
|
||||
}
|
||||
|
||||
pub fn push_oauth2_authorisation_request(r: AuthorisationRequest) {
|
||||
let mut tstorage = get_temporary_storage();
|
||||
tstorage.store("oauth2_authorisation_request", Json(&r));
|
||||
}
|
||||
|
||||
pub fn pop_oauth2_authorisation_request() -> Option<AuthorisationRequest> {
|
||||
let mut tstorage = get_temporary_storage();
|
||||
|
||||
let l: Json<Result<AuthorisationRequest, _>> = tstorage.restore("oauth2_authorisation_request");
|
||||
ConsoleService::log(format!("oauth2_authorisation_request -> {:?}", l).as_str());
|
||||
tstorage.remove("oauth2_authorisation_request");
|
||||
|
||||
l.into_inner().ok()
|
||||
}
|
360
kanidmd_web_ui/src/oauth2.rs
Normal file
360
kanidmd_web_ui/src/oauth2.rs
Normal file
|
@ -0,0 +1,360 @@
|
|||
use anyhow::Error;
|
||||
use yew::format::{Json, Nothing};
|
||||
use yew::prelude::*;
|
||||
use yew_services::fetch::{FetchOptions, FetchService, FetchTask, Redirect, Request, Response};
|
||||
use yew_services::ConsoleService;
|
||||
|
||||
use crate::manager::Route;
|
||||
use crate::models;
|
||||
|
||||
pub use kanidm_proto::oauth2::{
|
||||
AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod,
|
||||
ConsentRequest, ErrorResponse,
|
||||
};
|
||||
|
||||
enum State {
|
||||
// We don't have a token, or something is invalid.
|
||||
LoginRequired,
|
||||
// We are in the process of check the auth token to be sure we can proceed.
|
||||
TokenCheck(String, FetchTask),
|
||||
// Token check done, lets do it.
|
||||
SubmitAuthReq(String, FetchTask),
|
||||
Consent(String, ConsentRequest),
|
||||
ConsentGranted(FetchTask),
|
||||
ErrInvalidRequest,
|
||||
}
|
||||
|
||||
pub struct Oauth2App {
|
||||
link: ComponentLink<Self>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
pub enum Oauth2Msg {
|
||||
LoginProceed,
|
||||
ConsentGranted,
|
||||
TokenValid,
|
||||
Consent(ConsentRequest),
|
||||
Redirect(String),
|
||||
Error { emsg: String, kopid: Option<String> },
|
||||
}
|
||||
|
||||
impl Oauth2App {
|
||||
fn fetch_token_valid(token: &str, link: &ComponentLink<Self>) -> Result<FetchTask, String> {
|
||||
let callback = link.callback(move |response: Response<Result<String, Error>>| {
|
||||
let (parts, body) = response.into_parts();
|
||||
|
||||
if parts.status.is_success() {
|
||||
Oauth2Msg::TokenValid
|
||||
} else if parts.status == 401 {
|
||||
Oauth2Msg::LoginProceed
|
||||
} else {
|
||||
Oauth2Msg::Error {
|
||||
emsg: body.unwrap_or_else(|_| "".to_string()),
|
||||
kopid: parts
|
||||
.headers
|
||||
.get("x-kanidm-opid")
|
||||
.map(|id| id.to_str().unwrap().to_string()),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Request::get("/v1/auth/valid")
|
||||
.header("content-type", "application/json")
|
||||
.header("authorization", format!("Bearer {}", token))
|
||||
.body(Nothing)
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|request| {
|
||||
FetchService::fetch(request, callback).map_err(|e| format!("{:?}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_authreq(
|
||||
token: &str,
|
||||
authreq: &AuthorisationRequest,
|
||||
link: &ComponentLink<Self>,
|
||||
) -> Result<FetchTask, String> {
|
||||
let callback = link.callback(
|
||||
move |response: Response<Json<Result<ConsentRequest, Error>>>| {
|
||||
let (parts, body) = response.into_parts();
|
||||
|
||||
match body {
|
||||
Json(Ok(state)) => Oauth2Msg::Consent(state),
|
||||
Json(Err(e)) => Oauth2Msg::Error {
|
||||
emsg: format!("{:?}", e),
|
||||
kopid: parts
|
||||
.headers
|
||||
.get("x-kanidm-opid")
|
||||
.map(|id| id.to_str().unwrap().to_string()),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Request::post("/oauth2/authorise")
|
||||
.header("content-type", "application/json")
|
||||
.header("authorization", format!("Bearer {}", token))
|
||||
.body(Json(authreq))
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|request| {
|
||||
FetchService::fetch_binary(request, callback).map_err(|e| format!("{:?}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_consent_token(
|
||||
token: &str,
|
||||
consent_token: String,
|
||||
link: &ComponentLink<Self>,
|
||||
) -> Result<FetchTask, String> {
|
||||
let callback = link.callback(move |response: Response<Result<Vec<u8>, Error>>| {
|
||||
let (parts, _body) = response.into_parts();
|
||||
|
||||
let kopid = parts
|
||||
.headers
|
||||
.get("x-kanidm-opid")
|
||||
.map(|id| id.to_str().unwrap().to_string());
|
||||
|
||||
if parts.status == 200 {
|
||||
if let Some(loc) = parts
|
||||
.headers
|
||||
.get("location")
|
||||
.and_then(|hv| hv.to_str().ok().map(str::to_string))
|
||||
{
|
||||
Oauth2Msg::Redirect(loc)
|
||||
} else {
|
||||
Oauth2Msg::Error {
|
||||
emsg: "no location header".to_string(),
|
||||
kopid,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Oauth2Msg::Error {
|
||||
emsg: "Redirect error".to_string(),
|
||||
kopid,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let options = FetchOptions {
|
||||
cache: None,
|
||||
credentials: None,
|
||||
redirect: Some(Redirect::Manual),
|
||||
mode: None,
|
||||
referrer: None,
|
||||
referrer_policy: None,
|
||||
integrity: None,
|
||||
};
|
||||
|
||||
Request::post("/oauth2/authorise/permit")
|
||||
.header("content-type", "application/json")
|
||||
.header("authorization", format!("Bearer {}", token))
|
||||
.body(Json(&consent_token))
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|request| {
|
||||
FetchService::fetch_binary_with_options(request, options, callback)
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Oauth2App {
|
||||
type Message = Oauth2Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
ConsoleService::log("oauth2::create");
|
||||
|
||||
// Do we have a query here?
|
||||
// Did we get sent a valid Oauth2 request?
|
||||
let query: Option<AuthorisationRequest> = yew_router::parse_query()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("lstorage error -> {:?}", e);
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
})
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
ConsoleService::log("pop_oauth2_authorisation_request");
|
||||
models::pop_oauth2_authorisation_request()
|
||||
});
|
||||
|
||||
// If we have neither we need to say that we can not proceed at all.
|
||||
let query = match query {
|
||||
Some(q) => q,
|
||||
None => {
|
||||
return Oauth2App {
|
||||
link,
|
||||
state: State::ErrInvalidRequest,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let e_msg = format!("{:?}", query);
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
|
||||
// Push the request down. This covers if we move to LoginRequired.
|
||||
models::push_oauth2_authorisation_request(query);
|
||||
|
||||
match models::get_bearer_token() {
|
||||
Some(token) => {
|
||||
// Start the fetch req.
|
||||
// Put the fetch handle into the consent type.
|
||||
match Self::fetch_token_valid(token.as_str(), &link) {
|
||||
Ok(ft) => Oauth2App {
|
||||
link,
|
||||
state: State::TokenCheck(token, ft),
|
||||
},
|
||||
Err(e_msg) => {
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
Oauth2App {
|
||||
link,
|
||||
state: State::ErrInvalidRequest,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Oauth2App {
|
||||
link,
|
||||
state: State::LoginRequired,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||
ConsoleService::log("oauth2::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
ConsoleService::log("oauth2::update");
|
||||
|
||||
match msg {
|
||||
Oauth2Msg::LoginProceed => {
|
||||
models::push_return_location(models::Location::Oauth2);
|
||||
yew_router::push_route(Route::Login);
|
||||
// Don't need to redraw as we are yolo-ing out.
|
||||
false
|
||||
}
|
||||
Oauth2Msg::TokenValid => {
|
||||
// Okay we can proceed, pop the query.
|
||||
let ar = models::pop_oauth2_authorisation_request();
|
||||
|
||||
self.state = match (&self.state, ar) {
|
||||
(State::TokenCheck(token, _), Some(ar)) => {
|
||||
match Self::fetch_authreq(&token, &ar, &self.link) {
|
||||
Ok(ft) => State::SubmitAuthReq(token.clone(), ft),
|
||||
Err(e_msg) => {
|
||||
ConsoleService::log(e_msg.as_str());
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ConsoleService::log("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
true
|
||||
}
|
||||
Oauth2Msg::Consent(consent_req) => {
|
||||
self.state = match &self.state {
|
||||
State::SubmitAuthReq(token, _) => State::Consent(token.clone(), consent_req),
|
||||
_ => {
|
||||
ConsoleService::log("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
true
|
||||
}
|
||||
Oauth2Msg::ConsentGranted => {
|
||||
self.state = match &self.state {
|
||||
State::Consent(token, consent_req) => {
|
||||
match Self::fetch_consent_token(
|
||||
&token,
|
||||
consent_req.consent_token.clone(),
|
||||
&self.link,
|
||||
) {
|
||||
Ok(ft) => State::ConsentGranted(ft),
|
||||
Err(e) => {
|
||||
ConsoleService::log(e.as_str());
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ConsoleService::log("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
// We need to send off fetch task here.
|
||||
true
|
||||
}
|
||||
Oauth2Msg::Error { emsg, kopid } => {
|
||||
self.state = State::ErrInvalidRequest;
|
||||
ConsoleService::log(format!("{:?}", kopid).as_str());
|
||||
ConsoleService::log(emsg.as_str());
|
||||
true
|
||||
}
|
||||
Oauth2Msg::Redirect(loc) => {
|
||||
ConsoleService::log(format!("Redirecting to {}", loc).as_str());
|
||||
// Send the location here, and then update will trigger the redir via
|
||||
// https://docs.rs/web-sys/0.3.51/web_sys/struct.Location.html#method.replace
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
|
||||
let location = yew::utils::window().location();
|
||||
match location.replace(loc.as_str()) {
|
||||
// No need to redraw, we are leaving.
|
||||
Ok(_) => false,
|
||||
Err(e) => {
|
||||
// Something went bang, opps.
|
||||
ConsoleService::log(format!("{:?}", e).as_str());
|
||||
self.state = State::ErrInvalidRequest;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _first_render: bool) {
|
||||
ConsoleService::log("oauth2::rendered");
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
match &self.state {
|
||||
State::LoginRequired => {
|
||||
html! {
|
||||
<body class="html-body form-body">
|
||||
<main class="form-signin">
|
||||
<form>
|
||||
<h1 class="h3 mb-3 fw-normal">{" Sign in to proceed" }</h1>
|
||||
<button class="w-100 btn btn-lg btn-primary" type="submit" onclick=self.link.callback(|_| Oauth2Msg::LoginProceed)>{ "Sign in" }</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
}
|
||||
State::Consent(_, query) => {
|
||||
let client_name = query.client_name.clone();
|
||||
|
||||
html! {
|
||||
<body class="html-body form-body">
|
||||
<main class="form-signin">
|
||||
<form>
|
||||
<h1 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ client_name }</h1>
|
||||
<button class="w-100 btn btn-lg btn-primary" type="submit" onclick=self.link.callback(|_| Oauth2Msg::ConsentGranted)>{ "Proceed" }</button>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
}
|
||||
State::ConsentGranted(_) | State::SubmitAuthReq(_, _) | State::TokenCheck(_, _) => {
|
||||
html! { <body> <h1>{ " ... " }</h1> </body> }
|
||||
}
|
||||
State::ErrInvalidRequest => {
|
||||
html! { <body> <h1>{ " ❌ " }</h1> </body> }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&mut self) {
|
||||
ConsoleService::log("oauth2::destroy");
|
||||
}
|
||||
}
|
141
kanidmd_web_ui/src/style.css
Normal file
141
kanidmd_web_ui/src/style.css
Normal file
|
@ -0,0 +1,141 @@
|
|||
.html-body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.form-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
width: 100%;
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
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: .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, .1);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
top: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-sticky {
|
||||
position: relative;
|
||||
top: 0;
|
||||
height: calc(100vh - 48px);
|
||||
padding-top: .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: .75rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/*
|
||||
* Navbar
|
||||
*/
|
||||
|
||||
.navbar-brand {
|
||||
padding-top: .75rem;
|
||||
padding-bottom: .75rem;
|
||||
font-size: 1rem;
|
||||
background-color: rgba(0, 0, 0, .25);
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
.navbar .navbar-toggler {
|
||||
top: .25rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.navbar .form-control {
|
||||
padding: .75rem 1rem;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.form-control-dark {
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
border-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.form-control-dark:focus {
|
||||
border-color: transparent;
|
||||
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||
}
|
136
kanidmd_web_ui/src/views/mod.rs
Normal file
136
kanidmd_web_ui/src/views/mod.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
use yew::prelude::*;
|
||||
use yew_services::ConsoleService;
|
||||
|
||||
use crate::models;
|
||||
|
||||
use crate::manager::Route;
|
||||
|
||||
enum State {
|
||||
LoginRequired,
|
||||
Authenticated(String),
|
||||
}
|
||||
|
||||
pub struct ViewsApp {
|
||||
link: ComponentLink<Self>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
pub enum ViewsMsg {
|
||||
Logout,
|
||||
}
|
||||
|
||||
impl Component for ViewsApp {
|
||||
type Message = ViewsMsg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
ConsoleService::log("views::create");
|
||||
|
||||
let state = models::get_bearer_token()
|
||||
.map(State::Authenticated)
|
||||
.unwrap_or(State::LoginRequired);
|
||||
|
||||
ViewsApp { link, state }
|
||||
}
|
||||
|
||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||
ConsoleService::log("views::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
ConsoleService::log("views::update");
|
||||
match msg {
|
||||
ViewsMsg::Logout => {
|
||||
models::clear_bearer_token();
|
||||
self.state = State::LoginRequired;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _first_render: bool) {
|
||||
ConsoleService::log("views::rendered");
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
match self.state {
|
||||
State::LoginRequired => {
|
||||
models::push_return_location(models::Location::Views);
|
||||
yew_router::push_route(Route::Login);
|
||||
html! { <div></div> }
|
||||
}
|
||||
State::Authenticated(_) => self.view_authenticated(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewsApp {
|
||||
fn view_authenticated(&self) -> Html {
|
||||
html! {
|
||||
<body class="dash-body">
|
||||
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
||||
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">{ "Kanidm" }</a>
|
||||
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-nav">
|
||||
<div class="nav-item text-nowrap">
|
||||
<a class="nav-link px-3" href="#" onclick=self.link.callback(|_| ViewsMsg::Logout) >{ "Sign out" }</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
|
||||
<div class="position-sticky pt-3">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">
|
||||
<span data-feather="home"></span>
|
||||
{ "Account" }
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
<h2>{ "Section title" }</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{ "#" }</th>
|
||||
<th scope="col">{ "Header" }</th>
|
||||
<th scope="col">{ "Header" }</th>
|
||||
<th scope="col">{ "Header" }</th>
|
||||
<th scope="col">{ "Header" }</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{ "1,001" }</td>
|
||||
<td>{ "random" }</td>
|
||||
<td>{ "data" }</td>
|
||||
<td>{ "placeholder" }</td>
|
||||
<td>{ "text" }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{ "1,015" }</td>
|
||||
<td>{ "random" }</td>
|
||||
<td>{ "tabular" }</td>
|
||||
<td>{ "information" }</td>
|
||||
<td>{ "text" }</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue