mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
A pile of Wasm UI tweaks (#958)
This commit is contained in:
parent
c0cca979d9
commit
845cabb206
4853
Cargo.lock
generated
Normal file
4853
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
@ -44,3 +44,14 @@ webauthn-authenticator-rs = { git = "https://github.com/kanidm/webauthn-rs.git",
|
|||
# compact_jwt = { path = "../compact_jwt" }
|
||||
# compact_jwt = { git = "https://github.com/kanidm/compact-jwt.git" }
|
||||
|
||||
|
||||
# enshrinken the WASMs
|
||||
[profile.release.package.kanidmd_web_ui]
|
||||
# optimization over all codebase ( better optimization, slower build )
|
||||
codegen-units = 1
|
||||
# optimization for size ( more aggressive )
|
||||
opt-level = 'z'
|
||||
# optimization for size
|
||||
# opt-level = 's'
|
||||
# link time optimization using using whole-program analysis
|
||||
# lto = true
|
||||
|
|
12
examples/apache_oauth/Dockerfile
Normal file
12
examples/apache_oauth/Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM ubuntu/apache2:latest
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y \
|
||||
libapache2-mod-auth-openidc \
|
||||
apache2-utils
|
||||
|
||||
RUN a2enmod auth_openidc
|
||||
RUN a2enmod ssl
|
||||
RUN rm /etc/apache2/sites-enabled/000-default.conf
|
||||
COPY index.html /var/www/html/index.html
|
||||
COPY oauth2.conf /etc/apache2/sites-enabled/oauth2.conf
|
32
examples/apache_oauth/Makefile
Normal file
32
examples/apache_oauth/Makefile
Normal file
|
@ -0,0 +1,32 @@
|
|||
# set the following environment variables
|
||||
# OAUTH_HOSTNAME - the hostname you'll be exposing this as
|
||||
# OAUTH_PORT - the external port this'll be running on (sets it in --publish)
|
||||
# this is 8553, but ... things get weird if the stack doesn't end up being accessed
|
||||
# through 443, eg via a tunneled proxy
|
||||
# KANIDM_HOSTNAME - the hostname of the Kanidm instance
|
||||
# KANIDM_PORT - if you're running it on a different port
|
||||
# KANIDM_CLIENT_SECRET - the client secret for the RP in Kanidm's OAuth config
|
||||
KANIDM_PORT ?= 443
|
||||
OAUTH_PORT ?= 8553
|
||||
|
||||
.DEFAULT: build_and_run
|
||||
|
||||
.PHONY: build_and_run
|
||||
build_and_run: build run
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
docker build -t kanidm_oauth_test:latest .
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
docker rm -f kanidm_oauth_test
|
||||
docker run --rm -it \
|
||||
--env OAUTH_HOSTNAME=$(OAUTH_HOSTNAME) \
|
||||
--env KANIDM_HOSTNAME=$(KANIDM_HOSTNAME) \
|
||||
--env KANIDM_PORT=$(KANIDM_PORT) \
|
||||
--env KANIDM_CLIENT_SECRET=$(KANIDM_CLIENT_SECRET) \
|
||||
--volume /tmp/kanidm/:/certs/ \
|
||||
--publish "$(OAUTH_PORT):443" \
|
||||
--name kanidm_oauth_test \
|
||||
kanidm_oauth_test:latest
|
16
examples/apache_oauth/README.md
Normal file
16
examples/apache_oauth/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Apache OAuth config example
|
||||
|
||||
This example is here mainly for devs to come up with super complicated ways to test the changes they're
|
||||
making which affect OAuth things.
|
||||
|
||||
## Example of how to run it
|
||||
|
||||
```shell
|
||||
OAUTH_HOSTNAME=test-oauth2.example.com \
|
||||
KANIDM_HOSTNAME=test-kanidm.example.com \
|
||||
KANIDM_CLIENT_SECRET=1234Hq5d1J5GG9VNae3bRMFGDVFR3bUyyXg3RPRSefJLNhee \
|
||||
KANIDM_PORT=443 \
|
||||
make
|
||||
```
|
||||
|
||||
This'll build and run the docker container.
|
15
examples/apache_oauth/index.html
Normal file
15
examples/apache_oauth/index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Kanidm OAuth Test Page</title>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi there!</h1>
|
||||
<p>
|
||||
Looks like you've successfully authenticated to the page, welcome!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
46
examples/apache_oauth/oauth2.conf
Normal file
46
examples/apache_oauth/oauth2.conf
Normal file
|
@ -0,0 +1,46 @@
|
|||
# this is a template file to set up an oauth2 RP for testing
|
||||
# LoadModule auth_openidc_module /usr/lib/apache2/modules/mod_auth_openidc.so
|
||||
LogLevel debug
|
||||
ErrorLog /dev/stderr
|
||||
TransferLog /dev/stdout
|
||||
<IfModule mod_ssl.c>
|
||||
<VirtualHost *:443>
|
||||
ServerName localhost
|
||||
|
||||
SSLEngine on
|
||||
# use the results from insecure_generate_certs.sh - super handy
|
||||
SSLCertificateFile /certs/cert.pem
|
||||
SSLCertificateKeyFile /certs/key.pem
|
||||
|
||||
# since we're using a self-signed cert we have to disable this
|
||||
OIDCSSLValidateServer Off
|
||||
OIDCProviderMetadataURL https://${KANIDM_HOSTNAME}:${KANIDM_PORT}/oauth2/openid/test_rp/.well-known/openid-configuration
|
||||
OIDCClientID test_rp
|
||||
OIDCClientSecret ${KANIDM_CLIENT_SECRET}
|
||||
OIDCUserInfoTokenMethod authz_header
|
||||
OIDCPKCEMethod S256
|
||||
OIDCCookieSameSite On
|
||||
|
||||
# Define the OpenID Connect scope that is requested from the OP (eg. "openid email profile").
|
||||
# When not defined, the bare minimal scope "openid" is used.
|
||||
# OIDCScope "openid"
|
||||
# OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content
|
||||
OIDCRedirectURI https://${OAUTH_HOSTNAME}/redirect_url
|
||||
OIDCCryptoPassphrase Th1sIsA5uperS3cretP4ssphraseD0ntT3llTh3Cr4bz
|
||||
|
||||
OIDCUserInfoRefreshInterval 300
|
||||
OIDCJWKSRefreshInterval 300
|
||||
|
||||
OIDCSessionInactivityTimeout 300
|
||||
OIDCSessionType client-cookie:persistent
|
||||
|
||||
# preferred_username
|
||||
OIDCRemoteUserClaim preferred_username
|
||||
|
||||
<Location />
|
||||
AuthType openid-connect
|
||||
Require valid-user
|
||||
</Location>
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
|
@ -1110,23 +1110,23 @@ async fn credential_update_exec(
|
|||
CUAction::PasskeyRemove => {
|
||||
// TODO: make this a scrollable selector with a "cancel" option as the default
|
||||
match client
|
||||
.idm_account_credential_update_status(&session_token)
|
||||
.await
|
||||
{
|
||||
Ok(status) => {
|
||||
if status.passkeys.is_empty() {
|
||||
println!("No passkeys are configured for this user");
|
||||
return
|
||||
}
|
||||
println!("Current passkeys:");
|
||||
for pk in status.passkeys {
|
||||
println!(" {} ({})", pk.tag, pk.uuid);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("An error occured pulling existing credentials -> {:?}", e);
|
||||
.idm_account_credential_update_status(&session_token)
|
||||
.await
|
||||
{
|
||||
Ok(status) => {
|
||||
if status.passkeys.is_empty() {
|
||||
println!("No passkeys are configured for this user");
|
||||
return;
|
||||
}
|
||||
println!("Current passkeys:");
|
||||
for pk in status.passkeys {
|
||||
println!(" {} ({})", pk.tag, pk.uuid);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("An error occured pulling existing credentials -> {:?}", e);
|
||||
}
|
||||
}
|
||||
let uuid_s: String = Input::new()
|
||||
.with_prompt("\nEnter the UUID of the Passkey to remove (blank to stop) # ")
|
||||
.validate_with(|input: &String| -> Result<(), &str> {
|
||||
|
|
|
@ -18,7 +18,8 @@ RUN zypper install -y \
|
|||
sqlite3-devel \
|
||||
sccache \
|
||||
gcc \
|
||||
rsync
|
||||
rsync \
|
||||
which
|
||||
RUN zypper clean -a
|
||||
|
||||
COPY . /usr/src/kanidm
|
||||
|
@ -45,7 +46,7 @@ RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
|||
export RUSTC_WRAPPER=sccache && \
|
||||
sccache --start-server; \
|
||||
fi && \
|
||||
./build_wasm_dev.sh
|
||||
./build_wasm.sh
|
||||
|
||||
WORKDIR /usr/src/kanidm/kanidmd/daemon
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@ impl Oauth2ResourceServersReadTransaction {
|
|||
if auth_req.redirect_uri.origin() != o2rs.origin {
|
||||
admin_warn!(
|
||||
origin = ?o2rs.origin,
|
||||
"Invalid oauth2 redirect_uri (must be related to origin of)"
|
||||
"Invalid oauth2 redirect_uri (must be related to origin of) - got {:?}", auth_req.redirect_uri.origin()
|
||||
);
|
||||
return Err(Oauth2Error::InvalidOrigin);
|
||||
}
|
||||
|
|
|
@ -1897,7 +1897,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
.ok_or(OperationError::InvalidRequestState)
|
||||
.and_then(|session| session.totp_accept_sha1(&origin, &aste.target))
|
||||
.map_err(|e| {
|
||||
admin_error!("Failed to accept sha1 totp {:?}", e);
|
||||
admin_error!("Failed to accept SHA1 TOTP {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
@ -1913,7 +1913,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
})?;
|
||||
// reg the token
|
||||
let modlist = session.account.gen_totp_mod(token).map_err(|e| {
|
||||
admin_error!("Failed to gen totp mod {:?}", e);
|
||||
admin_error!("Failed to gen TOTP mod {:?}", e);
|
||||
e
|
||||
})?;
|
||||
// Perform the mod
|
||||
|
|
|
@ -441,7 +441,9 @@ pub fn domain_rename_core(config: &Configuration) {
|
|||
}
|
||||
|
||||
let qs_write = task::block_on(qs.write_async(duration_from_epoch_now()));
|
||||
let r = qs_write.domain_rename(new_domain_name).and_then(|_| qs_write.commit());
|
||||
let r = qs_write
|
||||
.domain_rename(new_domain_name)
|
||||
.and_then(|_| qs_write.commit());
|
||||
|
||||
match r {
|
||||
Ok(_) => info!("Domain Rename Success!"),
|
||||
|
|
|
@ -13,42 +13,29 @@ documentation = "https://docs.rs/kanidm/latest/kanidm/"
|
|||
homepage = "https://github.com/kanidm/kanidm/"
|
||||
repository = "https://github.com/kanidm/kanidm/"
|
||||
|
||||
[profile.release]
|
||||
# These are ignored because the crate is in a workspace
|
||||
#[profile.release]
|
||||
# less code to include into binary
|
||||
panic = 'abort'
|
||||
# optimization over all codebase ( better optimization, slower build )
|
||||
codegen-units = 1
|
||||
# optimization for size ( more aggressive )
|
||||
opt-level = 'z'
|
||||
# optimization for size
|
||||
# opt-level = 's'
|
||||
# link time optimization using using whole-program analysis
|
||||
lto = true
|
||||
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "^1.0.139", features = ["derive"] }
|
||||
serde_json = "^1.0.82"
|
||||
|
||||
wasm-bindgen = { version = "^0.2.81", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = { version = "^0.4.30" }
|
||||
kanidm_proto = { path = "../kanidm_proto", features = ["wasm"] }
|
||||
|
||||
qrcode = { version = "^0.12.0", default-features = false, features = ["svg"] }
|
||||
|
||||
yew-router = "^0.16.0"
|
||||
yew = "^0.19.3"
|
||||
yew-agent = "^0.1.0"
|
||||
gloo = "^0.8.0"
|
||||
js-sys = "^0.3.58"
|
||||
|
||||
uuid = "^1.1.2"
|
||||
|
||||
compact_jwt = { version = "^0.2.3", default-features = false, features = ["unsafe_release_without_verify"] }
|
||||
# compact_jwt = { path = "../../compact_jwt" , default-features = false, features = ["unsafe_release_without_verify"] }
|
||||
|
||||
gloo = "^0.8.0"
|
||||
js-sys = "^0.3.58"
|
||||
kanidm_proto = { path = "../kanidm_proto", features = ["wasm"] }
|
||||
qrcode = { version = "^0.12.0", default-features = false, features = ["svg"] }
|
||||
serde = { version = "^1.0.139", features = ["derive"] }
|
||||
serde_json = "^1.0.82"
|
||||
uuid = "^1.1.2"
|
||||
wasm-bindgen = { version = "^0.2.81", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = { version = "^0.4.30" }
|
||||
yew = "^0.19.3"
|
||||
yew-agent = "^0.1.0"
|
||||
yew-router = "^0.16.0"
|
||||
wee_alloc = "^0.4.5"
|
||||
|
||||
[dependencies.web-sys]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
build_wasm_release.sh
|
33
kanidmd_web_ui/build_wasm.sh
Executable file
33
kanidmd_web_ui/build_wasm.sh
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This builds the assets for the Web UI, defaulting to a release build.
|
||||
|
||||
if [ ! -f build_wasm.sh ]; then
|
||||
echo "Please run from the crate directory. (kanidmd_web_ui)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${BUILD_FLAGS}" ]; then
|
||||
BUILD_FLAGS="--release --no-typescript"
|
||||
fi
|
||||
|
||||
if [ -z "$(which rsync)" ]; then
|
||||
echo "Cannot find rsync which is needed to move things around, quitting!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(find ./pkg/ -name 'kanidmd*' | wc -l)" -gt 0 ]; then
|
||||
echo "Cleaning up"
|
||||
rm pkg/kanidmd*
|
||||
fi
|
||||
|
||||
# we can disable this since we want it to expand
|
||||
# shellcheck disable=SC2086
|
||||
wasm-pack build ${BUILD_FLAGS} --target web || exit 1
|
||||
|
||||
touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \
|
||||
rsync --delete-after -r --copy-links -v ./src/img/ ./pkg/img/ && \
|
||||
rsync --delete-after -r --copy-links -v ./src/external/ ./pkg/external/ && \
|
||||
cp ./src/style.css ./pkg/style.css && \
|
||||
cp ./src/wasmloader.js ./pkg/wasmloader.js && \
|
||||
rm ./pkg/.gitignore
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ -z "${BUILD_FLAGS}" ]; then
|
||||
BUILD_FLAGS="--release"
|
||||
fi
|
||||
|
||||
wasm-pack build ${BUILD_FLAGS} --target web || exit 1
|
||||
|
||||
touch ./pkg/ANYTHING_HERE_WILL_BE_DELETED_ADD_TO_SRC && \
|
||||
rsync --delete-after -r --copy-links -v ./src/img/ ./pkg/img/ && \
|
||||
rsync --delete-after -r --copy-links -v ./src/external/ ./pkg/external/ && \
|
||||
cp ./src/style.css ./pkg/style.css && \
|
||||
cp ./src/wasmloader.js ./pkg/wasmloader.js && \
|
||||
rm ./pkg/.gitignore
|
1
kanidmd_web_ui/build_wasm_release.sh
Symbolic link
1
kanidmd_web_ui/build_wasm_release.sh
Symbolic link
|
@ -0,0 +1 @@
|
|||
build_wasm.sh
|
40
kanidmd_web_ui/pkg/kanidmd_web_ui.d.ts
vendored
40
kanidmd_web_ui/pkg/kanidmd_web_ui.d.ts
vendored
|
@ -1,40 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
*/
|
||||
export function run_app(): void;
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly run_app: (a: number) => void;
|
||||
readonly __wbindgen_malloc: (a: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
|
||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||
readonly _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc22c8c82b073edeb: (a: number, b: number, c: number) => void;
|
||||
readonly _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h88db5b93ed6c64b4: (a: number, b: number, c: number) => void;
|
||||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha0cda2043cde760e: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
readonly __wbindgen_free: (a: number, b: number) => void;
|
||||
readonly __wbindgen_exn_store: (a: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously compiles the given `bytes` and instantiates the WebAssembly module.
|
||||
*
|
||||
* @param {BufferSource} bytes
|
||||
*
|
||||
* @returns {InitOutput}
|
||||
*/
|
||||
export function initSync(bytes: BufferSource): InitOutput;
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {InitInput | Promise<InitInput>} module_or_path
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
|
|
@ -8,20 +8,6 @@ heap.push(undefined, null, true, false);
|
|||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedUint8Memory0 = new Uint8Array();
|
||||
|
@ -95,14 +81,12 @@ function getInt32Memory0() {
|
|||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
|
@ -112,8 +96,24 @@ function addHeapObject(obj) {
|
|||
return idx;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let cachedFloat64Memory0 = new Float64Array();
|
||||
|
@ -224,7 +224,7 @@ function addBorrowedObject(obj) {
|
|||
}
|
||||
function __wbg_adapter_30(arg0, arg1, arg2) {
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc22c8c82b073edeb(arg0, arg1, addBorrowedObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0f7357f2a774775d(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
|
@ -252,11 +252,11 @@ function makeClosure(arg0, arg1, dtor, f) {
|
|||
return real;
|
||||
}
|
||||
function __wbg_adapter_33(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h88db5b93ed6c64b4(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h207c5c753184291b(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wbg_adapter_36(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha0cda2043cde760e(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hae7ad8c3f91d439a(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -336,9 +336,6 @@ async function load(module, imports) {
|
|||
function getImports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbindgen_json_serialize = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = JSON.stringify(obj === undefined ? null : obj);
|
||||
|
@ -347,22 +344,6 @@ function getImports() {
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
obj.a = 0;
|
||||
return true;
|
||||
}
|
||||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_modalhidebyid_b9efcd5f48cb1c79 = function(arg0, arg1) {
|
||||
modal_hide_by_id(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
|
@ -375,6 +356,25 @@ function getImports() {
|
|||
const ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
obj.a = 0;
|
||||
return true;
|
||||
}
|
||||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_modalhidebyid_b9efcd5f48cb1c79 = function(arg0, arg1) {
|
||||
modal_hide_by_id(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
|
@ -421,10 +421,10 @@ function getImports() {
|
|||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.debug(...v0);
|
||||
};
|
||||
imports.wbg.__wbg_log_06b7ffc63a0f8bee = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_error_1a35d3879f286b52 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.log(...v0);
|
||||
console.error(...v0);
|
||||
};
|
||||
imports.wbg.__wbg_warn_2aa0e7178e1d35f6 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
|
@ -631,17 +631,6 @@ function getImports() {
|
|||
const ret = getObject(arg0).getClientExtensionResults();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlDocument_508cdf1699f6d222 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof HTMLDocument;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_cookie_d9164dc637b7b2fd = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg1).cookie;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_addEventListener_ec92ea1297eefdfc = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
|
@ -824,16 +813,16 @@ function getImports() {
|
|||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper2488 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1041, __wbg_adapter_30);
|
||||
imports.wbg.__wbindgen_closure_wrapper4809 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1273, __wbg_adapter_30);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper2593 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1074, __wbg_adapter_33);
|
||||
imports.wbg.__wbindgen_closure_wrapper4914 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1306, __wbg_adapter_33);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper2859 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1127, __wbg_adapter_36);
|
||||
imports.wbg.__wbindgen_closure_wrapper5180 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1359, __wbg_adapter_36);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
|
Binary file not shown.
13
kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm.d.ts
vendored
13
kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function run_app(a: number): void;
|
||||
export function __wbindgen_malloc(a: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
||||
export const __wbindgen_export_2: WebAssembly.Table;
|
||||
export function _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc22c8c82b073edeb(a: number, b: number, c: number): void;
|
||||
export function _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h88db5b93ed6c64b4(a: number, b: number, c: number): void;
|
||||
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha0cda2043cde760e(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
export function __wbindgen_free(a: number, b: number): void;
|
||||
export function __wbindgen_exn_store(a: number): void;
|
|
@ -14,11 +14,9 @@
|
|||
"files": [
|
||||
"kanidmd_web_ui_bg.wasm",
|
||||
"kanidmd_web_ui.js",
|
||||
"kanidmd_web_ui.d.ts",
|
||||
"LICENSE.md"
|
||||
],
|
||||
"module": "kanidmd_web_ui.js",
|
||||
"homepage": "https://github.com/kanidm/kanidm/",
|
||||
"types": "kanidmd_web_ui.d.ts",
|
||||
"sideEffects": false
|
||||
}
|
|
@ -6,10 +6,9 @@ async function main() {
|
|||
}
|
||||
main()
|
||||
|
||||
// this is used in
|
||||
// this is used in modals
|
||||
export function modal_hide_by_id(m) {
|
||||
var elem = document.getElementById(m);
|
||||
var modal = bootstrap.Modal.getInstance(elem);
|
||||
modal.hide();
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::utils;
|
|||
use super::eventbus::{EventBus, EventBusMsg};
|
||||
use super::reset::ModalProps;
|
||||
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use yew::prelude::*;
|
||||
use yew_agent::Dispatched;
|
||||
|
@ -90,18 +91,21 @@ impl Component for DeleteApp {
|
|||
type Properties = ModalProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("delete modal create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal create");
|
||||
|
||||
DeleteApp { state: State::Init }
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("delete modal::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("delete modal::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal::update");
|
||||
let token_c = ctx.props().token.clone();
|
||||
match msg {
|
||||
Msg::Cancel => {
|
||||
|
@ -130,15 +134,18 @@ impl Component for DeleteApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("delete modal::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal::rendered");
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("delete modal::destroy");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal::destroy");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("delete modal::view");
|
||||
#[cfg(debug)]
|
||||
console::debug!("delete modal::view");
|
||||
|
||||
let submit_enabled = matches!(&self.state, State::Init);
|
||||
|
||||
|
@ -160,6 +167,7 @@ impl Component for DeleteApp {
|
|||
<div class="modal-body">
|
||||
|
||||
<p>{ "Delete your Password and any associated MFA?" }</p>
|
||||
<p><strong>{ "Note:"}</strong>{" this will not remove Passkeys." }</p>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
|
@ -117,7 +117,7 @@ impl Component for PasskeyModalApp {
|
|||
type Properties = ModalProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("passkey modal create");
|
||||
console::debug!("passkey modal create");
|
||||
|
||||
PasskeyModalApp {
|
||||
state: State::Init,
|
||||
|
@ -126,12 +126,12 @@ impl Component for PasskeyModalApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("passkey modal::change");
|
||||
console::debug!("passkey modal::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("passkey modal::update");
|
||||
console::debug!("passkey modal::update");
|
||||
match msg {
|
||||
Msg::LabelCheck => {
|
||||
let label = utils::get_value_from_element_id("passkey-label")
|
||||
|
@ -179,7 +179,7 @@ impl Component for PasskeyModalApp {
|
|||
self.state = State::FetchingChallenge;
|
||||
}
|
||||
Msg::ChallengeReady(challenge) => {
|
||||
console::log!(format!("{:?}", challenge).as_str());
|
||||
console::debug!(format!("{:?}", challenge).as_str());
|
||||
self.state = State::ChallengeReady(challenge);
|
||||
}
|
||||
Msg::CredentialCreate => {
|
||||
|
@ -209,7 +209,7 @@ impl Component for PasskeyModalApp {
|
|||
Msg::CredentialReady(rpkc)
|
||||
}
|
||||
Err(e) => {
|
||||
console::log!(format!("error -> {:?}", e).as_str());
|
||||
console::error!(format!("error -> {:?}", e).as_str());
|
||||
Msg::NavigatorError
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ impl Component for PasskeyModalApp {
|
|||
}
|
||||
}
|
||||
Msg::CredentialReady(rpkc) => {
|
||||
console::log!(format!("{:?}", rpkc).as_str());
|
||||
console::debug!(format!("{:?}", rpkc).as_str());
|
||||
self.state = State::CredentialReady(rpkc);
|
||||
}
|
||||
Msg::NavigatorError => {
|
||||
|
@ -247,15 +247,15 @@ impl Component for PasskeyModalApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("passkey modal::rendered");
|
||||
console::debug!("passkey modal::rendered");
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("passkey modal::destroy");
|
||||
console::debug!("passkey modal::destroy");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("passkey modal::view");
|
||||
console::debug!("passkey modal::view");
|
||||
|
||||
let label_val = self.label_val.clone();
|
||||
|
||||
|
@ -355,7 +355,7 @@ impl Component for PasskeyModalApp {
|
|||
|
||||
<form class="row g-3 needs-validation" novalidate=true
|
||||
onsubmit={ ctx.link().callback(move |e: FocusEvent| {
|
||||
console::log!("passkey modal::on form submit prevent default");
|
||||
console::debug!("passkey modal::on form submit prevent default");
|
||||
e.prevent_default();
|
||||
if submit_enabled {
|
||||
Msg::Submit
|
||||
|
|
|
@ -50,14 +50,14 @@ impl PasskeyRemoveModalApp {
|
|||
let tag = tag.to_string();
|
||||
|
||||
html! {
|
||||
<li>
|
||||
<div class="row g-3">
|
||||
<p>{ tag }</p>
|
||||
<button type="button" class="btn btn-dark btn-sml" data-bs-toggle="modal" data-bs-target={ remove_tgt }>
|
||||
<div class="row mb-3">
|
||||
<div class="col">{ tag.clone() }</div>
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-dark btn-sml" id={tag} data-bs-toggle="modal" data-bs-target={ remove_tgt }>
|
||||
{ "Remove" }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ impl Component for PasskeyRemoveModalApp {
|
|||
type Properties = PasskeyRemoveModalProps;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
console::log!("passkey remove modal create");
|
||||
console::debug!("passkey remove modal create");
|
||||
|
||||
let tag = ctx.props().tag.clone();
|
||||
let uuid = ctx.props().uuid.clone();
|
||||
|
@ -141,12 +141,12 @@ impl Component for PasskeyRemoveModalApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("passkey remove modal::change");
|
||||
console::debug!("passkey remove modal::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("passkey remove modal::update");
|
||||
console::debug!("passkey remove modal::update");
|
||||
match msg {
|
||||
Msg::Submit => {
|
||||
self.reset_and_hide();
|
||||
|
@ -178,15 +178,15 @@ impl Component for PasskeyRemoveModalApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("passkey remove modal::rendered");
|
||||
console::debug!("passkey remove modal::rendered");
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("passkey remove modal::destroy");
|
||||
console::debug!("passkey remove modal::destroy");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("passkey remove modal::view");
|
||||
console::debug!("passkey remove modal::view");
|
||||
|
||||
let remove_tgt = self.target.clone();
|
||||
let remove_id = format!("staticPasskeyRemove-{}", self.uuid);
|
||||
|
|
|
@ -114,7 +114,8 @@ impl Component for PwModalApp {
|
|||
type Properties = ModalProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("pw modal create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal create");
|
||||
|
||||
PwModalApp {
|
||||
state: PwState::Init,
|
||||
|
@ -125,12 +126,14 @@ impl Component for PwModalApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("pw modal::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("pw modal::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal::update");
|
||||
match msg {
|
||||
Msg::PasswordCheck => {
|
||||
let pw =
|
||||
|
@ -181,15 +184,18 @@ impl Component for PwModalApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("pw modal::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal::rendered");
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("pw modal::destroy");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal::destroy");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("pw modal::view");
|
||||
#[cfg(debug)]
|
||||
console::debug!("pw modal::view");
|
||||
|
||||
let (pw_class, pw_feedback) = match &self.state {
|
||||
PwState::Feedback(feedback) => {
|
||||
|
@ -250,7 +256,7 @@ impl Component for PwModalApp {
|
|||
<div class="modal-body">
|
||||
<form class="row g-3 needs-validation" novalidate=true
|
||||
onsubmit={ ctx.link().callback(move |e: FocusEvent| {
|
||||
console::log!("pw modal::on form submit prevent default");
|
||||
console::debug!("pw modal::on form submit prevent default");
|
||||
e.prevent_default();
|
||||
if submit_enabled {
|
||||
Msg::PasswordSubmit
|
||||
|
|
|
@ -95,7 +95,8 @@ impl Component for CredentialResetApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
console::log!("credential::reset::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::create");
|
||||
|
||||
// On a page refresh/reload, should we restart a session that *may* have existed?
|
||||
// This could be achieved with local storage
|
||||
|
@ -114,11 +115,12 @@ impl Component for CredentialResetApp {
|
|||
.location()
|
||||
.expect_throw("Can't access current location");
|
||||
|
||||
// TODO: the error here ... isn't always an error, when a user comes from the dashboard they don't set a cred token in the URL, probably should handle this with a *slightly* nicer error
|
||||
let query: Option<CUIntentToken> = location
|
||||
.query()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("query decode error -> {:?}", e);
|
||||
console::log!(e_msg.as_str());
|
||||
let e_msg = format!("error decoding URL parameters -> {:?}", e);
|
||||
console::error!(e_msg.as_str());
|
||||
})
|
||||
.ok();
|
||||
|
||||
|
@ -153,18 +155,20 @@ impl Component for CredentialResetApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("credential::reset::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("credential::reset::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::update");
|
||||
let next_state = match (msg, &self.state) {
|
||||
(Msg::Ignore, _) => None,
|
||||
(Msg::TokenSubmit, State::TokenInput) => {
|
||||
#[allow(clippy::expect_used)]
|
||||
let token = utils::get_value_from_element_id("autofocus")
|
||||
.expect("Unable to find autofocus element");
|
||||
let token = utils::get_value_from_element_id("token")
|
||||
.expect("Unable to find an input with id=token");
|
||||
|
||||
ctx.link().send_future(async {
|
||||
match Self::exchange_intent_token(token).await {
|
||||
|
@ -176,18 +180,20 @@ impl Component for CredentialResetApp {
|
|||
Some(State::WaitingForStatus)
|
||||
}
|
||||
(Msg::BeginSession { token, status }, State::WaitingForStatus) => {
|
||||
console::log!(format!("{:?}", status).as_str());
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("begin session {:?}", status).as_str());
|
||||
Some(State::Main { token, status })
|
||||
}
|
||||
(Msg::UpdateSession { status }, State::Main { token, status: _ }) => {
|
||||
console::log!(format!("{:?}", status).as_str());
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("{:?}", status).as_str());
|
||||
Some(State::Main {
|
||||
token: token.clone(),
|
||||
status,
|
||||
})
|
||||
}
|
||||
(Msg::Commit, State::Main { token, status }) => {
|
||||
console::log!(format!("{:?}", status).as_str());
|
||||
console::debug!(format!("{:?}", status).as_str());
|
||||
let token_c = token.clone();
|
||||
|
||||
ctx.link().send_future(async {
|
||||
|
@ -200,7 +206,7 @@ impl Component for CredentialResetApp {
|
|||
Some(State::WaitingForCommit)
|
||||
}
|
||||
(Msg::Cancel, State::Main { token, status }) => {
|
||||
console::log!(format!("{:?}", status).as_str());
|
||||
console::debug!(format!("{:?}", status).as_str());
|
||||
let token_c = token.clone();
|
||||
|
||||
ctx.link().send_future(async {
|
||||
|
@ -214,14 +220,15 @@ impl Component for CredentialResetApp {
|
|||
}
|
||||
(Msg::Success, State::WaitingForCommit) => {
|
||||
let loc = models::pop_return_location();
|
||||
console::log!(format!("Going to -> {:?}", loc));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("Going to -> {:?}", loc));
|
||||
loc.goto(&ctx.link().history().expect_throw("failed to read history"));
|
||||
|
||||
None
|
||||
}
|
||||
(Msg::Error { emsg, kopid }, _) => Some(State::Error { emsg, kopid }),
|
||||
(_, _) => {
|
||||
console::debug!("CredentialResetApp state match fail on update.");
|
||||
console::error!("CredentialResetApp state match fail on update.");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
@ -235,12 +242,15 @@ impl Component for CredentialResetApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
crate::utils::autofocus();
|
||||
console::log!("credential::reset::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::rendered");
|
||||
// because sometimes bootstrap doesn't catch it, which is annoying.
|
||||
crate::utils::autofocus("token");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("credential::reset::view");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::view");
|
||||
match &self.state {
|
||||
State::TokenInput => self.view_token_input(ctx),
|
||||
State::WaitingForStatus | State::WaitingForCommit => self.view_waiting(ctx),
|
||||
|
@ -250,7 +260,8 @@ impl Component for CredentialResetApp {
|
|||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("credential::reset::destroy");
|
||||
#[cfg(debug)]
|
||||
console::debug!("credential::reset::destroy");
|
||||
remove_body_form_classes!();
|
||||
}
|
||||
}
|
||||
|
@ -261,31 +272,38 @@ impl CredentialResetApp {
|
|||
<main class="flex-shrink-0 form-signin">
|
||||
<center>
|
||||
<img src="/pkg/img/logo-square.svg" alt="Kanidm" class="kanidm_logo"/>
|
||||
<h2>{ "Credential Reset" } </h2>
|
||||
// TODO: replace this with a call to domain info
|
||||
<h3>{ "Kanidm idm.example.com" } </h3>
|
||||
<h3>{ "idm.example.com" } </h3>
|
||||
</center>
|
||||
<p>
|
||||
{"Enter your credential reset token"}
|
||||
</p>
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("credential::reset::view_token_input -> TokenInput - prevent_default()");
|
||||
console::debug!("credential::reset::view_token_input -> TokenInput - prevent_default()");
|
||||
e.prevent_default();
|
||||
|
||||
Msg::TokenSubmit
|
||||
} ) }
|
||||
action="javascript:void(0);">
|
||||
<p class="text-center">
|
||||
<label for="token" class="form-label">
|
||||
{"Enter your credential reset token."}
|
||||
</label>
|
||||
<input
|
||||
id="autofocus"
|
||||
id="token"
|
||||
name="token"
|
||||
autofocus=true
|
||||
type="text"
|
||||
class="form-control"
|
||||
value=""
|
||||
/>
|
||||
<button type="submit" class="btn btn-dark">{" Submit "}</button><br />
|
||||
</form>
|
||||
<p>
|
||||
<a href="/"><button href="/" class="btn btn-dark" aria-label="Return home">{"Return to the home page"}</button></a>
|
||||
</p>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<button type="submit" class="btn btn-primary">{" Submit "}</button><br />
|
||||
</p>
|
||||
</form>
|
||||
<p class="text-center">
|
||||
<a href="/"><button href="/" class="btn btn-secondary" aria-label="Return home">{"Return to the home page"}</button></a>
|
||||
</p>
|
||||
|
||||
</main>
|
||||
}
|
||||
|
@ -321,8 +339,8 @@ impl CredentialResetApp {
|
|||
}) => {
|
||||
html! {
|
||||
<>
|
||||
<p>{ "Password Set" }</p>
|
||||
<p>{ "Mfa Disabled" }</p>
|
||||
<p>{ "✅ Password Set" }</p>
|
||||
<p>{ "❌ MFA Disabled" }</p>
|
||||
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticTotpCreate">
|
||||
{ "Add TOTP" }
|
||||
|
@ -373,8 +391,8 @@ impl CredentialResetApp {
|
|||
}) => {
|
||||
html! {
|
||||
<>
|
||||
<p>{ "Password Set" }</p>
|
||||
<p>{ "Mfa Enabled" }</p>
|
||||
<p>{ "✅ Password Set" }</p>
|
||||
<p>{ "✅ MFA Enabled" }</p>
|
||||
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticTotpCreate">
|
||||
{ "Reset TOTP" }
|
||||
|
@ -401,15 +419,14 @@ impl CredentialResetApp {
|
|||
}
|
||||
} else {
|
||||
html! {
|
||||
<div class="container">
|
||||
<ul class="list-unstyled">
|
||||
{ for status.passkeys.iter()
|
||||
.map(|detail|
|
||||
PasskeyRemoveModalApp::render_button(&detail.tag, detail.uuid)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<>
|
||||
<h4>{"Registered Passkeys"}</h4>
|
||||
{ for status.passkeys.iter()
|
||||
.map(|detail|
|
||||
PasskeyRemoveModalApp::render_button(&detail.tag, detail.uuid)
|
||||
)
|
||||
}
|
||||
</>
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -424,10 +441,11 @@ impl CredentialResetApp {
|
|||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<div class="d-flex align-items-start form-cred-reset-body">
|
||||
<main class="w-100">
|
||||
<div class="py-5 text-center">
|
||||
<h4>{ "Updating Credentials" }</h4>
|
||||
<div class="py-3 text-center">
|
||||
<h3>{ "Updating Credentials" }</h3>
|
||||
<p>{ displayname }</p>
|
||||
<p>{ spn }</p>
|
||||
</div>
|
||||
|
@ -444,20 +462,22 @@ impl CredentialResetApp {
|
|||
|
||||
<hr class="my-4" />
|
||||
|
||||
<h4>{"Password / MFA"}</h4>
|
||||
{ pw_html }
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
// TODO: this could probably just be a link back home, currently it breaks and sends the user to an error..
|
||||
<button class="w-50 btn btn-danger btn-lg" type="submit"
|
||||
disabled=false
|
||||
onclick={
|
||||
ctx.link()
|
||||
.callback(move |_| {
|
||||
Msg::Cancel
|
||||
})
|
||||
.callback(move |_| {
|
||||
Msg::Cancel
|
||||
})
|
||||
}
|
||||
>{ "Cancel" }</button>
|
||||
<button class="w-50 btn btn-success btn-lg" type="submit"
|
||||
>{ "Cancel" }</button>
|
||||
<button class="w-50 btn btn-success btn-lg" type="submit"
|
||||
disabled={ !can_commit }
|
||||
onclick={
|
||||
ctx.link()
|
||||
|
@ -481,15 +501,19 @@ impl CredentialResetApp {
|
|||
{ passkey_modals_html }
|
||||
|
||||
</div>
|
||||
{ crate::utils::do_footer() }
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn view_error(&self, _ctx: &Context<Self>, msg: &str, kopid: Option<&str>) -> Html {
|
||||
html! {
|
||||
<main class="form-signin">
|
||||
<div class="container">
|
||||
<p class="text-center">
|
||||
<img src="/pkg/img/logo-square.svg" alt="Kanidm" class="kanidm_logo"/>
|
||||
</p>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h2>{ "An Error Occured 🥺" }</h2>
|
||||
</div>
|
||||
<p>{ msg.to_string() }</p>
|
||||
<p>
|
||||
{
|
||||
|
@ -500,6 +524,10 @@ impl CredentialResetApp {
|
|||
}
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-center">
|
||||
<a href="/"><button href="/" class="btn btn-secondary" aria-label="Return home">{"Return to the home page"}</button></a>
|
||||
</p>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::utils;
|
|||
use super::eventbus::{EventBus, EventBusMsg};
|
||||
use super::reset::ModalProps;
|
||||
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use web_sys::Node;
|
||||
use yew::prelude::*;
|
||||
|
@ -125,7 +126,8 @@ impl Component for TotpModalApp {
|
|||
type Properties = ModalProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("totp modal create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal create");
|
||||
|
||||
TotpModalApp {
|
||||
state: TotpState::Init,
|
||||
|
@ -135,12 +137,14 @@ impl Component for TotpModalApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("totp modal::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("totp modal::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal::update");
|
||||
let token_c = ctx.props().token.clone();
|
||||
match msg {
|
||||
Msg::TotpCancel => {
|
||||
|
@ -235,15 +239,18 @@ impl Component for TotpModalApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("totp modal::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal::rendered");
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("totp modal::destroy");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal::destroy");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("totp modal::view");
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp modal::view");
|
||||
|
||||
let totp_class = match &self.check {
|
||||
TotpCheck::Invalid | TotpCheck::Sha1Accept => classes!("form-control", "is-invalid"),
|
||||
|
@ -268,7 +275,7 @@ impl Component for TotpModalApp {
|
|||
Msg::TotpAcceptSha1
|
||||
})
|
||||
}
|
||||
>{ "Accept Sha1 Token" }</button>
|
||||
>{ "Accept SHA1 Token" }</button>
|
||||
},
|
||||
_ => html! {
|
||||
<button id="totp-submit" type="button" class="btn btn-primary"
|
||||
|
|
|
@ -5,6 +5,7 @@ use wasm_bindgen::JsCast;
|
|||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
use yew::prelude::*;
|
||||
use yew::virtual_dom::VNode;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use crate::error::FetchError;
|
||||
|
@ -12,7 +13,7 @@ use crate::models;
|
|||
use crate::utils;
|
||||
|
||||
use kanidm_proto::v1::{
|
||||
AuthAllowed, AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep, AuthMech
|
||||
AuthAllowed, AuthCredential, AuthMech, AuthRequest, AuthResponse, AuthState, AuthStep,
|
||||
};
|
||||
use kanidm_proto::webauthn::PublicKeyCredential;
|
||||
|
||||
|
@ -114,7 +115,7 @@ impl LoginApp {
|
|||
} else if status == 404 {
|
||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
console::log!(format!(
|
||||
console::error!(format!(
|
||||
"User not found: {:?}. Operation ID: {:?}",
|
||||
text, kopid
|
||||
));
|
||||
|
@ -170,6 +171,15 @@ impl LoginApp {
|
|||
}
|
||||
}
|
||||
|
||||
/// Renders the "Start again" button
|
||||
fn button_start_again(&self, ctx: &Context<Self>) -> VNode {
|
||||
html! {
|
||||
<div class="col-md-auto text-center">
|
||||
<button type="button" class="btn btn-dark" onclick={ ctx.link().callback(|_| LoginAppMsg::Restart) } >{" Start Again "}</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn render_auth_allowed(&self, ctx: &Context<Self>, idx: usize, allow: &AuthAllowed) -> Html {
|
||||
html! {
|
||||
<li>
|
||||
|
@ -184,10 +194,10 @@ impl LoginApp {
|
|||
|
||||
fn render_mech_select(&self, ctx: &Context<Self>, idx: usize, allow: &AuthMech) -> Html {
|
||||
html! {
|
||||
<li>
|
||||
<li class="text-center">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-dark"
|
||||
class="btn btn-dark mb-2"
|
||||
onclick={ ctx.link().callback(move |_| LoginAppMsg::Select(idx)) }
|
||||
>{ allow.to_string() }</button>
|
||||
</li>
|
||||
|
@ -201,22 +211,25 @@ impl LoginApp {
|
|||
html! {
|
||||
<>
|
||||
<div class="container">
|
||||
<label for="autofocus" class="form-label">{ " Username " }</label>
|
||||
<label for="username" class="form-label">{ "Username" }</label>
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("login::view_state -> Init - prevent_default()".to_string());
|
||||
console::debug!("login::view_state -> Init - prevent_default()".to_string());
|
||||
e.prevent_default();
|
||||
LoginAppMsg::Begin
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<div class="input-group mb-3">
|
||||
<input id="autofocus"
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={ inputvalue }
|
||||
<input
|
||||
autofocus=true
|
||||
class="autofocus form-control"
|
||||
disabled={ !enable }
|
||||
id="username"
|
||||
name="username"
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e))) }
|
||||
type="text"
|
||||
value={ inputvalue }
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -271,34 +284,29 @@ impl LoginApp {
|
|||
LoginState::Password(enable) => {
|
||||
html! {
|
||||
<>
|
||||
<div class="container">
|
||||
<p>
|
||||
{" Password: "}
|
||||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<form class="row g-3"
|
||||
action="javascript:void(0);"
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("login::view_state -> Password - prevent_default()".to_string());
|
||||
console::debug!("login::view_state -> Password - prevent_default()".to_string());
|
||||
e.prevent_default();
|
||||
LoginAppMsg::PasswordSubmit
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<div class="col-12">
|
||||
<div class="col-12"><label for="password" class="form-label">{ "Password" }</label>
|
||||
<input
|
||||
id="autofocus"
|
||||
type="password"
|
||||
class="form-control"
|
||||
value={ inputvalue }
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e))) }
|
||||
autofocus=true
|
||||
class="autofocus form-control"
|
||||
disabled={ !enable }
|
||||
id="password"
|
||||
name="password"
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e))) }
|
||||
type="password"
|
||||
value={ inputvalue }
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<button type="submit" class="btn btn-dark" disabled={ !enable }>{" Submit "}</button>
|
||||
</center>
|
||||
<div class="col-12 text-center">
|
||||
<button type="submit" class="btn btn-dark" disabled={ !enable }>{ "Submit" }</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -308,29 +316,30 @@ impl LoginApp {
|
|||
LoginState::BackupCode(enable) => {
|
||||
html! {
|
||||
<>
|
||||
<div class="container">
|
||||
<p>
|
||||
{" Backup Code: "}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("login::view_state -> BackupCode - prevent_default()".to_string());
|
||||
console::debug!("login::view_state -> BackupCode - prevent_default()".to_string());
|
||||
e.prevent_default();
|
||||
LoginAppMsg::BackupCodeSubmit
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<label for="backup_code" class="form-label">
|
||||
{"Backup Code"}
|
||||
</label>
|
||||
<input
|
||||
id="autofocus"
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={ inputvalue }
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e))) }
|
||||
autofocus=true
|
||||
class="autofocus form-control"
|
||||
disabled={ !enable }
|
||||
id="backup_code"
|
||||
name="backup_code"
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e))) }
|
||||
type="text"
|
||||
value={ inputvalue }
|
||||
/>
|
||||
<button type="submit" class="btn btn-dark" disabled={ !enable }>{" Submit "}</button>
|
||||
<button type="submit" class="btn btn-dark">{" Submit "}</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
|
@ -339,25 +348,25 @@ impl LoginApp {
|
|||
LoginState::Totp(state) => {
|
||||
html! {
|
||||
<>
|
||||
<div class="container">
|
||||
<p>
|
||||
{" TOTP: "}
|
||||
{ if state==&TotpState::Invalid { "can only contain numeric digits" } else { "" } }
|
||||
</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("login::view_state -> Totp - prevent_default()".to_string());
|
||||
console::debug!("login::view_state -> Totp - prevent_default()".to_string());
|
||||
e.prevent_default();
|
||||
LoginAppMsg::TotpSubmit
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<label for="totp" class="form-label">
|
||||
{"TOTP"}
|
||||
{ if state==&TotpState::Invalid { "can only contain numeric digits" } else { "" } }
|
||||
</label>
|
||||
<input
|
||||
id="autofocus"
|
||||
autofocus=true
|
||||
name="totp"
|
||||
id="totp"
|
||||
type="text"
|
||||
class="form-control"
|
||||
class="autofocus form-control"
|
||||
value={ inputvalue }
|
||||
oninput={ ctx.link().callback(|e: InputEvent| LoginAppMsg::Input(utils::get_value_from_input_event(e)))}
|
||||
disabled={ state==&TotpState::Disabled }
|
||||
|
@ -445,9 +454,9 @@ impl LoginApp {
|
|||
};
|
||||
|
||||
html! {
|
||||
<div class="container">
|
||||
<div class="container text-center">
|
||||
<p>
|
||||
{" Passkey "}
|
||||
{"Prompting for Passkey authentication..."}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
@ -455,7 +464,8 @@ impl LoginApp {
|
|||
LoginState::Authenticated => {
|
||||
let loc = models::pop_return_location();
|
||||
// redirect
|
||||
console::log!(format!("authenticated, try going to -> {:?}", loc));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("authenticated, try going to -> {:?}", loc));
|
||||
loc.goto(&ctx.link().history().expect_throw("failed to read history"));
|
||||
html! {
|
||||
<div class="container">
|
||||
|
@ -473,9 +483,7 @@ impl LoginApp {
|
|||
<p><strong>{ "Authentication Denied" }</strong></p>
|
||||
<p>{ msg.as_str() }</p>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<button type="button" class="btn btn-dark" onclick={ ctx.link().callback(|_| LoginAppMsg::Restart) } >{" Start Again "}</button>
|
||||
</div>
|
||||
{ self.button_start_again(ctx) }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -487,23 +495,16 @@ impl LoginApp {
|
|||
<div class="alert alert-danger" role="alert">
|
||||
{ "That username was not found. Please try again!" }
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<button type="button"
|
||||
class="btn btn-dark"
|
||||
onclick={ ctx.link().callback(|_| LoginAppMsg::Restart) }
|
||||
>
|
||||
{" Start Again "}</button>
|
||||
</div>
|
||||
{ self.button_start_again(ctx) }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
LoginState::Error { emsg, kopid } => {
|
||||
html! {
|
||||
<div class="container">
|
||||
<p>
|
||||
{ "An error has occured 😔 " }
|
||||
</p>
|
||||
<>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h2>{ "An error has occured 😔 " }</h2>
|
||||
<p>
|
||||
{ emsg.as_str() }
|
||||
</p>
|
||||
|
@ -513,10 +514,14 @@ impl LoginApp {
|
|||
format!("Operation ID: {}", opid.clone())
|
||||
}
|
||||
else {
|
||||
"Local Error".to_string() }
|
||||
"Error occurred client-side.".to_string()
|
||||
}
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{ self.button_start_again(ctx) }
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -528,22 +533,27 @@ impl Component for LoginApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("create".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("create".to_string());
|
||||
// Assume we are here for a good reason.
|
||||
models::clear_bearer_token();
|
||||
// Do we have a login hint?
|
||||
let inputvalue = models::pop_login_hint().unwrap_or_else(|| "".to_string());
|
||||
// Clean any cookies.
|
||||
let document = utils::document();
|
||||
|
||||
let html_document = document
|
||||
.dyn_into::<web_sys::HtmlDocument>()
|
||||
.expect_throw("failed to dyn cast to htmldocument");
|
||||
let cookie = html_document
|
||||
.cookie()
|
||||
.expect_throw("failed to access page cookies");
|
||||
console::log!("cookies".to_string());
|
||||
console::log!(cookie);
|
||||
#[cfg(debug)]
|
||||
{
|
||||
let document = utils::document();
|
||||
let html_document = document
|
||||
.dyn_into::<web_sys::HtmlDocument>()
|
||||
.expect_throw("failed to dyn cast to htmldocument");
|
||||
let cookie = html_document
|
||||
.cookie()
|
||||
.expect_throw("failed to access page cookies");
|
||||
console::debug!("cookies".to_string());
|
||||
console::debug!(cookie);
|
||||
}
|
||||
// Clean any cookies.
|
||||
// TODO: actually check that it's cleaning the cookies.
|
||||
|
||||
let state = LoginState::Init(true);
|
||||
|
||||
|
@ -574,7 +584,8 @@ impl Component for LoginApp {
|
|||
true
|
||||
}
|
||||
LoginAppMsg::Begin => {
|
||||
console::log!(format!("begin -> {:?}", self.inputvalue));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("begin -> {:?}", self.inputvalue));
|
||||
// Disable the button?
|
||||
let username = self.inputvalue.clone();
|
||||
ctx.link().send_future(async {
|
||||
|
@ -587,7 +598,8 @@ impl Component for LoginApp {
|
|||
true
|
||||
}
|
||||
LoginAppMsg::PasswordSubmit => {
|
||||
console::log!("At password step".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("At password step".to_string());
|
||||
// Disable the button?
|
||||
self.state = LoginState::Password(false);
|
||||
let authreq = AuthRequest {
|
||||
|
@ -605,7 +617,8 @@ impl Component for LoginApp {
|
|||
true
|
||||
}
|
||||
LoginAppMsg::BackupCodeSubmit => {
|
||||
console::log!("backupcode".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("backupcode".to_string());
|
||||
// Disable the button?
|
||||
self.state = LoginState::BackupCode(false);
|
||||
let authreq = AuthRequest {
|
||||
|
@ -623,7 +636,8 @@ impl Component for LoginApp {
|
|||
true
|
||||
}
|
||||
LoginAppMsg::TotpSubmit => {
|
||||
console::log!("totp".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("totp".to_string());
|
||||
// Disable the button?
|
||||
match self.inputvalue.parse::<u32>() {
|
||||
Ok(totp) => {
|
||||
|
@ -650,7 +664,8 @@ impl Component for LoginApp {
|
|||
true
|
||||
}
|
||||
LoginAppMsg::SecurityKeySubmit(resp) => {
|
||||
console::log!("At securitykey step".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("At securitykey step".to_string());
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::SecurityKey(resp)),
|
||||
};
|
||||
|
@ -665,7 +680,8 @@ impl Component for LoginApp {
|
|||
false
|
||||
}
|
||||
LoginAppMsg::PasskeySubmit(resp) => {
|
||||
console::log!("At passkey step".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("At passkey step".to_string());
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Passkey(resp)),
|
||||
};
|
||||
|
@ -682,7 +698,8 @@ impl Component for LoginApp {
|
|||
LoginAppMsg::Start(session_id, resp) => {
|
||||
// Clear any leftover input
|
||||
self.inputvalue = "".to_string();
|
||||
console::log!(format!("start -> {:?} : {:?}", resp, session_id));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("start -> {:?} : {:?}", resp, session_id));
|
||||
match resp.state {
|
||||
AuthState::Choose(mut mechs) => {
|
||||
self.session_id = session_id;
|
||||
|
@ -702,18 +719,20 @@ impl Component for LoginApp {
|
|||
// We do NOT need to change state or redraw
|
||||
false
|
||||
} else {
|
||||
console::log!("multiple mechs exist".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("multiple mechs exist".to_string());
|
||||
self.state = LoginState::Select(mechs);
|
||||
true
|
||||
}
|
||||
}
|
||||
AuthState::Denied(reason) => {
|
||||
console::log!(format!("denied -> {:?}", reason));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("denied -> {:?}", reason));
|
||||
self.state = LoginState::Denied(reason);
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
console::log!("invalid state transition".to_string());
|
||||
console::error!("invalid state transition".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid UI State Transition".to_string(),
|
||||
kopid: None,
|
||||
|
@ -723,33 +742,32 @@ impl Component for LoginApp {
|
|||
}
|
||||
}
|
||||
LoginAppMsg::Select(idx) => {
|
||||
console::log!(format!("chose -> {:?}", idx));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("chose -> {:?}", idx));
|
||||
match &self.state {
|
||||
LoginState::Select(allowed) => {
|
||||
match allowed.get(idx) {
|
||||
Some(mech) => {
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Begin(mech.clone()),
|
||||
};
|
||||
let session_id = self.session_id.clone();
|
||||
ctx.link().send_future(async {
|
||||
match Self::auth_step(authreq, session_id).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
}
|
||||
None => {
|
||||
console::log!("invalid allowed mech idx".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid Continue Index".to_string(),
|
||||
kopid: None,
|
||||
};
|
||||
}
|
||||
LoginState::Select(allowed) => match allowed.get(idx) {
|
||||
Some(mech) => {
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Begin(mech.clone()),
|
||||
};
|
||||
let session_id = self.session_id.clone();
|
||||
ctx.link().send_future(async {
|
||||
match Self::auth_step(authreq, session_id).await {
|
||||
Ok(v) => v,
|
||||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
None => {
|
||||
console::error!("invalid allowed mech idx".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid Continue Index".to_string(),
|
||||
kopid: None,
|
||||
};
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
console::log!("invalid state transition".to_string());
|
||||
console::error!("invalid state transition".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid UI State Transition".to_string(),
|
||||
kopid: None,
|
||||
|
@ -761,12 +779,13 @@ impl Component for LoginApp {
|
|||
LoginAppMsg::Next(resp) => {
|
||||
// Clear any leftover input
|
||||
self.inputvalue = "".to_string();
|
||||
console::log!(format!("next -> {:?}", resp));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("next -> {:?}", resp));
|
||||
|
||||
// Based on the state we have, we need to chose our steps.
|
||||
match resp.state {
|
||||
AuthState::Choose(_mechs) => {
|
||||
console::log!("invalid state transition".to_string());
|
||||
console::error!("invalid state transition".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid UI State Transition".to_string(),
|
||||
kopid: None,
|
||||
|
@ -799,13 +818,14 @@ impl Component for LoginApp {
|
|||
}
|
||||
} else {
|
||||
// Else, present the options in a choice.
|
||||
console::log!("multiple choices exist".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("multiple choices exist".to_string());
|
||||
self.state = LoginState::Continue(allowed);
|
||||
}
|
||||
true
|
||||
}
|
||||
AuthState::Denied(reason) => {
|
||||
console::log!(format!("denied -> {:?}", reason));
|
||||
console::error!(format!("denied -> {:?}", reason));
|
||||
self.state = LoginState::Denied(reason);
|
||||
true
|
||||
}
|
||||
|
@ -819,7 +839,8 @@ impl Component for LoginApp {
|
|||
}
|
||||
LoginAppMsg::Continue(idx) => {
|
||||
// Are we in the correct internal state?
|
||||
console::log!(format!("chose -> {:?}", idx));
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("chose -> {:?}", idx));
|
||||
match &self.state {
|
||||
LoginState::Continue(allowed) => {
|
||||
match allowed.get(idx) {
|
||||
|
@ -843,7 +864,7 @@ impl Component for LoginApp {
|
|||
self.state = LoginState::Passkey(challenge.clone().into())
|
||||
}
|
||||
None => {
|
||||
console::log!("invalid allowed mech idx".to_string());
|
||||
console::error!("invalid allowed mech idx".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid Continue Index".to_string(),
|
||||
kopid: None,
|
||||
|
@ -852,7 +873,7 @@ impl Component for LoginApp {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
console::log!("invalid state transition".to_string());
|
||||
console::error!("invalid state transition".to_string());
|
||||
self.state = LoginState::Error {
|
||||
emsg: "Invalid UI State Transition".to_string(),
|
||||
kopid: None,
|
||||
|
@ -864,14 +885,14 @@ impl Component for LoginApp {
|
|||
LoginAppMsg::UnknownUser => {
|
||||
// Clear any leftover input
|
||||
self.inputvalue = "".to_string();
|
||||
console::log!("Unknown user".to_string());
|
||||
console::warn!("Unknown user".to_string());
|
||||
self.state = LoginState::UnknownUser;
|
||||
true
|
||||
}
|
||||
LoginAppMsg::Error { emsg, kopid } => {
|
||||
// Clear any leftover input
|
||||
self.inputvalue = "".to_string();
|
||||
console::log!(format!("error -> {:?}, {:?}", emsg, kopid));
|
||||
console::error!(format!("error -> {:?}, {:?}", emsg, kopid));
|
||||
self.state = LoginState::Error { emsg, kopid };
|
||||
true
|
||||
}
|
||||
|
@ -879,7 +900,8 @@ impl Component for LoginApp {
|
|||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("login::view".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("login::view".to_string());
|
||||
// How do we add a top level theme?
|
||||
/*
|
||||
let (width, height): (u32, u32) = if let Some(win) = web_sys::window() {
|
||||
|
@ -911,22 +933,19 @@ impl Component for LoginApp {
|
|||
</center>
|
||||
{ self.view_state(ctx) }
|
||||
</main>
|
||||
<footer class="footer mt-auto py-3 bg-light text-end">
|
||||
<div class="container">
|
||||
<span class="text-muted">{ "Powered by " }<a href="https://kanidm.com">{ "Kanidm" }</a></span>
|
||||
</div>
|
||||
</footer>
|
||||
{ crate::utils::do_footer() }
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("login::destroy".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("login::destroy".to_string());
|
||||
remove_body_form_classes!();
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
crate::utils::autofocus();
|
||||
console::log!("login::rendered".to_string());
|
||||
#[cfg(debug)]
|
||||
console::debug!("login::rendered".to_string());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ macro_rules! add_body_form_classes {
|
|||
() => {
|
||||
for x in $crate::constants::CSS_CLASSES_BODY_FORM {
|
||||
if let Err(e) = $crate::utils::body().class_list().add_1(x) {
|
||||
console::log!(format!("class_list add error -> {:?}", e));
|
||||
console::error!(format!("class_list add error -> {:?}", e));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ macro_rules! remove_body_form_classes {
|
|||
() => {
|
||||
for x in $crate::constants::CSS_CLASSES_BODY_FORM {
|
||||
if let Err(e) = $crate::utils::body().class_list().remove_1(x) {
|
||||
console::log!(format!("class_list removal error -> {:?}", e));
|
||||
console::error!(format!("class_list removal error -> {:?}", e));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! not atuhenticated, this will determine that and send you to authentication first, then
|
||||
//! will allow you to proceed with the oauth flow.
|
||||
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use yew::functional::*;
|
||||
|
@ -49,7 +50,8 @@ fn landing() -> Html {
|
|||
}
|
||||
|
||||
fn switch(route: &Route) -> Html {
|
||||
console::log!("manager::switch");
|
||||
#[cfg(debug)]
|
||||
console::debug!("manager::switch");
|
||||
match route {
|
||||
Route::Landing => html! { <Landing /> },
|
||||
Route::Login => html! { <LoginApp /> },
|
||||
|
@ -76,24 +78,28 @@ impl Component for ManagerApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("manager::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("manager::create");
|
||||
ManagerApp {}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("manager::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("manager::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
console::log!("manager::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("manager::update");
|
||||
true
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("manager::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("manager::rendered");
|
||||
// Can only access the current_route AFTER it renders.
|
||||
// console::log!(format!("{:?}", yew_router::current_route::<Route>()).as_str())
|
||||
// console::debug!(format!("{:?}", yew_router::current_route::<Route>()).as_str())
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
|
|
|
@ -15,7 +15,8 @@ use kanidm_proto::v1::{CUSessionToken, CUStatus};
|
|||
|
||||
pub fn get_bearer_token() -> Option<String> {
|
||||
let prev_session: Result<String, _> = PersistentStorage::get("kanidm_bearer_token");
|
||||
console::log!(format!("kanidm_bearer_token -> {:?}", prev_session).as_str());
|
||||
#[cfg(debug)]
|
||||
console::debug!(format!("kanidm_bearer_token -> {:?}", prev_session).as_str());
|
||||
|
||||
prev_session.ok()
|
||||
}
|
||||
|
@ -45,34 +46,36 @@ impl Location {
|
|||
}
|
||||
|
||||
pub fn push_return_location(l: Location) {
|
||||
TemporaryStorage::set("return_location", l).expect_throw("failed to set header");
|
||||
TemporaryStorage::set("return_location", l)
|
||||
.expect_throw("failed to set return_location in temporary storage");
|
||||
}
|
||||
|
||||
pub fn pop_return_location() -> Location {
|
||||
let l: Result<Location, _> = TemporaryStorage::get("return_location");
|
||||
console::log!(format!("return_location -> {:?}", l).as_str());
|
||||
console::debug!(format!("return_location -> {:?}", l).as_str());
|
||||
TemporaryStorage::delete("return_location");
|
||||
l.unwrap_or(Location::Manager(Route::Landing))
|
||||
}
|
||||
|
||||
pub fn push_oauth2_authorisation_request(r: AuthorisationRequest) {
|
||||
TemporaryStorage::set("oauth2_authorisation_request", r).expect_throw("failed to set header");
|
||||
TemporaryStorage::set("oauth2_authorisation_request", r)
|
||||
.expect_throw("failed to set oauth2_authorisation_request in temporary storage");
|
||||
}
|
||||
|
||||
pub fn pop_oauth2_authorisation_request() -> Option<AuthorisationRequest> {
|
||||
let l: Result<AuthorisationRequest, _> = TemporaryStorage::get("oauth2_authorisation_request");
|
||||
console::log!(format!("oauth2_authorisation_request -> {:?}", l).as_str());
|
||||
console::debug!(format!("oauth2_authorisation_request -> {:?}", l).as_str());
|
||||
TemporaryStorage::delete("oauth2_authorisation_request");
|
||||
l.ok()
|
||||
}
|
||||
|
||||
pub fn push_login_hint(r: String) {
|
||||
TemporaryStorage::set("login_hint", r).expect_throw("failed to set header");
|
||||
TemporaryStorage::set("login_hint", r).expect_throw("failed to set login hint");
|
||||
}
|
||||
|
||||
pub fn pop_login_hint() -> Option<String> {
|
||||
let l: Result<String, _> = TemporaryStorage::get("login_hint");
|
||||
console::log!(format!("login_hint -> {:?}", l).as_str());
|
||||
console::debug!(format!("login_hint::pop_login_hint -> {:?}", l).as_str());
|
||||
TemporaryStorage::delete("login_hint");
|
||||
l.ok()
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ enum State {
|
|||
pii_scopes: Vec<String>,
|
||||
consent_token: String,
|
||||
},
|
||||
ConsentGranted,
|
||||
ConsentGranted(String),
|
||||
ErrInvalidRequest,
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ pub struct Oauth2App {
|
|||
|
||||
pub enum Oauth2Msg {
|
||||
LoginProceed,
|
||||
ConsentGranted,
|
||||
ConsentGranted(String),
|
||||
TokenValid,
|
||||
Consent {
|
||||
client_name: String,
|
||||
|
@ -139,7 +139,7 @@ impl Oauth2App {
|
|||
.into_serde()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("serde error -> {:?}", e);
|
||||
console::log!(e_msg.as_str());
|
||||
console::error!(e_msg.as_str());
|
||||
})
|
||||
.expect_throw("Invalid response type");
|
||||
match state {
|
||||
|
@ -226,7 +226,8 @@ impl Component for Oauth2App {
|
|||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
console::log!("oauth2::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("oauth2::create");
|
||||
|
||||
// Do we have a query here?
|
||||
// Did we get sent a valid Oauth2 request?
|
||||
|
@ -239,11 +240,11 @@ impl Component for Oauth2App {
|
|||
.query()
|
||||
.map_err(|e| {
|
||||
let e_msg = format!("lstorage error -> {:?}", e);
|
||||
console::log!(e_msg.as_str());
|
||||
console::error!(e_msg.as_str());
|
||||
})
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
console::log!("pop_oauth2_authorisation_request");
|
||||
console::error!("pop_oauth2_authorisation_request");
|
||||
models::pop_oauth2_authorisation_request()
|
||||
});
|
||||
|
||||
|
@ -260,7 +261,7 @@ impl Component for Oauth2App {
|
|||
};
|
||||
|
||||
let e_msg = format!("{:?}", query);
|
||||
console::log!(e_msg.as_str());
|
||||
console::error!(e_msg.as_str());
|
||||
|
||||
// In the query, if this is openid there MAY be a hint
|
||||
// as to the users name.
|
||||
|
@ -295,12 +296,14 @@ impl Component for Oauth2App {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("oauth2::change");
|
||||
#[cfg(debug)]
|
||||
console::debug!("oauth2::change");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("oauth2::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("oauth2::update");
|
||||
|
||||
match msg {
|
||||
Oauth2Msg::LoginProceed => {
|
||||
|
@ -329,7 +332,7 @@ impl Component for Oauth2App {
|
|||
State::SubmitAuthReq(token.clone())
|
||||
}
|
||||
_ => {
|
||||
console::log!("Invalid state transition");
|
||||
console::error!("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
|
@ -350,17 +353,18 @@ impl Component for Oauth2App {
|
|||
consent_token,
|
||||
},
|
||||
_ => {
|
||||
console::log!("Invalid state transition");
|
||||
console::error!("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
true
|
||||
}
|
||||
Oauth2Msg::ConsentGranted => {
|
||||
Oauth2Msg::ConsentGranted(_) => {
|
||||
self.state = match &self.state {
|
||||
State::Consent {
|
||||
token,
|
||||
consent_token,
|
||||
client_name,
|
||||
..
|
||||
} => {
|
||||
let token_c = token.clone();
|
||||
|
@ -371,10 +375,10 @@ impl Component for Oauth2App {
|
|||
Err(v) => v.into(),
|
||||
}
|
||||
});
|
||||
State::ConsentGranted
|
||||
State::ConsentGranted(client_name.to_string())
|
||||
}
|
||||
_ => {
|
||||
console::log!("Invalid state transition");
|
||||
console::error!("Invalid state transition");
|
||||
State::ErrInvalidRequest
|
||||
}
|
||||
};
|
||||
|
@ -383,12 +387,13 @@ impl Component for Oauth2App {
|
|||
}
|
||||
Oauth2Msg::Error { emsg, kopid } => {
|
||||
self.state = State::ErrInvalidRequest;
|
||||
console::log!(format!("{:?}", kopid).as_str());
|
||||
console::log!(emsg.as_str());
|
||||
console::error!(format!("{:?}", kopid).as_str());
|
||||
console::error!(emsg.as_str());
|
||||
true
|
||||
}
|
||||
Oauth2Msg::Redirect(loc) => {
|
||||
console::log!(format!("Redirecting to {}", loc).as_str());
|
||||
#[cfg(debug)]
|
||||
console::debug!(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
|
||||
|
@ -400,7 +405,7 @@ impl Component for Oauth2App {
|
|||
Ok(_) => false,
|
||||
Err(e) => {
|
||||
// Something went bang, opps.
|
||||
console::log!(format!("{:?}", e).as_str());
|
||||
console::error!(format!("{:?}", e).as_str());
|
||||
self.state = State::ErrInvalidRequest;
|
||||
true
|
||||
}
|
||||
|
@ -410,30 +415,35 @@ impl Component for Oauth2App {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
crate::utils::autofocus();
|
||||
console::log!("oauth2::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("oauth2::rendered");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("oauth2::view");
|
||||
match &self.state {
|
||||
#[cfg(debug)]
|
||||
console::debug!("oauth2::view");
|
||||
|
||||
let body_content = match &self.state {
|
||||
State::LoginRequired => {
|
||||
// <body class="html-body form-body">
|
||||
|
||||
html! {
|
||||
<main class="form-signin">
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("oauth2::view -> LoginRequired - prevent_default()");
|
||||
console::debug!("oauth2::view -> LoginRequired - prevent_default()");
|
||||
e.prevent_default();
|
||||
Oauth2Msg::LoginProceed
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<h1 class="h3 mb-3 fw-normal">{" Sign in to proceed" }</h1>
|
||||
<button id="autofocus" class="w-100 btn btn-lg btn-primary" type="submit">{ "Sign in" }</button>
|
||||
<h1 class="h3 mb-3 fw-normal">
|
||||
// TODO: include the domain display name here
|
||||
{"Sign in to proceed" }
|
||||
</h1>
|
||||
<button autofocus=true class="w-100 btn btn-lg btn-primary" type="submit">
|
||||
{ "Sign in" }
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
State::Consent {
|
||||
|
@ -448,54 +458,86 @@ impl Component for Oauth2App {
|
|||
let pii_req = if pii_scopes.is_empty() {
|
||||
html! {
|
||||
<div>
|
||||
<p>{ "This site will not have access to your personal information" }</p>
|
||||
<p>{ "If this site requests personal information in the future we will check with you" }</p>
|
||||
<p>{ "This site will not have access to your personal information." }</p>
|
||||
<p>{ "If this site requests personal information in the future we will check with you." }</p>
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<div>
|
||||
<p>{ "This site has requested to see the following personal information" }</p>
|
||||
<p>{ "This site has requested to see the following personal information." }</p>
|
||||
<ul>
|
||||
{
|
||||
pii_scopes.iter().map(|s| html! { <li>{ s }</li> } ).collect::<Html>()
|
||||
}
|
||||
</ul>
|
||||
<p>{ "If this site requests different personal information in the future we will check with you again" }</p>
|
||||
<p>{ "If this site requests different personal information in the future we will check with you again." }</p>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
|
||||
// <body class="html-body form-body">
|
||||
let app_name = client_name.clone();
|
||||
html! {
|
||||
<main class="form-signin">
|
||||
<form
|
||||
onsubmit={ ctx.link().callback(|e: FocusEvent| {
|
||||
console::log!("oauth2::view -> Consent - prevent_default()");
|
||||
onsubmit={ ctx.link().callback(move |e: FocusEvent| {
|
||||
console::debug!("oauth2::view -> Consent - prevent_default()");
|
||||
e.prevent_default();
|
||||
Oauth2Msg::ConsentGranted
|
||||
Oauth2Msg::ConsentGranted(format!("{}", client_name))
|
||||
} ) }
|
||||
action="javascript:void(0);"
|
||||
>
|
||||
<h2 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ client_name }</h2>
|
||||
<h2 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ app_name }</h2>
|
||||
{ pii_req }
|
||||
|
||||
<button id="autofocus" class="w-100 btn btn-lg btn-primary" type="submit">{ "Proceed" }</button>
|
||||
<div class="text-center">
|
||||
<button autofocus=true class="w-100 btn btn-lg btn-primary" type="submit">{ "Proceed" }</button>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
State::ConsentGranted | State::SubmitAuthReq(_) | State::TokenCheck(_) => {
|
||||
html! { <div> <h1>{ " ... " }</h1> </div> }
|
||||
State::ConsentGranted(app_name) => {
|
||||
html! {
|
||||
<div class="alert alert-success" role="alert">
|
||||
<h2 class="text-center">{ "Taking you to " }{app_name}{" ... " }</h2>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
State::SubmitAuthReq(_) | State::TokenCheck(_) => {
|
||||
html! {
|
||||
<div class="alert alert-light" role="alert">
|
||||
<h2 class="text-center">{ "Processing ... " }</h2>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
State::ErrInvalidRequest => {
|
||||
html! { <div> <h1>{ " ❌ " }</h1> </div> }
|
||||
html! {
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h1>{ "Invalid request" } </h1>
|
||||
<p>
|
||||
{ "Please close this window and try again again from the beginning." }
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
};
|
||||
html! {
|
||||
<>
|
||||
<main class="form-signin">
|
||||
<center>
|
||||
<img src="/pkg/img/logo-square.svg" alt="Kanidm" class="kanidm_logo"/>
|
||||
</center>
|
||||
<div class="container">
|
||||
{ body_content }
|
||||
</div>
|
||||
</main>
|
||||
{ crate::utils::do_footer() }
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
console::log!("oauth2::destroy");
|
||||
console::debug!("oauth2::destroy");
|
||||
remove_body_form_classes!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use wasm_bindgen::prelude::*;
|
|||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
pub use web_sys::InputEvent;
|
||||
use web_sys::{Document, Event, /*HtmlButtonElement,*/ HtmlElement, HtmlInputElement, Window};
|
||||
use yew::html;
|
||||
use yew::virtual_dom::VNode;
|
||||
|
||||
pub fn window() -> Window {
|
||||
web_sys::window().expect_throw("Unable to retrieve window")
|
||||
|
@ -18,13 +20,16 @@ pub fn body() -> HtmlElement {
|
|||
document().body().expect_throw("Unable to retrieve body")
|
||||
}
|
||||
|
||||
pub fn autofocus() {
|
||||
// Once rendered if an element with id autofocus exists, focus it.
|
||||
pub fn autofocus(target: &str) {
|
||||
// If an element with an id attribute matching 'target' exists, focus it.
|
||||
let doc = document();
|
||||
if let Some(element) = doc.get_element_by_id("autofocus") {
|
||||
if let Some(element) = doc.get_element_by_id(target) {
|
||||
if let Ok(htmlelement) = element.dyn_into::<web_sys::HtmlElement>() {
|
||||
if htmlelement.focus().is_err() {
|
||||
console::log!("unable to autofocus.");
|
||||
console::warn!(
|
||||
"unable to autofocus element, couldn't find target with id '{}'",
|
||||
target
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,3 +71,14 @@ pub fn get_value_from_element_id(id: &str) -> Option<String> {
|
|||
extern "C" {
|
||||
pub fn modal_hide_by_id(m: &str);
|
||||
}
|
||||
|
||||
/// Returns the footer node for the UI
|
||||
pub fn do_footer() -> VNode {
|
||||
html! {
|
||||
<footer class="footer mt-auto py-3 bg-light text-end">
|
||||
<div class="container">
|
||||
<span class="text-muted">{ "Powered by " }<a href="https://kanidm.com">{ "Kanidm" }</a></span>
|
||||
</div>
|
||||
</footer>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use yew::prelude::*;
|
||||
|
||||
|
@ -12,17 +13,20 @@ impl Component for AppsApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("views::apps::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::apps::create");
|
||||
AppsApp {}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("views::apps::changed");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::apps::changed");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
console::log!("views::apps::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::apps::update");
|
||||
/*
|
||||
match msg {
|
||||
ViewsMsg::Logout => {
|
||||
|
@ -33,7 +37,8 @@ impl Component for AppsApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("views::apps::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::apps::rendered");
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::error::*;
|
||||
use crate::models;
|
||||
use crate::utils;
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use yew::prelude::*;
|
||||
|
||||
|
@ -70,7 +71,8 @@ impl From<FetchError> for ViewsMsg {
|
|||
}
|
||||
|
||||
fn switch(route: &ViewRoute) -> Html {
|
||||
console::log!("views::switch");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::switch");
|
||||
|
||||
// safety - can't panic because to get to this location we MUST be authenticated!
|
||||
let token =
|
||||
|
@ -91,7 +93,8 @@ impl Component for ViewsApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
console::log!("views::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::create");
|
||||
|
||||
// Ensure the token is valid before we proceed. Could be
|
||||
// due to a session expiry or something else, but we want to make
|
||||
|
@ -115,12 +118,14 @@ impl Component for ViewsApp {
|
|||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("views::changed");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::changed");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("views::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::update");
|
||||
match msg {
|
||||
ViewsMsg::Verified(token) => {
|
||||
self.state = State::Authenticated(token);
|
||||
|
@ -139,7 +144,8 @@ impl Component for ViewsApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("views::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::rendered");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
|
@ -177,19 +183,20 @@ impl Component for ViewsApp {
|
|||
State::Error { emsg, kopid } => {
|
||||
html! {
|
||||
<main class="form-signin">
|
||||
<div class="container">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h2>{ "An Error Occured 🥺" }</h2>
|
||||
</div>
|
||||
<p>{ emsg.to_string() }</p>
|
||||
<p>
|
||||
{
|
||||
if let Some(opid) = kopid.as_ref() {
|
||||
format!("Operation ID: {}", opid)
|
||||
} else {
|
||||
"Local Error".to_string()
|
||||
"Error occurred client-side.".to_string()
|
||||
}
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
}
|
||||
}
|
||||
|
@ -209,8 +216,6 @@ impl ViewsApp {
|
|||
<button class="navbar-toggler bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<img src="/pkg/img/favicon.png" />
|
||||
</button>
|
||||
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="mb-1">
|
||||
|
|
|
@ -23,17 +23,20 @@ impl Component for ProfileApp {
|
|||
type Properties = ViewProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("views::profile::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::create");
|
||||
ProfileApp {}
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("views::profile::changed");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::changed");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
console::log!("views::profile::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::update");
|
||||
/*
|
||||
match msg {
|
||||
ViewsMsg::Logout => {
|
||||
|
@ -44,17 +47,20 @@ impl Component for ProfileApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("views::profile::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::rendered");
|
||||
}
|
||||
|
||||
/// UI view for the user profile
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
console::log!("views::profile::starting view");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::profile::starting view");
|
||||
|
||||
// Submit a req to init the session.
|
||||
// The uuid we want to submit against - hint, it's us.
|
||||
let token = ctx.props().token.clone();
|
||||
console::log!("token: ", &token);
|
||||
#[cfg(debug)]
|
||||
console::debug!("token: ", &token);
|
||||
|
||||
let jwtu = JwsUnverified::from_str(&token).expect_throw("Invalid UAT, unable to parse");
|
||||
|
||||
|
@ -63,15 +69,14 @@ impl Component for ProfileApp {
|
|||
.expect_throw("Unvalid UAT, unable to release ");
|
||||
|
||||
let id = uat.inner.uuid.to_string();
|
||||
|
||||
console::log!("uuid:", id);
|
||||
console::debug!("uuid:", id);
|
||||
// let valid_token = ctx.link().send_future(async {
|
||||
// match Self::fetch_token_valid(id, token).await {
|
||||
// Ok(v) => v,
|
||||
// Err(v) => v.into(),
|
||||
// }
|
||||
// });
|
||||
// console::log!("valid_token: {:?}");
|
||||
// console::debug!("valid_token: {:?}");
|
||||
|
||||
html! {
|
||||
<>
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::manager::Route;
|
|||
use crate::views::{ViewProps, ViewRoute};
|
||||
|
||||
use compact_jwt::{Jws, JwsUnverified};
|
||||
#[cfg(debug)]
|
||||
use gloo::console;
|
||||
use std::str::FromStr;
|
||||
use yew::prelude::*;
|
||||
|
@ -56,17 +57,20 @@ impl Component for SecurityApp {
|
|||
type Properties = ViewProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("views::security::create");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::security::create");
|
||||
SecurityApp { state: State::Init }
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
console::log!("views::security::changed");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::security::changed");
|
||||
false
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
console::log!("views::security::update");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::security::update");
|
||||
match msg {
|
||||
Msg::RequestCredentialUpdate => {
|
||||
// Submit a req to init the session.
|
||||
|
@ -113,7 +117,8 @@ impl Component for SecurityApp {
|
|||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
console::log!("views::security::rendered");
|
||||
#[cfg(debug)]
|
||||
console::debug!("views::security::rendered");
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
|
|
|
@ -6,10 +6,9 @@ async function main() {
|
|||
}
|
||||
main()
|
||||
|
||||
// this is used in
|
||||
// this is used in modals
|
||||
export function modal_hide_by_id(m) {
|
||||
var elem = document.getElementById(m);
|
||||
var modal = bootstrap.Modal.getInstance(elem);
|
||||
modal.hide();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue