invite_admin

Niko PLP 10 months ago
parent 59f63aef17
commit a1f11f3a6a
  1. 2
      Cargo.lock
  2. 8
      ng-app/src-tauri/src/lib.rs
  3. 10
      ng-app/src/api.ts
  4. 277
      ng-app/src/routes/WalletCreate.svelte
  5. 3
      ng-sdk-js/README.md
  6. 2
      ng-sdk-js/index.html
  7. 5
      ng-sdk-js/js/browser.js
  8. 48
      ng-sdk-js/src/lib.rs
  9. 4
      ngaccount/Cargo.toml
  10. 76
      ngaccount/src/main.rs
  11. 8
      ngaccount/src/types.rs
  12. 6
      ngaccount/web/package.json
  13. 48
      ngaccount/web/pnpm-lock.yaml
  14. 4
      ngaccount/web/src/App.svelte
  15. 265
      ngaccount/web/src/routes/Create.svelte
  16. 186
      ngaccount/web/src/routes/Delete.svelte
  17. 1
      ngaccount/web/vite.config.js
  18. 8
      ngd/src/cli.rs
  19. 24
      ngd/src/main.rs
  20. 2
      p2p-broker/src/broker_store/mod.rs
  21. 101
      p2p-broker/src/broker_store/wallet.rs
  22. 20
      p2p-broker/src/server_ws.rs
  23. 73
      p2p-broker/src/storage.rs
  24. 4
      p2p-broker/src/types.rs
  25. 1
      p2p-client-ws/src/remote_ws.rs
  26. 1
      p2p-client-ws/src/remote_ws_wasm.rs
  27. 92
      p2p-net/src/broker.rs
  28. 42
      p2p-net/src/connection.rs
  29. 1
      p2p-net/src/errors.rs
  30. 2
      p2p-net/src/lib.rs
  31. 49
      p2p-net/src/types.rs
  32. 15
      p2p-net/src/utils.rs
  33. 9
      p2p-repo/src/types.rs

2
Cargo.lock generated

@ -2792,6 +2792,7 @@ dependencies = [
name = "ngaccount"
version = "0.1.0"
dependencies = [
"anyhow",
"base64-url",
"bytes",
"env_logger",
@ -2805,7 +2806,6 @@ dependencies = [
"serde_bare",
"serde_bytes",
"serde_json",
"slice_as_array",
"stores-lmdb",
"tokio",
"warp",

@ -10,6 +10,7 @@ use async_std::stream::StreamExt;
use ng_wallet::types::*;
use ng_wallet::*;
use p2p_net::broker::*;
use p2p_net::types::CreateAccountBSP;
use p2p_net::utils::{spawn_and_log_error, Receiver, ResultSend};
use p2p_repo::log::*;
use p2p_repo::types::*;
@ -75,6 +76,12 @@ async fn wallet_create_wallet(mut params: CreateWalletV0) -> Result<CreateWallet
res
}
#[tauri::command(rename_all = "snake_case")]
async fn encode_create_account(payload: CreateAccountBSP) -> Result<String, ()> {
log_info!("{:?}", payload);
payload.encode().ok_or(())
}
#[tauri::command(rename_all = "snake_case")]
async fn doc_sync_branch(nuri: &str, stream_id: &str, app: tauri::AppHandle) -> Result<(), ()> {
log_info!("doc_sync_branch {} {}", nuri, stream_id);
@ -184,6 +191,7 @@ impl AppBuilder {
wallet_gen_shuffle_for_pin,
wallet_open_wallet_with_pazzle,
wallet_create_wallet,
encode_create_account,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

@ -18,6 +18,7 @@ const mapping = {
"wallet_gen_shuffle_for_pin": [],
"wallet_open_wallet_with_pazzle": ["wallet","pazzle","pin"],
"wallet_create_wallet": ["params"],
"encode_create_account": ["payload"],
"test": [ ]
}
@ -61,7 +62,7 @@ const handler = {
info.browser.ua = window.navigator.userAgent;
let res = {
// TODO: install timestamp
ClientInfoV0 : { client_type, details: JSON.stringify(info), version, timestamp_install:0, timestamp_updated:0 }
V0 : { client_type, details: JSON.stringify(info), version, timestamp_install:0, timestamp_updated:0 }
};
//console.log(res);
return res;
@ -93,7 +94,7 @@ const handler = {
params.result_with_wallet_file = false;
params.security_img = Array.from(new Uint8Array(params.security_img));
return await tauri.invoke(path[0],{params})
} else if (path[0] === "get_local_bootstrap") {
} else if (path[0].starts_with("get_local_bootstrap")) {
return false;
}
else {
@ -108,7 +109,12 @@ const handler = {
const api = createAsyncProxy({}, handler);
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 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";
export default api;

@ -14,7 +14,13 @@
import { link, querystring } from "svelte-spa-router";
import EULogo from "../assets/EU.svg?component";
import Logo from "../assets/nextgraph.svg?component";
import { NG_EU_BSP, NG_NET_BSP, default as ng } from "../api";
import {
NG_EU_BSP,
NG_NET_BSP,
NG_EU_BSP_REGISTER,
NG_EU_BSP_REGISTERED,
default as ng,
} from "../api";
import { display_pazzle } from "../wallet_emojis";
import { onMount, tick } from "svelte";
@ -122,7 +128,10 @@
async function bootstrap() {
console.log(await ng.client_info());
invitation = await ng.get_local_bootstrap(location.href, params.get("i"));
invitation = await ng.get_local_bootstrap_with_public(
location.href,
params.get("i")
);
console.log(invitation);
// TODO: implement this error screen and link button
if (!invitation && params.get("i")) {
@ -210,14 +219,52 @@
onMount(async () => await bootstrap());
const selectEU = (event) => {
ready = {
user: {
Ed25519PubKey: [
141, 114, 111, 29, 59, 133, 182, 172, 177, 211, 238, 224, 62, 208, 206,
18, 226, 219, 118, 229, 184, 76, 204, 29, 194, 228, 248, 186, 15, 113,
125, 119,
],
},
};
const selectEU = async (event) => {
if (!tauri_platform) {
window.open(NG_EU_BSP + "/#/wallet/create", "_blank").focus();
let local_invitation = await ng.get_local_bootstrap(location.href);
let additional_bootstrap;
if (local_invitation) {
additional_bootstrap = local_invitation.V0.bootstrap;
}
let create = {
V0: {
additional_bootstrap,
invitation: undefined,
user: ready.user,
redirect_url: NG_EU_BSP_REGISTERED,
},
};
let ca = await ng.encode_create_account(create);
window.location.href = NG_EU_BSP_REGISTER + "?ca=" + ca;
//window.open(), "_self").focus();
} else {
let create = {
V0: {
additional_bootstrap: undefined,
invitation: undefined,
user: ready.user,
redirect_url: NG_EU_BSP_REGISTERED,
},
};
let ca = await ng.encode_create_account(create);
// TODO: open window with registration URL : NG_EU_BSP_REGISTER + "?ca=" + ca;
}
};
const selectNET = (event) => {};
const enterINVITE = (event) => {};
const enterQRcode = (event) => {};
const displayNGbox = (event) => {};
const displaySelfHost = (event) => {};
</script>
<main class="container3" bind:this={top}>
@ -612,11 +659,12 @@
</svg>
<span>
Very soon we will offer you the opportunity to host your own
broker at <b>home</b> or <b>office</b>. Instead of using a "broker
service provider", you will own a small device that you connect
behind your internet router. It is called NG Box and will be
available soon.</span
Soon we will offer you the opportunity to host your own broker at <b
>home</b
>
or <b>office</b>. Instead of using a "broker service provider",
you will own a small device that you connect behind your internet
router. It is called <b>NG Box</b> and will be available soon.</span
>
</li>
<li class="flex space-x-3">
@ -637,10 +685,13 @@
</svg>
<span>
Large organizations and companies have the opportunity to host a
broker <b>on-premise</b> or in the cloud, as the software is open
source. Individuals can also <b>self-host</b> a broker on any VPS server
or at home.</span
Organizations and companies have the opportunity to host a broker <b
>on-premise</b
>
or in the <b>cloud</b>, as the software is open source.
Individuals can also
<b>self-host</b> a broker on any VPS server or at home, on their dedicated
hardware.</span
>
</li>
</ul>
@ -648,75 +699,37 @@
</div>
</div>
<div class="row mt-5">
{#if !tauri_platform}
<a href="https://nextgraph.eu/#/wallet/create">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
</a>
{:else}
<button
on:click|once={selectEU}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
{/if}
<button
on:click|once={selectEU}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
</div>
<div class="row mt-5">
{#if !tauri_platform}
<a href="https://nextgraph.net/#/wallet/create">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
</a>
{:else}
<button
on:click|once={selectNET}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
<button
on:click|once={selectNET}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
{/if}
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
</div>
<div class="row mt-5">
@ -774,59 +787,55 @@
</div>
{/if}
<div class="row mt-5">
<a href="https://nextgraph.org/self-host">
<button
tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
<button
on:click|once={displaySelfHost}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
Self-hosted broker
</button>
</a>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
Self-hosted broker
</button>
</div>
<div class="row mt-5 mb-12">
<a href="https://nextgraph.org/ng-box/">
<button
tabindex="-1"
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
<button
on:click|once={displayNGbox}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 225 225"
class="mr-4 block h-10 w-10"
stroke="currentColor"
stroke-width="12"
fill="none"
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 225 225"
class="mr-4 block h-10 w-10"
stroke="currentColor"
stroke-width="12"
fill="none"
>
<path
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z"
/>
<rect
ry="37.596001"
y="10.583322"
x="14.363095"
height="204.86308"
width="195.79167"
/>
</svg>
NG Box (owned or invited)
</button>
</a>
<path
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z"
/>
<rect
ry="37.596001"
y="10.583322"
x="14.363095"
height="204.86308"
width="195.79167"
/>
</svg>
NG Box (owned or invited)
</button>
</div>
{:else if pin.length < 4}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
@ -1091,8 +1100,8 @@
We can keep an encrypted copy of your wallet in our cloud. Only you will
be able to download it with a special link. You would have to keep this link
safely though. By selecting this option, you agree to the
<a target="_blank" href="https://nextgraph.one/tos"
>Terms and Conditions of our cloud</a
<a target="_blank" href="https://nextgraph.one/#/tos"
>Terms of Service of our cloud</a
>.
<br />
<Toggle class="mt-3" bind:checked={options.cloud}
@ -1116,11 +1125,15 @@
<span class="text-xl"
>Create a link to access your wallet easily?
</span> <br />
When you want to use your wallet on the web or in other apps, we can help
you find your wallet by creating a simple link accessible from anywhere.
When you want to use your wallet on the web or from other devices, we can
help you find your wallet by creating a simple link accessible from anywhere.
Only you will have access to this link. In order to do so, we will keep
your wallet ID and some information about your broker on our cloud servers.
If you prefer to opt out, just uncheck this option.
If you prefer to opt out, just uncheck this option. By selecting this option,
you agree to the
<a target="_blank" href="https://nextgraph.one/#/tos"
>Terms of Service of our cloud</a
>.
<br />
<Toggle class="mt-3" bind:checked={options.bootstrap}
>Create a link to my wallet?</Toggle

@ -46,7 +46,7 @@ node prepare-node.js
For testing in vanilla JS
```
wasm-pack build --target web -d web
wasm-pack build --dev --target web -d web
python3 -m http.server
// open http://localhost:8000
@ -63,6 +63,7 @@ wasm-pack test --chrome --headless
```
wasm-pack build --target bundler
wasm-pack build -t nodejs -d pkg-node
wasm-pack build --target web -d web
node prepare-node.js
cd pkg
npm publish --access=public

@ -25,7 +25,7 @@
start();
test();
});
// DON'T DO THE FOLLOW:
// DON'T DO THE FOLLOWING:
// it will instantiate twice the SDK, which is not what we want
// init().then(() => {
// test();

@ -1,10 +1,9 @@
import {version} from '../../../package.json';
export function client_details() {
return window.navigator.userAgent;
}
export function client_details2(obj) {
export function client_details2(obj,version) {
console.log("version",version)
obj.browser.appVersion = navigator?.appVersion;
obj.browser.arch = navigator?.platform;
obj.browser.vendor = navigator?.vendor;

@ -21,7 +21,9 @@ use ng_wallet::*;
use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket;
use p2p_net::broker::*;
use p2p_net::connection::{ClientConfig, StartConfig};
use p2p_net::types::{BootstrapContentV0, ClientInfoV0, ClientType, DirectPeerId, IP};
use p2p_net::types::{
BootstrapContentV0, ClientInfo, ClientInfoV0, ClientType, CreateAccountBSP, DirectPeerId, IP,
};
use p2p_net::utils::{retrieve_local_bootstrap, spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_net::WS_PORT;
use p2p_repo::log::*;
@ -39,7 +41,18 @@ use wasm_bindgen_futures::{future_to_promise, JsFuture};
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn get_local_bootstrap(location: String, invite: JsValue) -> JsValue {
let res = retrieve_local_bootstrap(location, invite.as_string()).await;
let res = retrieve_local_bootstrap(location, invite.as_string(), false).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 {
let res = retrieve_local_bootstrap(location, invite.as_string(), true).await;
if res.is_some() {
serde_wasm_bindgen::to_value(&res.unwrap()).unwrap()
} else {
@ -120,7 +133,7 @@ extern "C" {
#[cfg(wasmpack_target = "nodejs")]
#[wasm_bindgen]
pub fn client_info() -> JsValue {
pub fn client_info() -> ClientInfoV0 {
let res = ClientInfoV0 {
client_type: ClientType::NodeService,
details: client_details(),
@ -128,6 +141,18 @@ pub fn client_info() -> JsValue {
timestamp_install: 0,
timestamp_updated: 0,
};
res
//serde_wasm_bindgen::to_value(&res).unwrap()
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn encode_create_account(payload: JsValue) -> JsValue {
log_info!("{:?}", payload);
let create_account = serde_wasm_bindgen::from_value::<CreateAccountBSP>(payload).unwrap();
log_info!("create_account {:?}", create_account);
let res = create_account.encode();
log_info!("res {:?}", res);
serde_wasm_bindgen::to_value(&res).unwrap()
}
@ -148,18 +173,18 @@ extern "C" {
#[cfg(not(wasmpack_target = "nodejs"))]
#[wasm_bindgen(module = "/js/browser.js")]
extern "C" {
fn client_details2(val: JsValue) -> String;
fn client_details2(val: JsValue, version: String) -> String;
}
#[cfg(all(not(wasmpack_target = "nodejs"), target_arch = "wasm32"))]
#[wasm_bindgen]
pub fn client_info() -> JsValue {
pub fn client_info() -> ClientInfoV0 {
let ua = client_details();
let bowser = Bowser::parse(ua);
//log_info!("{:?}", bowser);
let details_string = client_details2(bowser);
let details_string = client_details2(bowser, env!("CARGO_PKG_VERSION").to_string());
let res = ClientInfoV0 {
client_type: ClientType::Web,
@ -168,18 +193,16 @@ pub fn client_info() -> JsValue {
timestamp_install: 0,
timestamp_updated: 0,
};
serde_wasm_bindgen::to_value(&res).unwrap()
res
//serde_wasm_bindgen::to_value(&res).unwrap()
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn test() {
log_info!("test is {}", BROKER.read().await.test());
//let client_info = client_info();
//log_info!("{:?}", client_info);
//let b = Bowser::parse(ua);
//log_info!("{:?}", b);
let client_info = client_info();
log_info!("{:?}", client_info);
}
#[cfg(target_arch = "wasm32")]
@ -313,6 +336,7 @@ pub async fn start() {
user_priv,
client,
client_priv,
info: ClientInfo::V0(client_info()),
}),
)
.await;

@ -23,6 +23,6 @@ serde_bare = "0.5.0"
serde_bytes = "0.11.7"
serde-big-array = "0.5.1"
base64-url = "2.0.0"
slice_as_array = "1.1.0"
serde_json = "1.0.96"
bytes = "1.0"
bytes = "1.0"
anyhow = "1.0.71"

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
#[macro_use]
extern crate slice_as_array;
extern crate anyhow;
mod types;
@ -23,7 +23,7 @@ use std::{env, fs};
use crate::types::*;
use ng_wallet::types::*;
use p2p_net::types::{APP_NG_ONE_URL, NG_ONE_URL};
use p2p_net::types::{CreateAccountBSP, APP_NG_ONE_URL, NG_ONE_URL};
use p2p_repo::log::*;
use p2p_repo::types::*;
use p2p_repo::utils::{generate_keypair, sign, verify};
@ -34,49 +34,81 @@ struct Static;
struct Server {}
impl Server {}
impl Server {
fn register_(&self, ca: String) -> Result<(), NgHttpError> {
log_debug!("registering {}", ca);
let cabsp: CreateAccountBSP = ca.try_into().map_err(|_| NgHttpError::InvalidParams)?;
log_debug!("{:?}", cabsp);
Ok(())
}
pub fn register(&self, ca: String) -> Response {
match self.register_(ca) {
Ok(_) => warp::http::StatusCode::CREATED.into_response(),
Err(e) => e.into_response(),
}
}
}
#[tokio::main]
async fn main() {
async fn main() -> anyhow::Result<()> {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info"); //trace
}
env_logger::init();
// let (wallet_key, wallet_id) = generate_keypair();
// let content = BootstrapContentV0 { servers: vec![] };
// let ser = serde_bare::to_vec(&content).unwrap();
// let sig = sign(wallet_key, wallet_id, &ser).unwrap();
let server = Arc::new(Server {});
let domain =
env::var("NG_ACCOUNT_DOMAIN").map_err(|_| anyhow!("NG_ACCOUNT_DOMAIN must be set"))?;
// let bootstrap = Bootstrap::V0(BootstrapV0 {
// id: wallet_id,
// content,
// sig,
// });
let admin_user =
env::var("NG_ACCOUNT_ADMIN").map_err(|_| anyhow!("NG_ACCOUNT_ADMIN must be set"))?;
let server = Arc::new(Server {});
// format is IP,PORT,PEERID
let server_address =
env::var("NG_ACCOUNT_SERVER").map_err(|_| anyhow!("NG_ACCOUNT_SERVER must be set"))?;
let addr: Vec<&str> = server_address.split(',').collect();
if addr.len() != 3 {
return Err(anyhow!(
"NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEERID"
));
}
let ip: IP = addr[0].into();
log::info!("{}", 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(warp::path!("register" / String))
.map(move |ca| server_for_move.register(ca));
let api_v1 = warp::path!("api" / "v1" / ..).and(register_api);
let static_files = warp::get().and(warp_embed::embed(&Static)).boxed();
let mut cors = warp::cors()
.allow_methods(vec!["GET", "POST"])
.allow_methods(vec!["GET"])
.allow_headers(vec!["Content-Type"]);
#[cfg(not(debug_assertions))]
{
cors = cors
.allow_origin(NG_ONE_URL)
.allow_origin(APP_NG_ONE_URL)
.allow_origin("https://nextgraph.eu")
.allow_origin("https://nextgraph.net");
cors = cors.allow_origin(format!("https://{}", domain));
}
#[cfg(debug_assertions)]
{
log_debug!("CORS: any origin");
cors = cors.allow_any_origin();
}
log::info!("Starting server on http://localhost:3030");
warp::serve(static_files.with(cors))
log::info!("Starting server on http://localhost:3031");
warp::serve(api_v1.or(static_files).with(cors))
.run(([127, 0, 0, 1], 3031))
.await;
Ok(())
}

@ -20,7 +20,13 @@ impl Reply for NgHttpError {
fn into_response(self) -> Response {
match (self) {
NgHttpError::NotFound => warp::http::StatusCode::NOT_FOUND.into_response(),
NgHttpError::InvalidParams => warp::http::StatusCode::BAD_REQUEST.into_response(),
NgHttpError::InvalidParams => {
let response = Response::new("Invalid params".into());
let (mut parts, body) = response.into_parts();
parts.status = warp::http::StatusCode::BAD_REQUEST;
let response = Response::from_parts(parts, body);
response
}
NgHttpError::AlreadyExists => warp::http::StatusCode::CONFLICT.into_response(),
NgHttpError::InternalError => {
warp::http::StatusCode::INTERNAL_SERVER_ERROR.into_response()

@ -4,7 +4,8 @@
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "cross-env NG_ACCOUNT_DOMAIN=example.com vite",
"devenv": "vite",
"build": "vite build --base=./",
"preview": "vite preview"
},
@ -22,6 +23,7 @@
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.3.1",
"autoprefixer": "^10.4.14",
"vite-plugin-svelte-svg": "^2.2.1"
"vite-plugin-svelte-svg": "^2.2.1",
"cross-env": "^7.0.3"
}
}

@ -3,6 +3,7 @@ lockfileVersion: 5.4
specifiers:
'@sveltejs/vite-plugin-svelte': ^2.0.4
autoprefixer: ^10.4.14
cross-env: ^7.0.3
flowbite: ^1.6.5
flowbite-svelte: ^0.37.1
postcss: ^8.4.23
@ -22,6 +23,7 @@ dependencies:
devDependencies:
'@sveltejs/vite-plugin-svelte': 2.4.1_svelte@3.59.1+vite@4.3.9
autoprefixer: 10.4.14_postcss@8.4.24
cross-env: 7.0.3
postcss: 8.4.24
postcss-load-config: 4.0.1_postcss@8.4.24
svelte: 3.59.1
@ -455,6 +457,23 @@ packages:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
/cross-env/7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
hasBin: true
dependencies:
cross-spawn: 7.0.3
dev: true
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
dev: true
/css-select/5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
dependencies:
@ -755,6 +774,10 @@ packages:
engines: {node: '>=0.12.0'}
dev: true
/isexe/2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/jiti/1.18.2:
resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==}
hasBin: true
@ -895,6 +918,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/path-key/3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
dev: true
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
@ -1054,6 +1082,18 @@ packages:
rimraf: 2.7.1
dev: true
/shebang-command/2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
dev: true
/shebang-regex/3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
dev: true
/sorcery/0.11.0:
resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==}
hasBin: true
@ -1300,6 +1340,14 @@ packages:
vite: 4.3.9
dev: true
/which/2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
dependencies:
isexe: 2.0.0
dev: true
/wrappy/1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true

@ -13,11 +13,15 @@
import { onMount, tick } from "svelte";
import Home from "./routes/Home.svelte";
import Create from "./routes/Create.svelte";
import Delete from "./routes/Delete.svelte";
import NotFound from "./routes/NotFound.svelte";
const routes = new Map();
routes.set("/", Home);
routes.set("/create", Create);
routes.set("/delete", Delete);
routes.set("*", NotFound);
</script>

@ -0,0 +1,265 @@
<!--
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script>
import { Button } from "flowbite-svelte";
import EULogo from "../assets/EU.svg?component";
import Logo from "../assets/nextgraph.svg?component";
import { link, querystring } from "svelte-spa-router";
import { onMount } from "svelte";
let domain = import.meta.env.NG_ACCOUNT_DOMAIN;
const params = new URLSearchParams($querystring);
let ca = params.get("ca");
let top;
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3031/api/v1/";
async function register() {
const opts = {
method: "get",
};
const response = await fetch(api_url + "register/" + ca, opts);
const result = await response.text();
console.log("Result:", response.status, result); // 400 is error, 201 ok
}
async function bootstrap() {}
let error;
onMount(() => bootstrap());
const accept = async (event) => {
await register();
};
const refuse = (event) => {
window.history.go(-1);
};
</script>
<main class="container3" bind:this={top}>
<div class="row">
<Logo class="logo block h-24" alt="NextGraph Logo" />
{#if domain == "nextgraph.eu"}
<EULogo
class="logo block h-20"
style="margin-top: 0.5em;"
alt="European Union Logo"
/>
{/if}
</div>
{#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
An error occurred while registering on this broker :<br />
{error}
</p>
</div>
{:else}
{#if ca}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
You would like to choose <b>{domain}</b> as your Broker Service
Provider.<br />Please read carefully the Terms of Service here below,
before you accept them.
</p>
</div>
{/if}
<div class="px-4 pt-5 mx-auto max-w-6xl lg:px-8 lg:pt-10 dark:bg-slate-800">
<div class="max-w-xl md:mx-auto sm:text-center lg:max-w-2xl">
<h2 class="pb-5 text-xl">{domain} Terms of Service</h2>
<ul class="mb-8 space-y-4 text-left text-gray-500 dark:text-gray-400">
{#if domain == "nextgraph.eu"}
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M20.893 13.393l-1.135-1.135a2.252 2.252 0 01-.421-.585l-1.08-2.16a.414.414 0 00-.663-.107.827.827 0 01-.812.21l-1.273-.363a.89.89 0 00-.738 1.595l.587.39c.59.395.674 1.23.172 1.732l-.2.2c-.212.212-.33.498-.33.796v.41c0 .409-.11.809-.32 1.158l-1.315 2.191a2.11 2.11 0 01-1.81 1.025 1.055 1.055 0 01-1.055-1.055v-1.172c0-.92-.56-1.747-1.414-2.089l-.655-.261a2.25 2.25 0 01-1.383-2.46l.007-.042a2.25 2.25 0 01.29-.787l.09-.15a2.25 2.25 0 012.37-1.048l1.178.236a1.125 1.125 0 001.302-.795l.208-.73a1.125 1.125 0 00-.578-1.315l-.665-.332-.091.091a2.25 2.25 0 01-1.591.659h-.18c-.249 0-.487.1-.662.274a.931.931 0 01-1.458-1.137l1.411-2.353a2.25 2.25 0 00.286-.76m11.928 9.869A9 9 0 008.965 3.525m11.928 9.868A9 9 0 118.965 3.525"
/>
</svg>
<span
>Our servers are located in Germany, and we comply with the GDPR
regulation.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.35 3.836c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m8.9-4.414c.376.023.75.05 1.124.08 1.131.094 1.976 1.057 1.976 2.192V16.5A2.25 2.25 0 0118 18.75h-2.25m-7.5-10.5H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V18.75m-7.5-10.5h6.375c.621 0 1.125.504 1.125 1.125v9.375m-8.25-3l1.5 1.5 3-3.75"
/>
</svg>
<span>legal details about GDPR... TBD</span>
</li>
{/if}
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/>
</svg>
<span
>All the data you exchange with us while using the broker is
end-to-end encrypted and we do not have access to your decryption
keys, meaning that we cannot see the content of your documents.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
<span
>We do not log any private information about you (nor IP, nor
country, nor statistics of any kind). Only your UserId is kept,
together with the list of devices (clientId) you use to connect to
the broker. We collect general purpose information about your
device (OS version, browser version, and if you use the app, the
version and date of last update). We do not have access to any
unique tracking identifier of your device (like Android MAID or
iPhone IDFA). We could nevertheless be asked by law enforcement
authorities, depending on the jurisdiction of the server, to log
the IP you use when connecting to the broker, and/or to provide
them with the encrypted content you have stored on our servers. If
you prefer to avoid that eventually, please refrain from any
illegal activity while using this broker.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>
You can delete your account with us at any time by going to the
link <a target="_blank" href="https://account.{domain}/#/delete"
>https://account.{domain}/#/delete</a
> or by entering in your NextGraph application and selecting the menu,
then Account, then delete</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.25 7.756a4.5 4.5 0 100 8.488M7.5 10.5h5.25m-5.25 3h5.25M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span
>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: <a
target="_blank"
href="https://nextgraph.org/donate"
>https://nextgraph.org/donate</a
>
</span>
</li>
</ul>
</div>
</div>
{#if ca}
<div class="row mb-20">
<button
on:click|once={accept}
role="button"
class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/>
</svg>
I accept
</button>
<button
on:click|once={refuse}
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
I refuse
</button>
</div>
{/if}
{/if}
</main>

@ -0,0 +1,186 @@
<!--
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script>
import { Button } from "flowbite-svelte";
import EULogo from "../assets/EU.svg?component";
import Logo from "../assets/nextgraph.svg?component";
import { link, querystring } from "svelte-spa-router";
import { onMount } from "svelte";
const params = new URLSearchParams($querystring);
let ca = params.get("ca");
let domain = import.meta.env.NG_ACCOUNT_DOMAIN;
let top;
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3031/api/v1/";
async function bootstrap() {}
let error;
onMount(() => bootstrap());
const accept = (event) => {};
const refuse = (event) => {
window.history.go(-1);
};
</script>
<main class="container3" bind:this={top}>
<div class="row">
<Logo class="logo block h-24" alt="NextGraph Logo" />
{#if domain == "nextgraph.eu"}
<EULogo
class="logo block h-20"
style="margin-top: 0.5em;"
alt="European Union Logo"
/>
{/if}
</div>
{#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
An error occurred while deleting your account on this broker :<br />
{error}
</p>
</div>
{:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
You want to delete your account at <b>{domain}</b>?<br />Please read
carefully the details below before you do so.
</p>
</div>
<div class="px-4 pt-5 mx-auto max-w-6xl lg:px-8 lg:pt-10 dark:bg-slate-800">
<div class="max-w-xl md:mx-auto sm:text-center lg:max-w-2xl">
<h2 class="pb-5 text-xl">Delete your account at {domain}</h2>
<ul class="mb-8 space-y-4 text-left text-gray-500 dark:text-gray-400">
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
/>
</svg>
<span
>Your personal data on this broker will be permanently removed
(UserId, ClientId) and the data of your documents will be removed,
except if they are shared with other users who are using this
broker as well.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
/>
</svg>
<span
>You can come back anytime. Please understand that you must have
at least one broker configured in your wallet in order to be able
to use NextGraph. You have other options to select a new broker,
like hosting it yourself, or buying an NG Box. Please visit <a
target="_blank"
href="https://nextgraph.one/#/account/register"
>https://nextgraph.one/#/account/register</a
> in order to choose a new broker.
</span>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 9.75v6.75m0 0l-3-3m3 3l3-3m-8.25 6a4.5 4.5 0 01-1.41-8.775 5.25 5.25 0 0110.233-2.33 3 3 0 013.758 3.848A3.752 3.752 0 0118 19.5H6.75z"
/>
</svg>
<span
>All the data you still have locally on your devices (if you
installed the NextGraph application) will remain accessible to you
even after you delete your account from this broker.<br /> If you
haven't installed any NextGraph app yet, maybe it is a good idea
to do so now, before you delete your account from here. This way,
you will keep a copy of all your documents data locally. To
install the app,
<a target="_blank" href="https://nextgraph.one/#/install"
>go here</a
>. After installing the app, you will have to go to the menu and
select "Sync all my documents now".</span
>
</li>
</ul>
</div>
</div>
<div class="row mb-20">
<button
on:click|once={accept}
role="button"
class="mr-5 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/>
</svg>
Delete my account
</button>
<button
on:click|once={refuse}
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
Cancel
</button>
</div>
{/if}
</main>

@ -5,6 +5,7 @@ import svelteSVG from "vite-plugin-svelte-svg";
// https://vitejs.dev/config/
export default defineConfig({
envPrefix: ["VITE_", "NG_"],
plugins: [svelte({
preprocess: [
vitePreprocess(),

@ -112,6 +112,14 @@ pub(crate) struct Cli {
#[arg(long, conflicts_with("registration_off"))]
pub registration_open: bool,
/// Admin userID
#[arg(long)]
pub admin: Option<String>,
/// Admin invitation
#[arg(long, conflicts_with("admin"))]
pub invite_admin: bool,
/// Saves the quick config into a file on disk, that can then be modified for advanced configs
#[arg(long)]
pub save_config: bool,

@ -916,10 +916,24 @@ async fn main_inner() -> Result<(), ()> {
RegistrationConfig::Invitation
};
let admin_user = if args.admin.is_some() {
args.admin
.unwrap()
.as_str()
.try_into()
.map_err(|e| {
log_warn!("The admin UserId supplied is invalid. no admin user configured.");
})
.ok()
} else {
None
};
config = Some(DaemonConfig::V0(DaemonConfigV0 {
listeners,
overlays_configs: vec![overlays_config],
registration,
admin_user,
}));
if args.print_config {
@ -972,7 +986,15 @@ async fn main_inner() -> Result<(), ()> {
match config.unwrap() {
DaemonConfig::V0(v0) => {
run_server_v0(privkey, pubkey, SymKey::from_array(keys[2]), v0, path).await?
run_server_v0(
privkey,
pubkey,
SymKey::from_array(keys[2]),
v0,
path,
args.invite_admin,
)
.await?
}
}

@ -11,3 +11,5 @@ pub mod repostoreinfo;
pub mod topic;
pub mod invitation;
pub mod wallet;

@ -0,0 +1,101 @@
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Broker Wallet, persists to store all the SymKeys needed to open other storages
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
pub struct Wallet<'a> {
store: &'a dyn KCVStore,
}
impl<'a> Wallet<'a> {
const PREFIX: u8 = b"w"[0];
const PREFIX_OVERLAY: u8 = b"o"[0];
const PREFIX_USER: u8 = b"u"[0];
const KEY_ACCOUNTS: [u8; 8] = *b"accounts";
const KEY_PEERS: [u8; 5] = *b"peers";
// propertie's suffixes
const SYM_KEY: u8 = b"s"[0];
const ALL_PROPERTIES: [u8; 1] = [Self::SYM_KEY];
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::SYM_KEY;
pub fn open(store: &'a dyn KCVStore) -> Wallet<'a> {
Wallet { store }
}
pub fn get_or_create_single_key(
&self,
prefix: u8,
key: &Vec<u8>,
) -> Result<SymKey, StorageError> {
// FIXME. this get or create is not using a transaction, because calls will be made from the broker, that is behind a mutex.
// if this was to change, we should make the get and put inside one transaction.
let get = self
.store
.get(prefix, key, Some(Self::SUFFIX_FOR_EXIST_CHECK));
match get {
Err(e) => {
if e == StorageError::NotFound {
self.create_single_key(prefix, key)
} else {
Err(StorageError::BackendError)
}
}
Ok(p) => {
let k: SymKey = p
.as_slice()
.try_into()
.map_err(|_| StorageError::BackendError)?;
Ok(k)
}
}
}
pub fn get_or_create_user_key(&self, user: &UserId) -> Result<SymKey, StorageError> {
self.get_or_create_single_key(Self::PREFIX_USER, &to_vec(user)?)
}
pub fn get_or_create_overlay_key(&self, overlay: &OverlayId) -> Result<SymKey, StorageError> {
self.get_or_create_single_key(Self::PREFIX_USER, &to_vec(overlay)?)
}
pub fn create_single_key(&self, prefix: u8, key: &Vec<u8>) -> Result<SymKey, StorageError> {
let symkey = SymKey::random();
let vec = symkey.slice().to_vec();
self.store.put(prefix, key, Some(Self::SYM_KEY), vec)?;
Ok(symkey)
}
pub fn exists_single_key(&self, prefix: u8, key: &Vec<u8>) -> bool {
self.store
.get(prefix, key, Some(Self::SUFFIX_FOR_EXIST_CHECK))
.is_ok()
}
pub fn exists_accounts_key(&self) -> bool {
self.exists_single_key(Self::PREFIX, &Self::KEY_ACCOUNTS.to_vec())
}
pub fn create_accounts_key(&self) -> Result<SymKey, StorageError> {
self.create_single_key(Self::PREFIX, &Self::KEY_ACCOUNTS.to_vec())
}
pub fn get_or_create_peers_key(&self) -> Result<SymKey, StorageError> {
self.get_or_create_single_key(Self::PREFIX, &Self::KEY_PEERS.to_vec())
}
pub fn get_or_create_accounts_key(&self) -> Result<SymKey, StorageError> {
self.get_or_create_single_key(Self::PREFIX, &Self::KEY_ACCOUNTS.to_vec())
}
}

@ -39,7 +39,7 @@ use p2p_net::types::*;
use p2p_net::utils::get_domain_without_port;
use p2p_net::utils::is_private_ip;
use p2p_net::utils::is_public_ip;
use p2p_net::NG_BOOTSTRAP_LOCAL_URL;
use p2p_net::NG_BOOTSTRAP_LOCAL_PATH;
use p2p_repo::log::*;
use p2p_repo::types::SymKey;
use p2p_repo::types::{PrivKey, PubKey};
@ -255,7 +255,7 @@ fn upgrade_ws_or_serve_app(
.body(Some(file.data.to_vec()))
.unwrap();
return Err(res);
} else if uri == NG_BOOTSTRAP_LOCAL_URL {
} else if uri == NG_BOOTSTRAP_LOCAL_PATH {
log_debug!("Serving bootstrap");
let mut builder = Response::builder().status(StatusCode::OK);
@ -609,6 +609,7 @@ pub async fn run_server_v0(
wallet_master_key: SymKey,
config: DaemonConfigV0,
mut path: PathBuf,
admin_invite: bool,
) -> Result<(), ()> {
// check config
@ -754,8 +755,8 @@ pub async fn run_server_v0(
if !accept_clients {
log_warn!("There isn't any listener that accept clients. This is a misconfiguration as a core server that cannot receive client connections is useless");
}
let bootstrap = BootstrapContent::V0(BootstrapContentV0 { servers });
let bootstrap_v0 = BootstrapContentV0 { servers };
let bootstrap = BootstrapContent::V0(bootstrap_v0.clone());
BOOTSTRAP_STRING.set(json!(bootstrap).to_string()).unwrap();
// saving the infos in the broker. This needs to happen before we start listening, as new incoming connections can happen anytime after that.
@ -766,7 +767,16 @@ pub async fn run_server_v0(
std::fs::create_dir_all(path.clone()).unwrap();
// opening the server storage (that contains the encryption keys for each store/overlay )
let broker_storage = LmdbBrokerStorage::open(&mut path, wallet_master_key);
let broker_storage = LmdbBrokerStorage::open(
&mut path,
wallet_master_key,
if admin_invite {
Some(bootstrap_v0)
} else {
None
},
)
.map_err(|e| log_err!("Error while opening broker storage: {:?}", e))?;
let mut broker = BROKER.write().await;
broker.set_my_peer_id(peer_id);

@ -11,24 +11,85 @@
use std::path::PathBuf;
use crate::broker_store::invitation::Invitation;
use crate::broker_store::wallet::Wallet;
use crate::types::*;
use p2p_net::broker_storage::*;
use p2p_net::types::{BootstrapContentV0, InvitationCode, InvitationV0};
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::log::*;
use p2p_repo::store::StorageError;
use p2p_repo::types::SymKey;
use stores_lmdb::kcv_store::LmdbKCVStore;
use stores_lmdb::repo_store::LmdbRepoStore;
pub struct LmdbBrokerStorage {
wallet_storage: LmdbKCVStore,
accounts_storage: LmdbKCVStore,
peers_storage: LmdbKCVStore,
}
impl LmdbBrokerStorage {
pub fn open(path: &mut PathBuf, master_key: SymKey) -> Self {
path.push("wallet");
std::fs::create_dir_all(path.clone()).unwrap();
//TODO redo the whole key passing mechanism so it uses zeroize all the way
let wallet_storage = LmdbKCVStore::open(&path, master_key.slice().clone());
LmdbBrokerStorage { wallet_storage }
pub fn open(
path: &mut PathBuf,
master_key: SymKey,
admin_invite: Option<BootstrapContentV0>,
) -> Result<Self, StorageError> {
// create/open the WALLET
let mut wallet_path = path.clone();
wallet_path.push("wallet");
std::fs::create_dir_all(wallet_path.clone()).unwrap();
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let wallet_storage = LmdbKCVStore::open(&wallet_path, master_key.slice().clone());
let wallet = Wallet::open(&wallet_storage);
// create/open the ACCOUNTS storage
let mut accounts_path = path.clone();
let accounts_key;
accounts_path.push("accounts");
if admin_invite.is_some() && !accounts_path.exists() && !wallet.exists_accounts_key() {
accounts_key = wallet.create_accounts_key()?;
std::fs::create_dir_all(accounts_path.clone()).unwrap();
let accounts_storage = LmdbKCVStore::open(&accounts_path, accounts_key.slice().clone());
let symkey = SymKey::random();
let invite_code = InvitationCode::Admin(symkey.clone());
let _ = Invitation::create(&invite_code, 0, &accounts_storage)?;
let invitation = p2p_net::types::Invitation::V0(InvitationV0 {
code: Some(symkey),
name: Some("your NG Box, as admin".into()),
url: None,
bootstrap: admin_invite.unwrap(),
});
for link in invitation.get_urls() {
println!("The admin invitation link is: {}", link)
}
} else {
if admin_invite.is_some() {
log_warn!("Cannot add an admin invitation anymore, as it is not the first start of the server.");
}
accounts_key = wallet.get_or_create_accounts_key()?;
}
std::fs::create_dir_all(accounts_path.clone()).unwrap();
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let accounts_storage = LmdbKCVStore::open(&accounts_path, accounts_key.slice().clone());
// create/open the PEERS storage
let peers_key = wallet.get_or_create_peers_key()?;
let mut peers_path = path.clone();
peers_path.push("peers");
std::fs::create_dir_all(peers_path.clone()).unwrap();
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let peers_storage = LmdbKCVStore::open(&peers_path, peers_key.slice().clone());
Ok(LmdbBrokerStorage {
wallet_storage,
accounts_storage,
peers_storage,
})
}
}

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use p2p_net::types::{BrokerOverlayConfigV0, ListenerV0};
use p2p_repo::types::PrivKey;
use p2p_repo::types::{PrivKey, PubKey};
use serde::{Deserialize, Serialize};
/// Registration config
@ -27,6 +27,8 @@ pub struct DaemonConfigV0 {
pub overlays_configs: Vec<BrokerOverlayConfigV0>,
pub registration: RegistrationConfig,
pub admin_user: Option<PubKey>,
}
/// Daemon config

@ -337,6 +337,7 @@ mod test {
user_priv,
client,
client_priv,
info: ClientInfo::new(ClientType::Cli, "".into(), "".into()),
}),
)
.await;

@ -43,6 +43,7 @@ impl IConnect for ConnectionWebSocket {
remote_peer: DirectPeerId,
config: StartConfig,
) -> Result<ConnectionBase, NetError> {
log_debug!("url {}", url);
let mut cnx = ConnectionBase::new(ConnectionDir::Client, TransportProtocol::WS);
let (mut ws, wsio) = WsMeta::connect(url, None).await.map_err(|e| {

@ -53,7 +53,6 @@ pub struct BrokerPeerInfo {
#[derive(Debug)]
pub struct DirectConnection {
ip: IP,
interface: String,
remote_peer_id: X25519PrivKey,
tp: TransportProtocol,
//dir: ConnectionDir,
@ -62,7 +61,7 @@ pub struct DirectConnection {
pub static BROKER: Lazy<Arc<RwLock<Broker>>> = Lazy::new(|| Arc::new(RwLock::new(Broker::new())));
pub struct Broker {
pub struct Broker<'a> {
direct_connections: HashMap<IP, DirectConnection>,
/// tuple of optional userId and peer key in montgomery form. userId is always None on the server side.
peers: HashMap<(Option<PubKey>, X25519PubKey), BrokerPeerInfo>,
@ -76,13 +75,13 @@ pub struct Broker {
shutdown_sender: Sender<ProtocolError>,
closing: bool,
my_peer_id: Option<PubKey>,
storage: Option<Box<dyn BrokerStorage + Send + Sync>>,
storage: Option<Box<dyn BrokerStorage + Send + Sync + 'a>>,
test: u32,
tauri_streams: HashMap<String, Sender<Commit>>,
}
impl Broker {
impl<'a> Broker<'a> {
/// helper function to store the sender of a tauri stream in order to be able to cancel it later on
/// only used in Tauri, not used in the JS SDK
pub fn tauri_stream_add(&mut self, stream_id: String, sender: Sender<Commit>) {
@ -104,7 +103,7 @@ impl Broker {
}
}
pub fn set_storage(&mut self, storage: impl BrokerStorage + 'static) {
pub fn set_storage(&mut self, storage: impl BrokerStorage + 'a) {
self.storage = Some(Box::new(storage));
}
@ -152,7 +151,7 @@ impl Broker {
}
}
Authorization::ExtMessage => Err(ProtocolError::AccessDenied),
Authorization::Client(_) => Err(ProtocolError::AccessDenied),
Authorization::Client(user) => Err(ProtocolError::AccessDenied),
Authorization::Core => Err(ProtocolError::AccessDenied),
Authorization::Admin(_) => Err(ProtocolError::AccessDenied),
Authorization::OverlayJoin(_) => Err(ProtocolError::AccessDenied),
@ -401,6 +400,7 @@ impl Broker {
let _ = self.shutdown_sender.send(ProtocolError::Closing).await;
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn accept(
&mut self,
mut connection: ConnectionBase,
@ -460,33 +460,69 @@ impl Broker {
Ok(())
}
pub async fn attach_peer_id(
#[cfg(not(target_arch = "wasm32"))]
pub async fn attach_and_authorize_peer_id(
&mut self,
remote_bind_address: BindAddress,
local_bind_address: BindAddress,
remote_peer_id: X25519PrivKey,
core: Option<String>,
) -> Result<(), NetError> {
// if client is None it means we are Core mode
client: Option<ClientAuthContentV0>,
) -> Result<(), ProtocolError> {
log_debug!("ATTACH PEER_ID {:?}", remote_peer_id);
let already = self.peers.get(&(None, remote_peer_id));
if (already.is_some()) {
match already.unwrap().connected {
PeerConnection::NONE => {}
_ => {
return Err(ProtocolError::PeerAlreadyConnected);
}
};
}
// find the listener
let listener_id = self
.bind_addresses
.get(&local_bind_address)
.ok_or(ProtocolError::AccessDenied)?;
let listener = self
.listeners
.get(listener_id)
.ok_or(ProtocolError::AccessDenied)?;
// authorize
if client.is_none() {
// it is a Core connection
if !listener.config.is_core() {
return Err(ProtocolError::AccessDenied);
}
} else {
if !listener.config.accepts_client() {
return Err(ProtocolError::AccessDenied);
}
}
let mut connection = self
.anonymous_connections
.remove(&(local_bind_address, remote_bind_address))
.ok_or(NetError::InternalError)?;
.ok_or(ProtocolError::BrokerError)?;
connection.reset_shutdown(remote_peer_id).await;
let ip = remote_bind_address.ip;
let connected = if core.is_some() {
let connected = if let Some(client_auth) = client {
// TODO add client to storage
PeerConnection::Client(connection)
} else {
let dc = DirectConnection {
ip,
interface: core.clone().unwrap(),
remote_peer_id,
tp: connection.transport_protocol(),
cnx: connection,
};
self.direct_connections.insert(ip, dc);
PeerConnection::Core(ip)
} else {
PeerConnection::Client(connection)
};
let bpi = BrokerPeerInfo {
lastPeerAdvert: None,
@ -521,29 +557,39 @@ impl Broker {
return Err(NetError::Closing);
}
// TODO check that not already connected to peer
// IpAddr::from_str("127.0.0.1");
log_info!("CONNECTING");
let remote_peer_id_dh = remote_peer_id.to_dh_from_ed();
let already = self
.peers
.get(&(config.get_user(), *remote_peer_id_dh.slice()));
if already.is_some() {
match already.unwrap().connected {
PeerConnection::NONE => {}
_ => {
return Err(NetError::PeerAlreadyConnected);
}
};
}
let mut connection = cnx
.open(
config.get_url(),
peer_privk.clone(),
peer_pubk,
remote_peer_id,
remote_peer_id_dh,
config.clone(),
)
.await?;
let join = connection.take_shutdown();
let remote_peer_id_dh = remote_peer_id.to_dh_slice();
let connected = match &config {
StartConfig::Core(config) => {
let ip = config.addr.ip.clone();
let dc = DirectConnection {
ip,
interface: config.interface.clone(),
remote_peer_id: remote_peer_id_dh,
remote_peer_id: *remote_peer_id_dh.slice(),
tp: connection.transport_protocol(),
cnx: connection,
};
@ -560,7 +606,7 @@ impl Broker {
};
self.peers
.insert((config.get_user(), remote_peer_id_dh), bpi);
.insert((config.get_user(), *remote_peer_id_dh.slice()), bpi);
async fn watch_close(
mut join: Receiver<Either<NetError, X25519PrivKey>>,
@ -602,7 +648,7 @@ impl Broker {
cnx,
peer_privk,
peer_pubk,
remote_peer_id_dh,
*remote_peer_id_dh.slice(),
config,
));
Ok(())

@ -93,7 +93,7 @@ pub enum FSMstate {
Start,
Probe,
Relay,
Noise0,
Noise0, // unused
Noise1,
Noise2,
Noise3, // unused
@ -150,6 +150,7 @@ pub struct ClientConfig {
pub user_priv: PrivKey,
pub client: PubKey,
pub client_priv: PrivKey,
pub info: ClientInfo,
}
#[derive(PartialEq, Debug, Clone)]
@ -373,7 +374,7 @@ impl NoiseFSM {
self.local.take().unwrap().to_dh(),
)),
None,
Some(*self.remote.unwrap().to_dh_from_ed().slice()),
Some(*self.remote.unwrap().slice()),
None,
);
@ -538,20 +539,7 @@ impl NoiseFSM {
return Err(ProtocolError::NoiseHandshakeFailed);
}
let peer_id = handshake.get_rs().unwrap();
//self.remote = Some(peer_id);
let (local_bind_address, remote_bind_address) =
self.bind_addresses.ok_or(ProtocolError::BrokerError)?;
BROKER
.write()
.await
.attach_peer_id(
remote_bind_address,
local_bind_address,
peer_id,
None,
)
.await
.map_err(|_| ProtocolError::BrokerError)?;
self.remote = Some(PubKey::X25519PubKey(peer_id));
let ciphers = handshake.get_ciphers();
self.noise_cipher_state_enc = Some(ciphers.1);
@ -586,11 +574,13 @@ impl NoiseFSM {
if let StartConfig::Client(client_config) =
self.config.as_ref().unwrap()
{
let ClientInfo::V0(info) = &client_config.info;
let content = ClientAuthContentV0 {
user: client_config.user,
client: client_config.client,
/// Nonce from ServerHello
nonce: hello.nonce().clone(),
info: info.clone(),
};
let ser = serde_bare::to_vec(&content)?;
let sig =
@ -610,7 +600,9 @@ impl NoiseFSM {
}
}
}
FSMstate::ServerHello => {
FSMstate::ServerHello =>
{
#[cfg(not(target_arch = "wasm32"))]
if let Some(msg) = msg_opt.as_ref() {
if self.dir.is_server() {
if let ProtocolMessage::ClientAuth(client_auth) = msg {
@ -625,8 +617,20 @@ impl NoiseFSM {
if verif.is_err() {
result = verif.unwrap_err().into();
} else {
// TODO check that the device has been registered for this user. if not, set result = AccessDenied
let (local_bind_address, remote_bind_address) =
self.bind_addresses.ok_or(ProtocolError::BrokerError)?;
result = BROKER
.write()
.await
.attach_and_authorize_peer_id(
remote_bind_address,
local_bind_address,
*self.remote.unwrap().slice(),
Some(client_auth.content_v0()),
)
.await
.err()
.unwrap_or(ProtocolError::NoError);
}
let auth_result = AuthResult::V0(AuthResultV0 {
result: result.clone() as u16,

@ -31,6 +31,7 @@ pub enum NetError {
ProtocolError,
AccessDenied,
InternalError,
PeerAlreadyConnected,
Closing,
} //MAX 50 NetErrors

@ -33,7 +33,7 @@ pub mod tests;
pub mod site;
pub static NG_BOOTSTRAP_LOCAL_URL: &str = "/.ng_bootstrap";
pub static NG_BOOTSTRAP_LOCAL_PATH: &str = "/.ng_bootstrap";
#[cfg(debug_assertions)]
pub static WS_PORT: u16 = 14400;

@ -399,7 +399,7 @@ impl BrokerServerV0 {
}
}
pub async fn is_public_broker(&self) -> bool {
pub fn is_public_server(&self) -> bool {
match &self.server_type {
BrokerServerTypeV0::Localhost(_) => false,
BrokerServerTypeV0::BoxPrivate(_) => false,
@ -672,18 +672,30 @@ impl TryFrom<String> for CreateAccountBSP {
}
}
impl CreateAccountBSP {
pub fn encode(&self) -> Option<String> {
let payload_ser = serde_bare::to_vec(self).ok();
if payload_ser.is_none() {
return None;
}
Some(base64_url::encode(&payload_ser.unwrap()))
}
}
/// Create an account at a Broker Service Provider (BSP). Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateAccountBSPV0 {
pub invitation_code: Option<SymKey>,
pub invitation: Option<InvitationV0>,
pub additional_bootstrap: Option<BootstrapContentV0>,
/// the user asking to create an account
pub user: PubKey,
/// signature over serialized invitation, with user key
pub sig: Sig,
/// signature over serialized invitation code, with user key
// pub sig: Sig,
/// for web access, will redirect after successful signup. if left empty, it means user is on native app.
/// for web access, will redirect after successful signup. if left empty, it means user was on native app.
pub redirect_url: Option<String>,
}
@ -854,7 +866,6 @@ pub struct ListenerV0 {
pub accept_forward_for: AcceptForwardForV0,
// impl fn is_private()
// returns false if public IP in interface, or if PublicDyn, PublicStatic
// if the ip is local or private, and the forwarding is not PublicDyn nor PublicStatic, (if is_private) then the app is served on HTTP get of /
// an interface with no accept_forward_for and no accept_direct, is de facto, disabled
}
@ -890,6 +901,30 @@ impl ListenerV0 {
}
}
pub fn is_core(&self) -> bool {
match self.accept_forward_for {
AcceptForwardForV0::PublicStatic(_) => true,
AcceptForwardForV0::PublicDyn(_) => true,
AcceptForwardForV0::PublicDomain(_) | AcceptForwardForV0::PublicDomainPeer(_) => false,
AcceptForwardForV0::PrivateDomain(_) => false,
AcceptForwardForV0::No => self.if_type == InterfaceType::Public,
}
}
pub fn accepts_client(&self) -> bool {
match self.accept_forward_for {
AcceptForwardForV0::PublicStatic(_)
| AcceptForwardForV0::PublicDyn(_)
| AcceptForwardForV0::PublicDomain(_)
| AcceptForwardForV0::PublicDomainPeer(_) => self.accept_direct || !self.refuse_clients,
AcceptForwardForV0::PrivateDomain(_) => true,
AcceptForwardForV0::No => {
self.if_type == InterfaceType::Public && !self.refuse_clients
|| self.if_type != InterfaceType::Public
}
}
}
pub fn get_bootstraps(&self, addrs: Vec<BindAddress>) -> Vec<BrokerServerTypeV0> {
let mut res: Vec<BrokerServerTypeV0> = vec![];
match self.accept_forward_for {
@ -2910,6 +2945,8 @@ pub struct ClientAuthContentV0 {
/// Client pub key
pub client: PubKey,
pub info: ClientInfoV0,
/// Nonce from ServerHello
#[serde(with = "serde_bytes")]
pub nonce: Vec<u8>,

@ -11,7 +11,7 @@
use crate::types::BootstrapContent;
use crate::types::Invitation;
use crate::NG_BOOTSTRAP_LOCAL_URL;
use crate::NG_BOOTSTRAP_LOCAL_PATH;
use async_std::task;
use ed25519_dalek::*;
use futures::{channel::mpsc, select, Future, FutureExt, SinkExt};
@ -61,6 +61,7 @@ const APP_PREFIX: &str = "";
pub async fn retrieve_local_bootstrap(
location_string: String,
invite_string: Option<String>,
must_be_public: bool,
) -> Option<Invitation> {
let invite1: Option<Invitation> = if invite_string.is_some() {
let invitation: Result<Invitation, NgError> = invite_string.clone().unwrap().try_into();
@ -72,7 +73,7 @@ pub async fn retrieve_local_bootstrap(
log_debug!("invite_String {:?} invite1{:?}", invite_string, invite1);
let invite2: Option<Invitation> = {
let resp = reqwest::get(format!("{}{}", APP_PREFIX, NG_BOOTSTRAP_LOCAL_URL)).await;
let resp = reqwest::get(format!("{}{}", APP_PREFIX, NG_BOOTSTRAP_LOCAL_PATH)).await;
if resp.is_ok() {
let resp = resp.unwrap().json::<BootstrapContent>().await;
resp.ok().map(|v| v.into())
@ -91,10 +92,12 @@ pub async fn retrieve_local_bootstrap(
if res.is_some() {
for server in res.as_ref().unwrap().get_servers() {
if server
.get_ws_url(Some(location_string.clone()))
.await
.is_some()
if must_be_public && server.is_public_server()
|| !must_be_public
&& server
.get_ws_url(Some(location_string.clone()))
.await
.is_some()
{
return res;
}

@ -69,6 +69,15 @@ impl SymKey {
}
}
impl TryFrom<&[u8]> for SymKey {
type Error = NgError;
fn try_from(buf: &[u8]) -> Result<Self, NgError> {
let sym_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
let sym_key = SymKey::ChaCha20Key(sym_key_array);
Ok(sym_key)
}
}
/// Curve25519 public key Edwards form
pub type Ed25519PubKey = [u8; 32];

Loading…
Cancel
Save