diff --git a/Cargo.lock b/Cargo.lock index eff54ca..8711810 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -997,6 +997,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto_box" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd26c32de5307fd08aac445a75c43472b14559d5dccdfba8022dbcd075838ebc" +dependencies = [ + "aead", + "blake2", + "chacha20", + "chacha20poly1305", + "salsa20", + "x25519-dalek 1.1.1", + "xsalsa20poly1305", + "zeroize", +] + [[package]] name = "cssparser" version = "0.27.2" @@ -2768,6 +2784,8 @@ name = "ng-sdk-js" version = "0.1.0" dependencies = [ "async-std", + "base64-url", + "crypto_box", "futures", "getrandom 0.1.16", "gloo-timers", @@ -2777,6 +2795,7 @@ dependencies = [ "p2p-net", "p2p-repo", "pharos", + "rand 0.7.3", "serde", "serde-wasm-bindgen", "serde_bare", @@ -2822,6 +2841,7 @@ dependencies = [ "env_logger", "log", "ng-wallet", + "p2p-client-ws", "p2p-net", "p2p-repo", "rust-embed", @@ -2937,7 +2957,7 @@ dependencies = [ "chacha20poly1305", "noise-protocol", "sha2 0.10.7", - "x25519-dalek", + "x25519-dalek 2.0.0-rc.2", "zeroize", ] @@ -4019,6 +4039,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5982,6 +6011,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "x25519-dalek" version = "2.0.0-rc.2" @@ -6000,6 +6040,19 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47f9da296a88b6bc150b896d17770a62d4dc6f63ecf0ed10a9c08a1cb3d12f24" +[[package]] +name = "xsalsa20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7" +dependencies = [ + "aead", + "poly1305", + "salsa20", + "subtle", + "zeroize", +] + [[package]] name = "xxhash-rust" version = "0.8.6" diff --git a/ng-app/src/App.svelte b/ng-app/src/App.svelte index e06f7bd..dee103e 100644 --- a/ng-app/src/App.svelte +++ b/ng-app/src/App.svelte @@ -12,31 +12,131 @@
+
diff --git a/ng-app/src/api.ts b/ng-app/src/api.ts index 3a2d939..f3b4e46 100644 --- a/ng-app/src/api.ts +++ b/ng-app/src/api.ts @@ -96,6 +96,8 @@ const handler = { return await tauri.invoke(path[0],{params}) } else if (path[0].starts_with("get_local_bootstrap")) { return false; + } else if (path[0].starts_with("get_local_url")) { + return false; } else { let arg = {}; @@ -112,6 +114,8 @@ export const NG_EU_BSP = "https://nextgraph.eu"; export const NG_EU_BSP_REGISTER = "https://account.nextgraph.eu/#/create"; export const NG_EU_BSP_REGISTERED = "https://nextgraph.eu/#/user/registered"; +export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered"; + export const NG_NET_BSP = "https://nextgraph.net"; export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create"; export const NG_NET_BSP_REGISTERED = "https://nextgraph.net/#/user/registered"; diff --git a/ng-app/src/lib/Home.svelte b/ng-app/src/lib/Home.svelte index 27d6f48..0fe0394 100644 --- a/ng-app/src/lib/Home.svelte +++ b/ng-app/src/lib/Home.svelte @@ -12,20 +12,21 @@ -
-
-
-

Welcome to NextGraph

+{#if !$has_wallets || display_login_create} +
+
+
+

Welcome to NextGraph

- {#if display_login_create}

We could not find a wallet saved on this device.
If you already have a wallet, select "Log in", otherwise, select "Create Wallet" here below @@ -80,5 +81,5 @@ - {/if} -

+
+{/if} diff --git a/ng-app/src/lib/Login.svelte b/ng-app/src/lib/Login.svelte new file mode 100644 index 0000000..d99405e --- /dev/null +++ b/ng-app/src/lib/Login.svelte @@ -0,0 +1,421 @@ + + + + +{#if step == "load"} +
+ Loading... + + + + +
+{:else if step == "pazzle"} +
+ {#each [0, 1, 2, 3, 4] as row} +
+ {#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} +
select(row * 3 + i)} + on:keypress={() => select(row * 3 + i)} + > + +
+ {/each} +
+ {/each} +
+{:else if step == "order"} + +
+ {#each [0, 1, 2] as row} +
+ {#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} + {#if !emoji.sel} +
select_order(emoji, row * 3 + i)} + on:keypress={() => select_order(emoji, row * 3 + i)} + > + +
+ {:else} +
+ + {emoji.sel} +
+ {/if} + {/each} +
+ {/each} +
+{:else if step == "pin"} +
+

+ Enter your PIN code +

+
+ {#each [0, 1, 2] as row} +
+ {#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num} + + {/each} +
+ {/each} + +
+
+{:else if step == "opening"} +
+ Opening your wallet...
+ Please wait + + + + +
+{:else if step == "end"} + {#if error} +
+ An error occurred ! + + + {error} + + + +
+ {:else} +
+ Your wallet is opened! + +
+ {/if} +{/if} + + diff --git a/ng-app/src/routes/Grid.svelte b/ng-app/src/routes/Grid.svelte deleted file mode 100644 index 87e8312..0000000 --- a/ng-app/src/routes/Grid.svelte +++ /dev/null @@ -1,287 +0,0 @@ - - - - -{#if step == "pazzle"} -
- {#each [0, 1, 2, 3, 4] as row} -
- {#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} -
select(row * 3 + i)} - on:keypress={() => select(row * 3 + i)} - > - -
- {/each} -
- {/each} -
-{:else if step == "order"} - -
- {#each [0, 1, 2] as row} -
- {#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} - {#if !emoji.sel} -
select_order(emoji, row * 3 + i)} - on:keypress={() => select_order(emoji, row * 3 + i)} - > - -
- {:else} -
- - {emoji.sel} -
- {/if} - {/each} -
- {/each} -
-{:else if step == "pin"} -
- {#each [0, 1] as row} -
- {#each shuffle_pin.slice(0 + row * 5, 5 + row * 5) as num, i} -
await pin(num)} - on:keypress={async () => await pin(num)} - > - {num} -
- {/each} -
- {/each} -
-{:else if step == "end"}{/if} - - diff --git a/ng-app/src/routes/Home.svelte b/ng-app/src/routes/Home.svelte index da0a910..afedb38 100644 --- a/ng-app/src/routes/Home.svelte +++ b/ng-app/src/routes/Home.svelte @@ -13,25 +13,34 @@ import { Button } from "flowbite-svelte"; import { link } from "svelte-spa-router"; import Home from "../lib/Home.svelte"; - import { onMount } from "svelte"; + import { push } from "svelte-spa-router"; + import { onMount, onDestroy } from "svelte"; + import { + wallets, + active_wallet, + opened_wallets, + active_session, + has_wallets, + derived, + } from "../store"; - let display_login_create = false; + let unsubscribe; + onMount(() => { + const combined = derived([active_wallet, has_wallets], ([$s1, $s2]) => [ + $s1, + $s2, + ]); + unsubscribe = combined.subscribe((value) => { + console.log(value); + if (!value[0] && value[1]) { + push("#/wallet/login"); + } + }); + }); - async function bootstrap() { - let bs; - try { - bs = localStorage.getItem("bootstrap"); - } catch (e) {} - if (bs) { - } else { - // probe localhost and LAN - - // if nothing found, displays login/create account - console.log("no wallet found"); - display_login_create = true; - } - } - onMount(() => bootstrap()); + onDestroy(() => { + unsubscribe(); + }); - + diff --git a/ng-app/src/routes/Install.svelte b/ng-app/src/routes/Install.svelte index df77f23..9599283 100644 --- a/ng-app/src/routes/Install.svelte +++ b/ng-app/src/routes/Install.svelte @@ -12,23 +12,16 @@ + +
+
+ + +
+ {#if error} +
+ + {#if error == "AlreadyExists"} +

+ The user is already registered with the selected broker.
Try logging + in instead +

+ + + + {:else} +

+ An error occurred:
{error} +

+ + + + {/if} +
+ {:else if invite && user} +
+ +

+ You have been successfully
registered {#if invitation?.V0?.name} + to {invitation?.V0?.name}{/if} +

+
+ {/if} +
+ + diff --git a/ng-app/src/routes/WalletCreate.svelte b/ng-app/src/routes/WalletCreate.svelte index ad21528..cbb7cd2 100644 --- a/ng-app/src/routes/WalletCreate.svelte +++ b/ng-app/src/routes/WalletCreate.svelte @@ -12,20 +12,24 @@ + +{#if wallet} + +{:else if !$active_wallet && !selected} +
+
+
+

Select a wallet for log in

+
+ {#each Object.entries($wallets) as wallet_entry} +
{ + select(wallet_entry[0]); + }} + on:keypress={() => { + select(wallet_entry[0]); + }} + > + {wallet_entry[1].wallet.V0.content.security_txt} + {wallet_entry[0]} + {wallet_entry[1].wallet.V0.content.security_txt} +
+ {/each} +
+ {#if $has_wallets}

Log in with another wallet

+ {:else}

Import your wallet

+ {/if} + + + + + + +
+
+
+{:else if step == "security"}{:else if step == "qrcode"}{:else if step == "drop"}{:else if step == "cloud"}{:else if step == "loggedin"}you + are logged in{/if} + + diff --git a/ng-app/src/store.ts b/ng-app/src/store.ts index 75157fe..85813c0 100644 --- a/ng-app/src/store.ts +++ b/ng-app/src/store.ts @@ -1,33 +1,113 @@ -import { writable } from "svelte/store"; +import { writable, readonly, derived } from "svelte/store"; import ng from "./api"; +let all_branches = {}; + +export const opened_wallets = writable({}); + +/// { wallet:, id: } +export const active_wallet = writable(undefined); + +export const wallets = writable({}); + +export const has_wallets = derived(wallets,($wallets) => Object.keys($wallets).length); + +export const active_session = writable(undefined); + +export const set_active_session = function(session) { + let v = session.users.values().next().value; + v.branches_last_seq = Object.fromEntries(v.branches_last_seq); + let users = Object.fromEntries(session.users); + active_session.set(users); +}; + +export { writable, readonly, derived }; + +const close_active_wallet = function() { + + active_session.set(undefined); + active_wallet.update((w) => { + delete w.wallet; + }); + +} + const branch_commits = (nura, sub) => { + // console.log("branch_commits") + // const { subscribe, set, update } = writable([]); // create the underlying writable store + + // let unsub = () => {}; + // return { + // load: async () => { + // console.log("load") + // unsub = await ng.doc_sync_branch(nura, async (commit) => { + // console.log(commit); + // update( (old) => {old.unshift(commit); return old;} ) + // }); + // }, + // subscribe: (run, invalid) => { + // console.log("sub") + // let upper_unsub = subscribe(run, invalid); - const { subscribe, set, update } = writable([]); // create the underlying writable store + // return () => { + // upper_unsub(); + // unsub(); + // } + // } + // // set: (value) => { + // // localStorage.setItem(key, toString(value)); // save also to local storage as a string + // // return set(value); + // // }, + // // update, + // }; - let unsub = () => {}; + return { load: async () => { - unsub = await ng.doc_sync_branch(nura, async (commit) => { - console.log(commit); - update( (old) => {old.unshift(commit); return old;} ) - }); + let already_subscribed = all_branches[nura]; + if (!already_subscribed) return; + if (already_subscribed.load) { + await already_subscribed.load(); + already_subscribed.load = undefined; + } }, subscribe: (run, invalid) => { - - let upper_unsub = subscribe(run, invalid); - + let already_subscribed = all_branches[nura]; + if (!already_subscribed) { + const { subscribe, set, update } = writable([]); // create the underlying writable store + let count = 0; + let unsub = () => {}; + already_subscribed = { + load: async () => { + unsub = await ng.doc_sync_branch(nura, async (commit) => { + console.log(commit); + update( (old) => {old.unshift(commit); return old;} ) + }); + }, + increase: () => { + count += 1; + return readonly({subscribe}); + }, + decrease: () => { + count -= 1; + if (count == 0) { + unsub(); + delete all_branches[nura]; + } + }, + } + all_branches[nura] = already_subscribed; + } + + let new_store = already_subscribed.increase(); + let read_unsub = new_store.subscribe(run, invalid); return () => { - upper_unsub(); - unsub(); + read_unsub(); + already_subscribed.decrease(); } + } - // set: (value) => { - // localStorage.setItem(key, toString(value)); // save also to local storage as a string - // return set(value); - // }, - // update, - }; + } }; export default branch_commits; \ No newline at end of file diff --git a/ng-app/src/styles.css b/ng-app/src/styles.css index f50cfdb..e14b629 100644 --- a/ng-app/src/styles.css +++ b/ng-app/src/styles.css @@ -63,7 +63,7 @@ body { } #app { - max-width: 1280px; + /*max-width: 1280px;*/ margin: 0 auto; padding: 0rem; text-align: center; diff --git a/ng-sdk-js/Cargo.toml b/ng-sdk-js/Cargo.toml index 5990f0e..5f492d9 100644 --- a/ng-sdk-js/Cargo.toml +++ b/ng-sdk-js/Cargo.toml @@ -29,6 +29,9 @@ serde_bytes = "0.11.7" # snow = "0.9.2" getrandom = { version = "0.1.1", features = ["wasm-bindgen"] } serde_json = "1.0" +crypto_box = { version = "0.8.2", features = ["seal"] } +rand = { version = "0.7", features = ["getrandom"] } +base64-url = "2.0.0" # [target.'cfg(target_arch = "wasm32")'.dependencies.getrandom] # version = "0.2.7" diff --git a/ng-sdk-js/js/browser.js b/ng-sdk-js/js/browser.js index e0edfa3..ae46a5f 100644 --- a/ng-sdk-js/js/browser.js +++ b/ng-sdk-js/js/browser.js @@ -3,7 +3,7 @@ export function client_details() { } export function client_details2(obj,version) { - console.log("version",version) + //console.log("version",version) obj.browser.appVersion = navigator?.appVersion; obj.browser.arch = navigator?.platform; obj.browser.vendor = navigator?.vendor; @@ -11,3 +11,45 @@ export function client_details2(obj,version) { obj.engine.sdk = version; return JSON.stringify(obj); } + +export function session_save(key,value) { + try { + sessionStorage.setItem(key, value); + + } catch(e) { + console.error(e); + return e.message; + } +} + +export function session_get(key) { + + try { + return sessionStorage.getItem(key); + + } catch(e) { + console.error(e); + } + +} + +export function local_save(key,value) { + try { + localStorage.setItem(key, value); + + } catch(e) { + console.error(e); + return e.message; + } +} + +export function local_get(key) { + + try { + return localStorage.getItem(key); + + } catch(e) { + console.error(e); + } + +} \ No newline at end of file diff --git a/ng-sdk-js/js/node.js b/ng-sdk-js/js/node.js index 88f9360..6704151 100644 --- a/ng-sdk-js/js/node.js +++ b/ng-sdk-js/js/node.js @@ -119,4 +119,20 @@ module.exports.client_details = function () { versions: process.versions } }); -}; \ No newline at end of file +}; + +module.exports.session_save = function(key,value) { + +} + +module.exports.session_get = function(key) { + +} + +module.exports.local_save = function(key,value) { + +} + +module.exports.local_get = function(key) { + +} \ No newline at end of file diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs index b09f314..237152c 100644 --- a/ng-sdk-js/src/lib.rs +++ b/ng-sdk-js/src/lib.rs @@ -22,15 +22,20 @@ use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket; use p2p_net::broker::*; use p2p_net::connection::{ClientConfig, StartConfig}; use p2p_net::types::{ - BootstrapContentV0, ClientInfo, ClientInfoV0, ClientType, CreateAccountBSP, DirectPeerId, IP, + BootstrapContent, BootstrapContentV0, ClientId, ClientInfo, ClientInfoV0, ClientType, + CreateAccountBSP, DirectPeerId, UserId, IP, +}; +use p2p_net::utils::{ + decode_invitation_string, retrieve_local_bootstrap, retrieve_local_url, spawn_and_log_error, + Receiver, ResultSend, Sender, }; -use p2p_net::utils::{retrieve_local_bootstrap, spawn_and_log_error, Receiver, ResultSend, Sender}; use p2p_net::WS_PORT; use p2p_repo::log::*; use p2p_repo::types::*; use p2p_repo::utils::generate_keypair; use serde::{Deserialize, Serialize}; use serde_json::json; +use std::collections::HashMap; use std::net::IpAddr; use std::str::FromStr; use std::sync::Arc; @@ -49,6 +54,28 @@ pub async fn get_local_bootstrap(location: String, invite: JsValue) -> JsValue { } } +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub async fn decode_invitation(invite: String) -> JsValue { + let res = decode_invitation_string(invite); + if res.is_some() { + serde_wasm_bindgen::to_value(&res.unwrap()).unwrap() + } else { + JsValue::FALSE + } +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub async fn get_local_url(location: String) -> JsValue { + let res = retrieve_local_url(location).await; + if res.is_some() { + serde_wasm_bindgen::to_value(&res.unwrap()).unwrap() + } else { + JsValue::FALSE + } +} + #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue { @@ -91,6 +118,190 @@ pub fn wallet_open_wallet_with_pazzle( } } +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub fn wallet_update(js_wallet_id: JsValue, js_operations: JsValue) -> Result { + let wallet = serde_wasm_bindgen::from_value::(js_wallet_id) + .map_err(|_| "Deserialization error of WalletId")?; + let operations = serde_wasm_bindgen::from_value::>(js_operations) + .map_err(|_| "Deserialization error of operations")?; + unimplemented!(); + // match res { + // Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()), + // Err(e) => Err(e.to_string().into()), + // } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct SessionWalletStorageV0 { + // string is base64_url encoding of userId(pubkey) + users: HashMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +enum SessionWalletStorage { + V0(SessionWalletStorageV0), +} + +impl SessionWalletStorageV0 { + fn new() -> Self { + SessionWalletStorageV0 { + users: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct SessionPeerStorageV0 { + user: UserId, + peer_key: PrivKey, + last_wallet_nonce: u64, + // string is base64_url encoding of branchId(pubkey) + branches_last_seq: HashMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct LocalWalletStorageV0 { + bootstrap: BootstrapContent, + wallet: Wallet, + client: ClientId, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +enum LocalWalletStorage { + V0(HashMap), +} + +fn get_local_wallets_v0() -> Result, ()> { + let wallets_string = local_get("ng_wallets".to_string()); + if wallets_string.is_some() { + let map_ser = base64_url::decode(&wallets_string.unwrap()).unwrap(); + let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser).unwrap(); + let LocalWalletStorage::V0(v0) = wallets; + Ok(v0) + } else { + Err(()) + } +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub fn get_wallets_from_localstorage() -> JsValue { + let res = get_local_wallets_v0(); + if res.is_ok() { + return serde_wasm_bindgen::to_value(&res.unwrap()).unwrap(); + } + JsValue::UNDEFINED +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub fn get_local_session(id: String, key_js: JsValue, user_js: JsValue) -> JsValue { + let res = session_get(format!("ng_wallet@{}", id)); + if res.is_some() { + log_debug!("RESUMING SESSION"); + let key = serde_wasm_bindgen::from_value::(key_js).unwrap(); + let decoded = base64_url::decode(&res.unwrap()).unwrap(); + let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), &decoded).unwrap(); + let session: SessionWalletStorage = serde_bare::from_slice(&session_ser).unwrap(); + let SessionWalletStorage::V0(v0) = session; + return serde_wasm_bindgen::to_value(&v0).unwrap(); + } else { + // create a new session + let user = serde_wasm_bindgen::from_value::(user_js).unwrap(); + let wallet_id: PubKey = id.as_str().try_into().unwrap(); + let session_v0 = create_new_session(&id, wallet_id, user); + if session_v0.is_err() { + return JsValue::UNDEFINED; + } + return serde_wasm_bindgen::to_value(&session_v0.unwrap()).unwrap(); + } + JsValue::UNDEFINED +} + +fn create_new_session( + wallet_name: &String, + wallet_id: PubKey, + user: PubKey, +) -> Result { + let peer = generate_keypair(); + let mut sws = SessionWalletStorageV0::new(); + let sps = SessionPeerStorageV0 { + user, + peer_key: peer.0, + last_wallet_nonce: 0, + branches_last_seq: HashMap::new(), + }; + sws.users.insert(user.to_string(), sps); + let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap(); + let mut rng = crypto_box::aead::OsRng {}; + let cipher = crypto_box::seal(&mut rng, &wallet_id.to_dh_slice().into(), &sws_ser); + if cipher.is_ok() { + let encoded = base64_url::encode(&cipher.unwrap()); + let r = session_save(format!("ng_wallet@{}", wallet_name), encoded); + if r.is_some() { + return Err(r.unwrap()); + } + } + Ok(sws) +} + +fn save_wallet_locally(res: &CreateWalletResultV0) -> Result { + // let mut sws = SessionWalletStorageV0::new(); + // let sps = SessionPeerStorageV0 { + // user: res.user, + // peer_key: res.peer_key.clone(), + // last_wallet_nonce: res.nonce, + // branches_last_seq: HashMap::new(), + // }; + // sws.users.insert(res.user.to_string(), sps); + // let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap(); + // let mut rng = crypto_box::aead::OsRng {}; + // let cipher = crypto_box::seal(&mut rng, &res.wallet.id().to_dh_slice().into(), &sws_ser); + // if cipher.is_ok() { + // let encoded = base64_url::encode(&cipher.unwrap()); + // let r = session_save(format!("ng_wallet@{}", res.wallet_name), encoded); + // if r.is_some() { + // return Err(r.unwrap()); + // } + // } + let sws = create_new_session(&res.wallet_name, res.wallet.id(), res.user)?; + let mut wallets: HashMap = + get_local_wallets_v0().unwrap_or(HashMap::new()); + // TODO: check that the wallet is not already present in localStorage + let lws = LocalWalletStorageV0 { + bootstrap: BootstrapContent::V0(BootstrapContentV0 { servers: vec![] }), + wallet: res.wallet.clone(), + client: res.client.priv_key.to_pub(), + }; + wallets.insert(res.wallet_name.clone(), lws); + let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap(); + let encoded = base64_url::encode(&lws_ser); + let r = local_save("ng_wallets".to_string(), encoded); + if r.is_some() { + return Err(r.unwrap()); + } + Ok(sws) +} + +#[cfg(not(wasmpack_target = "nodejs"))] +#[wasm_bindgen(module = "/js/browser.js")] +extern "C" { + fn session_save(key: String, value: String) -> Option; + fn session_get(key: String) -> Option; + fn local_save(key: String, value: String) -> Option; + fn local_get(key: String) -> Option; +} + +#[cfg(wasmpack_target = "nodejs")] +#[wasm_bindgen(module = "/js/node.js")] +extern "C" { + fn session_save(key: String, value: String) -> Option; + fn session_get(key: String) -> Option; + fn local_save(key: String, value: String) -> Option; + fn local_get(key: String) -> Option; +} + #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub async fn wallet_create_wallet(js_params: JsValue) -> Result { @@ -99,7 +310,10 @@ pub async fn wallet_create_wallet(js_params: JsValue) -> Result Ok(serde_wasm_bindgen::to_value(&r).unwrap()), + Ok(r) => { + let session = save_wallet_locally(&r)?; + Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap()) + } Err(e) => Err(e.to_string().into()), } } @@ -133,7 +347,7 @@ extern "C" { #[cfg(wasmpack_target = "nodejs")] #[wasm_bindgen] -pub fn client_info() -> ClientInfoV0 { +pub fn client_info() -> JsValue { let res = ClientInfoV0 { client_type: ClientType::NodeService, details: client_details(), @@ -141,8 +355,8 @@ pub fn client_info() -> ClientInfoV0 { timestamp_install: 0, timestamp_updated: 0, }; - res - //serde_wasm_bindgen::to_value(&res).unwrap() + //res + serde_wasm_bindgen::to_value(&res).unwrap() } #[cfg(target_arch = "wasm32")] @@ -177,8 +391,7 @@ extern "C" { } #[cfg(all(not(wasmpack_target = "nodejs"), target_arch = "wasm32"))] -#[wasm_bindgen] -pub fn client_info() -> ClientInfoV0 { +pub fn client_info_() -> ClientInfoV0 { let ua = client_details(); let bowser = Bowser::parse(ua); @@ -197,6 +410,13 @@ pub fn client_info() -> ClientInfoV0 { //serde_wasm_bindgen::to_value(&res).unwrap() } +#[cfg(all(not(wasmpack_target = "nodejs"), target_arch = "wasm32"))] +#[wasm_bindgen] +pub fn client_info() -> JsValue { + let res = client_info_(); + serde_wasm_bindgen::to_value(&res).unwrap() +} + #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub async fn test() { @@ -336,7 +556,8 @@ pub async fn start() { user_priv, client, client_priv, - info: ClientInfo::V0(client_info()), + info: ClientInfo::V0(client_info_()), + registration: None, }), ) .await; diff --git a/ng-wallet/src/types.rs b/ng-wallet/src/types.rs index bb90743..100ba3a 100644 --- a/ng-wallet/src/types.rs +++ b/ng-wallet/src/types.rs @@ -104,6 +104,9 @@ pub enum SaveToNGOne { pub struct EncryptedWalletV0 { pub wallet_privkey: PrivKey, + #[zeroize(skip)] + pub wallet_id: String, + #[serde(with = "serde_bytes")] pub pazzle: Vec, @@ -210,7 +213,7 @@ pub struct WalletContentV0 { /// Wallet Log V0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct WalletLogV0 { - pub log: Vec<(u128, WalletOperationV0)>, + pub log: Vec<(u128, WalletOperation)>, } /// Wallet Log @@ -228,11 +231,11 @@ impl WalletLog { impl WalletLogV0 { pub fn new(create_op: WalletOpCreateV0) -> Self { let mut wallet = WalletLogV0 { log: vec![] }; - wallet.add(WalletOperationV0::CreateWalletV0(create_op)); + wallet.add(WalletOperation::CreateWalletV0(create_op)); wallet } - pub fn add(&mut self, op: WalletOperationV0) { + pub fn add(&mut self, op: WalletOperation) { let duration = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() @@ -244,49 +247,49 @@ impl WalletLogV0 { pub fn reduce(&self) -> Result { if self.log.len() < 1 { Err(NgWalletError::NoCreateWalletPresent) - } else if let (_, WalletOperationV0::CreateWalletV0(create_op)) = &self.log[0] { + } else if let (_, WalletOperation::CreateWalletV0(create_op)) = &self.log[0] { let mut wallet: EncryptedWalletV0 = create_op.into(); for op in &self.log { match &op.1 { - WalletOperationV0::CreateWalletV0(_) => { /* intentionally left blank. this op is already reduced */ + WalletOperation::CreateWalletV0(_) => { /* intentionally left blank. this op is already reduced */ } - WalletOperationV0::AddSiteV0(o) => { + WalletOperation::AddSiteV0(o) => { if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteV0") { wallet.add_site(o.clone()); } } - WalletOperationV0::RemoveSiteV0(_) => {} - WalletOperationV0::AddBrokerServerV0(o) => { + WalletOperation::RemoveSiteV0(_) => {} + WalletOperation::AddBrokerServerV0(o) => { if self.is_last_and_not_deleted_afterwards(op, "RemoveBrokerServerV0") { wallet.add_brokers(vec![BrokerInfoV0::ServerV0(o.clone())]); } } - WalletOperationV0::RemoveBrokerServerV0(_) => {} - WalletOperationV0::SetSaveToNGOneV0(o) => { + WalletOperation::RemoveBrokerServerV0(_) => {} + WalletOperation::SetSaveToNGOneV0(o) => { if self.is_last_occurrence(op.0, &op.1) != 0 { wallet.save_to_ng_one = o.clone(); } } - WalletOperationV0::SetBrokerCoreV0(o) => { + WalletOperation::SetBrokerCoreV0(o) => { if self.is_last_occurrence(op.0, &op.1) != 0 { wallet.add_brokers(vec![BrokerInfoV0::CoreV0(o.clone())]); } } - WalletOperationV0::SetClientV0(o) => { + WalletOperation::SetClientV0(o) => { if self.is_last_occurrence(op.0, &op.1) != 0 { wallet.add_client(o.clone()); } } - WalletOperationV0::AddOverlayCoreOverrideV0((overlay, cores)) => { + WalletOperation::AddOverlayCoreOverrideV0((overlay, cores)) => { if self .is_last_and_not_deleted_afterwards(op, "RemoveOverlayCoreOverrideV0") { wallet.add_overlay_core_overrides(overlay, cores); } } - WalletOperationV0::RemoveOverlayCoreOverrideV0(_) => {} - WalletOperationV0::AddSiteCoreV0((site, core)) => { + WalletOperation::RemoveOverlayCoreOverrideV0(_) => {} + WalletOperation::AddSiteCoreV0((site, core)) => { if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteCoreV0") { let _ = wallet.sites.get_mut(&site).and_then(|site| { site.cores.push(*core); @@ -294,8 +297,8 @@ impl WalletLogV0 { }); } } - WalletOperationV0::RemoveSiteCoreV0(_) => {} - WalletOperationV0::AddSiteBootstrapV0((site, server)) => { + WalletOperation::RemoveSiteCoreV0(_) => {} + WalletOperation::AddSiteBootstrapV0((site, server)) => { if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteBootstrapV0") { let _ = wallet.sites.get_mut(&site).and_then(|site| { site.bootstraps.push(*server); @@ -303,14 +306,14 @@ impl WalletLogV0 { }); } } - WalletOperationV0::RemoveSiteBootstrapV0(_) => {} - WalletOperationV0::AddThirdPartyDataV0((key, value)) => { + WalletOperation::RemoveSiteBootstrapV0(_) => {} + WalletOperation::AddThirdPartyDataV0((key, value)) => { if self.is_last_and_not_deleted_afterwards(op, "RemoveThirdPartyDataV0") { let _ = wallet.third_parties.insert(key.to_string(), value.to_vec()); } } - WalletOperationV0::RemoveThirdPartyDataV0(_) => {} - WalletOperationV0::SetSiteRBDRefV0((site, store_type, rbdr)) => { + WalletOperation::RemoveThirdPartyDataV0(_) => {} + WalletOperation::SetSiteRBDRefV0((site, store_type, rbdr)) => { if self.is_last_occurrence(op.0, &op.1) != 0 { let _ = wallet.sites.get_mut(&site).and_then(|site| { match store_type { @@ -328,7 +331,7 @@ impl WalletLogV0 { }); } } - WalletOperationV0::SetSiteRepoSecretV0((site, store_type, secret)) => { + WalletOperation::SetSiteRepoSecretV0((site, store_type, secret)) => { if self.is_last_occurrence(op.0, &op.1) != 0 { let _ = wallet.sites.get_mut(&site).and_then(|site| { match store_type { @@ -357,7 +360,7 @@ impl WalletLogV0 { pub fn is_first_and_not_deleted_afterwards( &self, - item: &(u128, WalletOperationV0), + item: &(u128, WalletOperation), delete_type: &str, ) -> bool { let hash = self.is_first_occurrence(item.0, &item.1); @@ -375,7 +378,7 @@ impl WalletLogV0 { pub fn is_last_and_not_deleted_afterwards( &self, - item: &(u128, WalletOperationV0), + item: &(u128, WalletOperation), delete_type: &str, ) -> bool { let hash = self.is_last_occurrence(item.0, &item.1); @@ -391,7 +394,7 @@ impl WalletLogV0 { false } - pub fn is_first_occurrence(&self, timestamp: u128, searched_op: &WalletOperationV0) -> u64 { + pub fn is_first_occurrence(&self, timestamp: u128, searched_op: &WalletOperation) -> u64 { let searched_hash = searched_op.hash(); //let mut timestamp = u128::MAX; //let mut found = searched_op; @@ -406,7 +409,7 @@ impl WalletLogV0 { searched_hash.0 } - pub fn is_last_occurrence(&self, timestamp: u128, searched_op: &WalletOperationV0) -> u64 { + pub fn is_last_occurrence(&self, timestamp: u128, searched_op: &WalletOperation) -> u64 { let searched_hash = searched_op.hash(); //let mut timestamp = 0u128; //let mut found = searched_op; @@ -426,7 +429,7 @@ impl WalletLogV0 { searched_type: &str, searched_hash: u64, after: u128, - ) -> Option<(u128, &WalletOperationV0)> { + ) -> Option<(u128, &WalletOperation)> { let mut timestamp = u128::MAX; let mut found = None; for op in &self.log { @@ -446,7 +449,7 @@ impl WalletLogV0 { /// WalletOperation #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum WalletOperationV0 { +pub enum WalletOperation { CreateWalletV0(WalletOpCreateV0), AddSiteV0(SiteV0), RemoveSiteV0(PrivKey), @@ -468,7 +471,7 @@ pub enum WalletOperationV0 { } use std::collections::hash_map::DefaultHasher; -impl WalletOperationV0 { +impl WalletOperation { pub fn hash(&self) -> (u64, &str) { let mut s = DefaultHasher::new(); match self { @@ -579,6 +582,7 @@ impl From<&WalletOpCreateV0> for EncryptedWalletV0 { fn from(op: &WalletOpCreateV0) -> Self { let mut wallet = EncryptedWalletV0 { wallet_privkey: op.wallet_privkey.clone(), + wallet_id: op.wallet_privkey.to_pub().to_string(), pazzle: op.pazzle.clone(), mnemonic: op.mnemonic.clone(), pin: op.pin.clone(), diff --git a/ngaccount/Cargo.toml b/ngaccount/Cargo.toml index d2aefb3..27086ea 100644 --- a/ngaccount/Cargo.toml +++ b/ngaccount/Cargo.toml @@ -17,6 +17,7 @@ env_logger = "0.10" stores-lmdb = { path = "../stores-lmdb" } p2p-repo = { path = "../p2p-repo", features = ["server_log_output"] } p2p-net = { path = "../p2p-net" } +p2p-client-ws = { path = "../p2p-client-ws" } ng-wallet = { path = "../ng-wallet" } serde = { version = "1.0.142", features = ["derive"] } serde_bare = "0.5.0" diff --git a/ngaccount/src/main.rs b/ngaccount/src/main.rs index 311d58b..0e73823 100644 --- a/ngaccount/src/main.rs +++ b/ngaccount/src/main.rs @@ -11,6 +11,9 @@ extern crate anyhow; mod types; +use p2p_client_ws::remote_ws::ConnectionWebSocket; +use p2p_net::actors::add_user::*; +use p2p_net::broker::BROKER; use p2p_repo::store::StorageError; use warp::reply::Response; use warp::{Filter, Reply}; @@ -18,12 +21,18 @@ use warp::{Filter, Reply}; use rust_embed::RustEmbed; use serde_bare::{from_slice, to_vec}; use serde_json::json; +use std::convert::Infallible; +use std::net::IpAddr; +use std::str::FromStr; use std::sync::Arc; use std::{env, fs}; use crate::types::*; use ng_wallet::types::*; -use p2p_net::types::{CreateAccountBSP, APP_NG_ONE_URL, NG_ONE_URL}; +use p2p_net::types::{ + BindAddress, CreateAccountBSP, Invitation, InvitationV0, APP_ACCOUNT_REGISTERED_SUFFIX, + APP_NG_ONE_URL, NG_ONE_URL, +}; use p2p_repo::log::*; use p2p_repo::types::*; use p2p_repo::utils::{generate_keypair, sign, verify}; @@ -32,27 +41,110 @@ use p2p_repo::utils::{generate_keypair, sign, verify}; #[folder = "web/dist"] struct Static; -struct Server {} +struct Server { + admin_key: PrivKey, + local_peer_key: PrivKey, + ip: IpAddr, + port: u16, + peer_id: PubKey, + domain: String, +} impl Server { - fn register_(&self, ca: String) -> Result<(), NgHttpError> { + async fn register_(&self, ca: String) -> Result> { log_debug!("registering {}", ca); - let cabsp: CreateAccountBSP = ca.try_into().map_err(|_| NgHttpError::InvalidParams)?; + let mut cabsp: CreateAccountBSP = ca.try_into().map_err(|_| None)?; log_debug!("{:?}", cabsp); - Ok(()) + // if needed, proceed with payment and verify it here. once validated, add the user + + let local_peer_pubk = self.local_peer_key.to_pub(); + let res = BROKER + .write() + .await + .admin( + Box::new(ConnectionWebSocket {}), + self.local_peer_key.clone(), + local_peer_pubk, + self.peer_id, + self.admin_key.to_pub(), + self.admin_key.clone(), + BindAddress { + port: self.port, + ip: (&self.ip).into(), + }, + AddUser::V0(AddUserV0 { + user: cabsp.user(), + is_admin: false, + }), + ) + .await; + + let mut redirect_url = cabsp + .redirect_url() + .clone() + .unwrap_or(format!("{}{}", self.domain, APP_ACCOUNT_REGISTERED_SUFFIX)); + + match &res { + Err(e) => { + log_err!("error while registering: {e} {:?}", cabsp); + Err(Some(format!("{}?e={}", redirect_url, e))) + } + Ok(_) => { + log_info!("User added successfully {}", cabsp.user()); + let mut return_invitation = if cabsp.invitation().is_some() { + InvitationV0 { + bootstrap: cabsp.invitation().as_ref().unwrap().bootstrap.clone(), + name: cabsp.invitation().as_ref().unwrap().name.clone(), + code: None, + url: None, + } + } else { + InvitationV0::empty(Some(self.domain.clone())) + }; + return_invitation.append_bootstraps(cabsp.additional_bootstrap()); + Ok(format!( + "{}?i={}&u={}", + redirect_url, + Invitation::V0(return_invitation), + cabsp.user() + )) + } + } } - pub fn register(&self, ca: String) -> Response { - match self.register_(ca) { - Ok(_) => warp::http::StatusCode::CREATED.into_response(), - Err(e) => e.into_response(), + pub async fn register(self: Arc, ca: String) -> Result { + match self.register_(ca).await { + Ok(redirect_url) => { + let response = Response::new(redirect_url.into()); + let (mut parts, body) = response.into_parts(); + parts.status = warp::http::StatusCode::OK; + let response = Response::from_parts(parts, body); + Ok(response) + } + Err(redirect_url) => { + if redirect_url.is_some() { + let response = Response::new(redirect_url.unwrap().into()); + let (mut parts, body) = response.into_parts(); + parts.status = warp::http::StatusCode::BAD_REQUEST; + let response = Response::from_parts(parts, body); + Ok(response) + } else { + Ok(warp::http::StatusCode::NOT_ACCEPTABLE.into_response()) + } + } } } } +fn with_server( + server: Arc, +) -> impl Filter,), Error = std::convert::Infallible> + Clone { + warp::any().map(move || Arc::clone(&server)) +} + #[tokio::main] async fn main() -> anyhow::Result<()> { if std::env::var("RUST_LOG").is_err() { @@ -60,13 +152,26 @@ async fn main() -> anyhow::Result<()> { } env_logger::init(); - let server = Arc::new(Server {}); - let domain = env::var("NG_ACCOUNT_DOMAIN").map_err(|_| anyhow!("NG_ACCOUNT_DOMAIN must be set"))?; - let admin_user = - env::var("NG_ACCOUNT_ADMIN").map_err(|_| anyhow!("NG_ACCOUNT_ADMIN must be set"))?; + let admin_user = env::var("NG_ACCOUNT_ADMIN") + .map_err(|_| anyhow!("NG_ACCOUNT_ADMIN must be set (with private key)"))?; + + let admin_key: PrivKey = admin_user.as_str().try_into().map_err(|_| { + anyhow!( + "NG_ACCOUNT_ADMIN is invalid. It should be a base64-url encoded serde serialization of a [u8; 32] of the private key for an admin user. cannot start" + ) + })?; + + let local_peer_privkey = env::var("NG_ACCOUNT_LOCAL_PEER_KEY") + .map_err(|_| anyhow!("NG_ACCOUNT_LOCAL_PEER_KEY must be set"))?; + + let local_peer_key: PrivKey = local_peer_privkey.as_str().try_into().map_err(|_| { + anyhow!( + "NG_ACCOUNT_LOCAL_PEER_KEY is invalid. It should be a base64-url encoded serde serialization of a [u8; 32] of the private key for the peerId. cannot start" + ) + })?; // format is IP,PORT,PEERID let server_address = @@ -78,15 +183,39 @@ async fn main() -> anyhow::Result<()> { "NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID" )); } - let ip: IP = addr[0].into(); + let ip = IpAddr::from_str(addr[0]).map_err(|_| { + anyhow!("NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID. The first part is not an IP address. cannot start") + })?; - log::info!("{}", domain); + let port = match addr[1].parse::() { + Err(_) => { + return Err(anyhow!("NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID. The port is invalid. It should be a number. cannot start")); + } + Ok(val) => val, + }; + let peer_id: PubKey = addr[2].try_into().map_err(|_| { + anyhow!( + "NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID. + The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]. cannot start" + ) + })?; + + log::info!("domain {}", domain); + + let server = Arc::new(Server { + admin_key, + local_peer_key, + ip, + port, + peer_id, + domain, + }); // GET /api/v1/register/ca with the same ?ca= query param => 201 CREATED - let server_for_move = Arc::clone(&server); let register_api = warp::get() + .and(with_server(server)) .and(warp::path!("register" / String)) - .map(move |ca| server_for_move.register(ca)); + .and_then(Server::register); let api_v1 = warp::path!("api" / "v1" / ..).and(register_api); diff --git a/ngaccount/web/package.json b/ngaccount/web/package.json index 7f95437..efa7dfe 100644 --- a/ngaccount/web/package.json +++ b/ngaccount/web/package.json @@ -12,7 +12,8 @@ "dependencies": { "flowbite": "^1.6.5", "flowbite-svelte": "^0.37.1", - "svelte-spa-router": "^3.3.0" + "svelte-spa-router": "^3.3.0", + "@tauri-apps/api": "2.0.0-alpha.4" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.4", diff --git a/ngaccount/web/pnpm-lock.yaml b/ngaccount/web/pnpm-lock.yaml index 8a2fcde..bc4e95e 100644 --- a/ngaccount/web/pnpm-lock.yaml +++ b/ngaccount/web/pnpm-lock.yaml @@ -2,6 +2,7 @@ lockfileVersion: 5.4 specifiers: '@sveltejs/vite-plugin-svelte': ^2.0.4 + '@tauri-apps/api': 2.0.0-alpha.4 autoprefixer: ^10.4.14 cross-env: ^7.0.3 flowbite: ^1.6.5 @@ -16,6 +17,7 @@ specifiers: vite-plugin-svelte-svg: ^2.2.1 dependencies: + '@tauri-apps/api': 2.0.0-alpha.4 flowbite: 1.6.6 flowbite-svelte: 0.37.5_svelte@3.59.1 svelte-spa-router: 3.3.0 @@ -332,6 +334,11 @@ packages: - supports-color dev: true + /@tauri-apps/api/2.0.0-alpha.4: + resolution: {integrity: sha512-gWe5fFHbwFM+dmdDPtlDvVDVtoMneGRM+S8mECevWhKpXYxId0yxznE56YGAvPSJXC3vgsXw16mOmkTnEVKnaw==} + engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + dev: false + /@trysound/sax/0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} diff --git a/ngaccount/web/src/routes/Create.svelte b/ngaccount/web/src/routes/Create.svelte index de94a95..e85d03c 100644 --- a/ngaccount/web/src/routes/Create.svelte +++ b/ngaccount/web/src/routes/Create.svelte @@ -11,14 +11,18 @@ @@ -60,11 +91,34 @@ {/if} {#if error} -
-

- An error occurred while registering on this broker :
- {error} +

+ + +

+ An error occurred while registering on this broker:
{error}

+ {#if go_back} + + {/if}
{:else} {#if ca} @@ -218,10 +272,9 @@ Registration is free of charge. And it would be very nice of you - if you wanted to donate a small amount for the fees we have to pay - for the servers. Here is the donation link: https://nextgraph.org/donate diff --git a/ngaccount/web/src/routes/Delete.svelte b/ngaccount/web/src/routes/Delete.svelte index 8a2691d..366d015 100644 --- a/ngaccount/web/src/routes/Delete.svelte +++ b/ngaccount/web/src/routes/Delete.svelte @@ -17,8 +17,8 @@ import { onMount } from "svelte"; - const params = new URLSearchParams($querystring); - let ca = params.get("ca"); + const param = new URLSearchParams($querystring); + let ca = param.get("ca"); let domain = import.meta.env.NG_ACCOUNT_DOMAIN; diff --git a/ngaccount/web/src/routes/Home.svelte b/ngaccount/web/src/routes/Home.svelte index 3828c22..c40f102 100644 --- a/ngaccount/web/src/routes/Home.svelte +++ b/ngaccount/web/src/routes/Home.svelte @@ -17,7 +17,7 @@ const api_url = import.meta.env.PROD ? "api/v1/" - : "http://localhost:3030/api/v1/"; + : "http://localhost:3031/api/v1/"; async function bootstrap() {} diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs index bce112c..83c0a3a 100644 --- a/ngcli/src/main.rs +++ b/ngcli/src/main.rs @@ -139,6 +139,10 @@ async fn main() -> Result<(), ProtocolError> { .about("add a user to the server, so it can connect to it") .arg(arg!([USER_ID] "userId of the user to add. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true)) .arg(arg!(-a --admin "make this user admin as well").required(false))) + .subcommand( + Command::new("del-user") + .about("removes a user from the broker") + .arg(arg!([USER_ID] "userId of the user to remove. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true))) .subcommand( Command::new("list-users") .about("list all users registered in the broker") @@ -146,7 +150,7 @@ async fn main() -> Result<(), ProtocolError> { .subcommand( Command::new("add-invitation") .about("add an invitation to register on the server") - .arg(arg!([EXPIRES] "offset (from now) of time after which the invitation should expire. Format example: 1w 1d 1m. default unit is second").conflicts_with("forever")) + .arg(arg!([EXPIRES] "offset (from now) of time after which the invitation should expire. Format example: 1w 1d 1m. default unit is second. see https://crates.io/crates/duration-str for format").conflicts_with("forever")) .arg(arg!(-a --admin "user registered with this invitation will have admin permissions").required(false)) .arg(arg!(-i --multi "many users can use this invitation to register themselves, until the invitation code is deleted by an admin").required(false).conflicts_with("admin").conflicts_with("unique")) .arg(arg!(-u --unique "this invitation can be used only once. this is the default").required(false).conflicts_with("admin")) @@ -440,6 +444,30 @@ async fn main() -> Result<(), ProtocolError> { } return res.map(|_| ()); } + Some(("del-user", sub2_matches)) => { + log_debug!("add-user"); + let res = do_admin_call( + keys[1], + config_v0, + DelUser::V0(DelUserV0 { + user: sub2_matches + .get_one::("USER_ID") + .unwrap() + .as_str() + .try_into() + .map_err(|_| { + log_err!("supplied USER_ID is invalid"); + ProtocolError::InvalidValue + })?, + }), + ) + .await; + match &res { + Err(e) => log_err!("An error occurred: {e}"), + Ok(_) => println!("User removed successfully"), + } + return res.map(|_| ()); + } Some(("list-users", sub2_matches)) => { log_debug!("list-users"); let admins = sub2_matches.get_flag("admin"); diff --git a/ngone/web/src/routes/Home.svelte b/ngone/web/src/routes/Home.svelte index c56d66a..bcb59cc 100644 --- a/ngone/web/src/routes/Home.svelte +++ b/ngone/web/src/routes/Home.svelte @@ -25,7 +25,7 @@ async function bootstrap() { let bs; try { - bs = localStorage.getItem("bootstrap"); + bs = localStorage.getItem("ng_wallets"); } catch (e) {} if (bs) { } else { diff --git a/ngone/web/src/routes/WalletCreate.svelte b/ngone/web/src/routes/WalletCreate.svelte index d5ca013..623f280 100644 --- a/ngone/web/src/routes/WalletCreate.svelte +++ b/ngone/web/src/routes/WalletCreate.svelte @@ -12,7 +12,9 @@