diff --git a/.editorconfig b/.editorconfig index 90b4a7b75..a16c1af96 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,3 +22,6 @@ indent_size = 2 [*.nsi] indent_size = 2 + +[*.{css,js,html}] +indent_size = 2 diff --git a/client/api.md b/client/api.md index 3e350ff52..535d24e25 100644 --- a/client/api.md +++ b/client/api.md @@ -15,65 +15,65 @@ HTTP ``` Voici la liste des erreurs par ordre alphabétique: -- `archived_workspace` (HTTP 403) -- `archiving_not_allowed` (HTTP 403) -- `archiving_period_is_too_short` (HTTP 400) -- `authentication_requested` (HTTP 401) -- `bad_claimer_sas` (HTTP 400) -- `bad_data` (HTTP 400) -- `bad_greeter_sas` (HTTP 400) -- `bad_key` (HTTP 400) -- `bad_timestamp_configuration` (HTTP 400) -- `cannot_delete_root_folder` (HTTP 400) -- `cannot_move_root_folder` (HTTP 400) -- `cannot_use_both_authentication_modes` (HTTP 400) -- `claimer_already_member` (HTTP 400) -- `claimer_not_a_member` (HTTP 400) -- `connection_refused_by_server` (HTTP 502) -- `deleted_workspace` (HTTP 410) -- `destination_parent_not_a_folder` (HTTP 400) -- `device_not_found` (HTTP 404) +- `archived_workspace` (HTTP 403) opération non autorisée, cause : espace archivé +- `archiving_not_allowed` (HTTP 403) archivage impossible +- `archiving_period_is_too_short` (HTTP 400) la période d'archivage est trop courte +- `authentication_requested` (HTTP 401) authentification requise +- `bad_claimer_sas` (HTTP 400) mauvais code sas +- `bad_data` (HTTP 400) mauvais champ (le champ doit être présent dans le champ fields) +- `bad_greeter_sas` (HTTP 400) mauvais code sas +- `bad_key` (HTTP 400) mot de passe incorrect +- `bad_timestamp_configuration` (HTTP 400) Date incorrecte +- `cannot_delete_root_folder` (HTTP 400) impossible de supprimer le dossier racine +- `cannot_move_root_folder` (HTTP 400) impossible de déplacer le dossier racine +- `claimer_already_member` (HTTP 400) utilisateur déjà enrôlé +- `claimer_not_a_member` (HTTP 400) Shamir : le compte à récupérer n'existe pas +- `connection_refused_by_server` (HTTP 502) Connexion refusée par le serveur +- `deleted_workspace` (HTTP 410) Opération impossible, espace supprimé +- `destination_parent_not_a_folder` (HTTP 400) déplacement impossible, la destination n'est pas un dossier +- `device_not_found` (HTTP 404) fichier de clé non trouvé sur le poste - `email_not_in_recipients` (HTTP 400) -- `failed_to_disable_offline_availability` (HTTP 400) -- `failed_to_enable_offline_availability` (HTTP 400) -- `file_exists` (HTTP 400) -- `forbidden_workspace` (HTTP 403) -- `invalid_configuration` (HTTP 400) -- `invalid_passphrase` (HTTP 400) -- `invalid_state` (HTTP 409) -- `invitation_already_used` (HTTP 400) -- `invitation_not_found` (HTTP 400) -- `json_body_expected` (HTTP 400) -- `mountpoint_already_mounted` (HTTP 400) -- `mountpoint_not_mounted` (HTTP 404) -- `no_shamir_recovery_setup` (HTTP 400) -- `not_enough_shares` (HTTP 400) -- `not_a_file` (HTTP 404) -- `not_a_folder` (HTTP 404) -- `not_connected_to_rie` (HTTP 401) -- `not_found` (HTTP 404) -- `not_setup` (HTTP 404) -- `offline` (HTTP 503) -- `offline_availability_already_disabled` (HTTP 400) -- `offline_availability_already_enabled` (HTTP 400) -- `organization_already_bootstrapped` (HTTP 400) -- `precondition_failed` (HTTP 409) -- `read_only_workspace` (HTTP 403) -- `recipient_already_recovered` (HTTP 400) -- `sharing_not_allowed` (HTTP 403) -- `source_not_a_folder` (HTTP 404) -- `unexpected_error` (HTTP 400) -- `unknown_destination_parent` (HTTP 404) -- `unknown_email` (HTTP 404) -- `unknown_entry` (HTTP 404) -- `unknown_file` (HTTP 404) -- `unknown_folder` (HTTP 404) -- `unknown_organization` (HTTP 404) -- `unknown_parent` (HTTP 404) -- `unknown_source` (HTTP 404) -- `unknown_token` (HTTP 404) -- `unknown_workspace` (HTTP 404) -- `users_not_found` (HTTP 400) +- `failed_to_disable_offline_availability` (HTTP 400) impossible de désactiver la rémanence des données +- `failed_to_enable_offline_availability` (HTTP 400) impossible d'activer la rémanence des données +- `file_exists` (HTTP 400) le fichier existe déjà +- `forbidden_workspace` (HTTP 403) accès à l'espace interdit +- `host_machine_not_compliant` (HTTP 401) le poste de répond pas aux conditions de conformité +- `invalid_configuration` (HTTP 400) Shamir : mauvaise configuration +- `invalid_passphrase` (HTTP 400) import de clé : mauvaise passphrase +- `invalid_state` (HTTP 409) état invalide (désynchonisation des utilisateurs), il faut repartir à l'étape 1 +- `invitation_already_used` (HTTP 400) invitation déjà utilisée +- `invitation_not_found` (HTTP 400) invitation introuvable +- `json_body_expected` (HTTP 400) json attendu en payload +- `mountpoint_already_mounted` (HTTP 400) point de montage déjà monté +- `mountpoint_not_mounted` (HTTP 404) point de montage non monté +- `no_shamir_recovery_setup` (HTTP 400) Shamir : pas de configuration présente +- `not_enough_shares` (HTTP 400) Shamir : pas assez de partage +- `not_a_file` (HTTP 404) pas un fichier +- `not_a_folder` (HTTP 404) pas un dossier +- `not_connected_to_rie` (HTTP 401) connexion impossible, utilisateur non connecté sur le RIE +- `not_found` (HTTP 404) introuvable +- `not_setup` (HTTP 404) Shamir : non paramétré +- `offline` (HTTP 503) serveur injoignable +- `offline_availability_already_disabled` (HTTP 400) rémanence des donnée déjà désactivé +- `offline_availability_already_enabled` (HTTP 400) rémanence des donnée déjà activé +- `organization_already_bootstrapped` (HTTP 400) organisation déjà validée +- `precondition_failed` (HTTP 409) mauvais nom actuel de l'espace +- `read_only_workspace` (HTTP 403) espace en lecture seule +- `recipient_already_recovered` (HTTP 400) Shamir +- `sharing_not_allowed` (HTTP 403) partage non autorisé +- `source_not_a_folder` (HTTP 404) la source n'est pas un dossier +- `unexpected_error` (HTTP 400) Shamir +- `unknown_destination_parent` (HTTP 404) destination inconnue +- `unknown_email` (HTTP 404) email inconnu +- `unknown_entry` (HTTP 404) entrée inconnue +- `unknown_file` (HTTP 404) fichier inconnue +- `unknown_folder` (HTTP 404) dossier inconnu +- `unknown_organization` (HTTP 404) organisation inconnue +- `unknown_parent` (HTTP 404) parent inconnu +- `unknown_source` (HTTP 404) source inconnue +- `unknown_token` (HTTP 404) token inconnu +- `unknown_workspace` (HTTP 404) espace inconnu +- `users_not_found` (HTTP 400) utilisateurs non trouvés Une erreur dans le formatage de la requête est retournée sous la forme suivante: @@ -173,8 +173,6 @@ Deux différentes options sont disponibles pour l'authentification : `email` correspondant à l'email utilisé lors de l'enrôlement RESANA Secure. -`user_password` doit être une chaîne de caractère échappé pour répondre aux normes json - **Response:** diff --git a/misc/test_book/auth.js b/misc/test_book/auth.js new file mode 100644 index 000000000..54635521e --- /dev/null +++ b/misc/test_book/auth.js @@ -0,0 +1,97 @@ +// LOGIN + +function login() { + const email = document.getElementById("auth-email").value; + const key = document.getElementById("auth-key").value; + const organization = document.getElementById("auth-organization-id").value; + const encryptedKey = document.getElementById("auth-encrypted-key").value; + const http = new XMLHttpRequest(); + http.open("POST", "http://localhost:5775/auth"); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ + email, + key, + organization, + encrypted_key: encryptedKey, + })); + http.onreadystatechange = (e) => { + document.getElementById("auth-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + document.getElementById("auth-info").innerHTML = `${email} | ${organization}`; + tokenSession = JSON.parse(http.responseText).token; + listWorkspaces(); + } + } +} + +const storageKey = "resana-secure-release-tests-key-auth"; + +function saveAuth() { + const auth = {}; + let storage = null; + auth['email'] = document.getElementById("auth-email").value; + auth['key'] = document.getElementById("auth-key").value; + auth['organization'] = document.getElementById("auth-organization-id").value; + auth['encryptedKey'] = document.getElementById("auth-encrypted-key").value; + auth['password'] = document.getElementById("auth-password").value; + storage = localStorage.getItem(storageKey) || "[]"; + storage = JSON.parse(storage); + storage.push(auth); + localStorage.setItem(storageKey, JSON.stringify(storage)); +} + +function listAuth() { + let saves = []; + const storage = localStorage.getItem(storageKey); + saves = JSON.parse(storage) || []; + const modal = document.getElementById("save-modal"); + modal.style.display = "block"; + const listElem = document.getElementById("saves-list"); + listElem.innerHTML = ""; + saves.forEach((save, index) => { + listElem.innerHTML += `
  • ${save.email} | ${save.key} | ${save.organization}
  • `; + }); +} + +function loadAuth(index) { + const storage = localStorage.getItem(storageKey); + const saves = JSON.parse(storage) || []; + document.getElementById("auth-email").value = saves[index].email; + document.getElementById("auth-key").value = saves[index].key; + document.getElementById("auth-organization-id").value = saves[index].organization; + document.getElementById("auth-encrypted-key").value = saves[index].encryptedKey; + document.getElementById("auth-password").value = saves[index].password; + const modal = document.getElementById("save-modal"); + modal.style.display = "none"; +} + +function closeAuthModal() { + const elem = document.getElementById("save-modal"); + elem.style.display = "none"; +} + + +// LOGOUT +function deconnect(force = false) { + const http = new XMLHttpRequest(); + http.open("DELETE", "http://localhost:5775/auth"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("auth-info").innerHTML = ""; + document.getElementById("logout-result").innerHTML = getHttpResult(http); + } + openAccount(force); +} + +function deconnectAll(force = false) { + const http = new XMLHttpRequest(); + http.open("DELETE", "http://localhost:5775/auth/all"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("auth-info").innerHTML = ""; + document.getElementById("logout-result").innerHTML = getHttpResult(http); + } + openAccount(force); +} diff --git a/misc/test_book/bootstrap.js b/misc/test_book/bootstrap.js new file mode 100644 index 000000000..353d5b8cb --- /dev/null +++ b/misc/test_book/bootstrap.js @@ -0,0 +1,18 @@ +function boostrap() { + const organizationUrl = document.getElementById("bootstrap-url").value; + const sequesterVerifyKey = document.getElementById("sequester-verify-key").value; + const email = document.getElementById("bootstrap-email").value; + const key = document.getElementById("bootstrap-key").value; + const http = new XMLHttpRequest(); + http.open("POST", "http://localhost:5775/organization/bootstrap"); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ + organization_url: organizationUrl, + email, + key, + sequester_verify_key: sequesterVerifyKey + })); + http.onreadystatechange = (e) => { + document.getElementById("bootstrap-result").innerHTML = getHttpResult(http); + } +} diff --git a/misc/test_book/common.js b/misc/test_book/common.js new file mode 100644 index 000000000..00bf7a287 --- /dev/null +++ b/misc/test_book/common.js @@ -0,0 +1,7 @@ +function getHttpResult(http) { + try { + return `${http.status}
    ` + JSON.stringify(JSON.parse(http.responseText), null, 4); + } catch (error) { + return `${http.status} ${http.responseURL}
    ` + http.responseText.replace(//gi, ">"); + } +} diff --git a/misc/test_book/file.js b/misc/test_book/file.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/folder.js b/misc/test_book/folder.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/index.html b/misc/test_book/index.html new file mode 100644 index 000000000..b437d79a8 --- /dev/null +++ b/misc/test_book/index.html @@ -0,0 +1,1242 @@ + + + + Resana Secure - release tests + + + + + + + + +
    +
    +
    + + Resana Secure - release tests +
    + +
    +
    + + + +
    + +
    + +
    +
    +

    Version de la release

    + + +

    Création de l'organisation de test

    +

    L'organisation de test doit être créée sur le serveur Resana Secure de test avec séquestre et anti-virus.

    +

    ID organisation : test_release_vX.Y.Z

    + parsec core create_organization test_release_<numéro de version> --addr parsec://<server url> --administration-token <administration token> + +

    Génération des clés de séquestre et anti-virus

    +

    + Les clés doivent être générées sur Linux (Ubuntu 22.04). +

    # clé de signature
    +openssl genrsa -out ./authority_key.private 4096
    +openssl rsa -in ./authority_key.private -out ./authority_key_.pub -pubout -outform PEM
    +# clé de séquestre
    +openssl genrsa -out ./sequestre_key.private 4096
    +openssl rsa -in ./sequestre_key.private -out ./sequestre_key.pub -pubout -outform PEM
    +parsec backend sequester generate_service_certificate --service-label="Service sequestre" --service-public-key=./sequestre_key.pub --authority-private-key=./authority_key.private -o ./sequestre_service_certificate.pem
    +# clé d'anti-virus
    +openssl genrsa -out ./antivirus_key.private 4096
    +openssl rsa -in ./antivirus_key.private -out ./antivirus_key.pub -pubout -outform PEM
    +parsec backend sequester generate_service_certificate --service-label="Service antivirus" --service-public-key=./antivirus_key.pub --authority-private-key=./authority_key.private -o ./antivirus_service_certificate.pem
    +

    + +

    Vérifier que le filtrage d'IP fonctionne.

    +
    + +
    +

    Installation

    +

    Réinstallation

    +

    Désinstallation

    +
    + +
    +

    Bootstrap

    + + + + +
    + + +
    + + +
    + + +
    + +
    
    +        

    Si OK, lancer le séquestre et l'anti-virus sur l'organisation.

    +
    parsec backend sequester import_service_certificate --service-certificate ./sequestre_service_certificate.pem --organization test_release_<numéro de version> --service-type storage
    +  parsec backend sequester import_service_certificate --service-certificate ./antivirus_service_certificate.pem --organization test_release_<numéro de version> --service-type webhook --webhook-url http://127.0.0.1:5775/submit
    +
    + +
    +

    Auth

    +

    Tests à réaliser

    +
      +
    • Connexion KO avec key (mauvaise key) => statut 400
    • +
    • Connexion KO avec key (device not found) => statut 404
    • +
    • Connexion OK avec key => statut 200
    • +
    • Connexion KO avec user_password/encrypted_key (mauvais mot de passe)
    • +
    • Connexion KO avec user_password/encrypted_key (device not found)
    • +
    • Connexion OK avec user_password/encrypted_key => statut 200
    • +
    • Spammer le bouton "Se connecter" (avec mauvaise key) pour vérifier la protection anti-bruteforce
    • +
    + + + +
    + + +
    + + +
    + + +
    + + +
    + + + +
    +
    
    +
    +        
    +        
    +        
    + +
    
    +      
    + + + +
    +

    Workspaces

    +

    Tests à réaliser

    +
      +
    • Création d'un workspace avec un nom valide => statut 201
    • +
    • Création d'un workspace avec un nom invalide (contient entre autre un des caractères suivants \ / : * ? " > < |) => statut 400
    • +
    • Vérifier la création en chargeant la liste
    • +
    • Vérifier la création en regardant le point de montage (si actif)
    • +
    • Renommer un point de montage avec un nouveau nom valide => 200
    • +
    • Renommer un point de montage avec l'ancien nom incorrect => 409
    • +
    • Renommer un point de montage avec un nouveau nom invalide => 400
    • +
    • Renommer un point de montage avec un faux ID organisation => 404
    • +
    • Vérifier le renommage en rechargeant la liste
    • +
    • Vérifier le renommage en regardant le point de montage (si actif)
    • +
    + +
    +

    Liste

    + +
    
    +        
    + +
    +

    Création

    + + + +
    
    +        
    + +
    +

    Renommage

    + + +
    + + +
    + + +
    + +
    
    +        
    +
    + +
    + TODO +

    Offline availability

    + + +
    
    +      
    + +
    +

    Mount/Unmount

    + TODO +
    + +
    +

    Folders

    +

    Tests à réaliser

    +
      +
    • TODO
    • +
    • (contient entre autre un des caractères suivants \ / : * ? " > < |)
    • +
    + + + + +
    +

    Liste

    + +
    
    +        
    + +
    +

    Création

    + + +
    + + +
    + +
    
    +        
    + +
    +

    Renommer/Déplacer

    + + +
    + + +
    + + +
    + +
    
    +        
    + +
    +

    Suppression

    + + +
    + +
    
    +        
    +
    + +
    +

    Files

    +

    Tests à réaliser

    +
      +
    • TODO
    • +
    • (contient entre autre un des caractères suivants \ / : * ? " > < |)
    • +
    + + + +
    + + + +
    +

    Liste

    + +
    
    +        
    + +
    +

    Création

    + + +
    + + +
    + +
    
    +        
    + +
    +

    Création multipart

    + + +
    + + +
    + +
    
    +        
    + +
    +

    Ouvrir

    + + +
    + +
    + +
    +

    Renommer/Déplacer

    + TODO +
    + +
    +

    Suppression

    + TODO +
    +
    + + + +
    +

    Invitation

    +

    Tests à réaliser

    +
      +
    • Création OK => statut 200
    • +
    • Suppression OK => statut 204
    • +
    + +
    +

    Liste

    + +
    
    +        
    + +
    +

    Création User

    + + +
    + +
    
    +        
    + +
    +

    Création Device

    + +
    
    +        
    + +
    +

    Suppression

    + + +
    + +
    
    +        
    +
    + +
    +

    Greeter

    + + + +

    1-wait-peer-ready

    + +
    
    +
    +        

    2-check-trust

    + +
    
    +
    +        

    3-wait-peer-trust

    + + +
    + +
    
    +
    +        

    4-finalize

    + + +
    + + +
    + +
    
    +      
    + +
    +

    Claimer

    + + + +

    0-retreive-info

    + +
    
    +
    +        

    1-wait-peer-ready

    + +
    
    +
    +        

    2-check-trust

    + + +
    + +
    
    +
    +        

    3-wait-peer-trust

    + +
    
    +
    +        

    4-finalize

    + + +
    + +
    
    +      
    + +
    +

    Humans

    +
    +

    Liste

    + +
    
    +        
    +
    +

    Révocation

    + + +
    + +
    
    +        
    +
    + +
    +

    Share

    + + + + +
    +

    Liste

    + +
    
    +        
    + +
    +

    Edit

    + + +
    + + +
    + +
    
    +        
    +
    + +
    +

    Recovery

    + +

    Export

    +

    + +

    
    +        

    + +

    Import

    +

    + + +
    + + +
    + + +
    + +

    
    +        

    +
    + +
    +

    Shamir

    + +

    Get

    +

    + +

    
    +        

    +

    + +

    
    +        

    + +

    Setup

    +

    + +

    
    +        

    + +

    Delete

    +

    + +

    
    +        

    +
    + +
    +

    Sequester

    +

    + Création du dump du séquestre +

    parsec backend sequester list_services --organization test_20230419
    +parsec backend human_accesses --organization test_20230419
    +parsec backend sequester export_realm --organization test_20230419 --service 99ec544e445344a8a985f30a24c2c1bf --realm 6005613898fb45e0ab8b563ceeaa49f5 --output realm-6005613898fb45e0ab8b563ceeaa49f5.sequester-export
    +

    +
    + +
    +

    Déconnexion

    + + +
    
    +      
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FonctionnalitésClient X avec Backend Y
    Installation
    Installer Resana Secure
    Désinstaller Resana Secure
    Désinstaller Resana Secure (client ouvert)
    Réinstaller Resana Secure
    Réinstaller Resana Secure (client ouvert)
    Login
    Liste des devices
    Connexion à un device
    Se délogger
    Changer de mot de passe
    Workspaces
    Liste des workspaces
    Créer des workspaces
    Partager un workspace
    Retirer une personne d'un workspace
    Rechiffrer un workspaceNot implemented
    Afficher l'historique d'un fichierNot implemented
    Monter un workspace à un moment donnée
    Vérifier la synchronisation d'un workspace
    Rendre un workspace disponible hors-ligne
    Filtrer les workspacesNot implemented
    Fichiers
    Créer un dossier avec l'API
    Naviguer dans l'arborescence
    Importer un fichier depuis l'API
    Importer un dossier depuis l'API
    Supprimer un fichier
    Renommer un fichier
    Copier/coller un fichier dans l'explorer
    Copier/coller un dossier dans l'explorer
    Supprimer un dossier dans l'explorer
    Supprimer un fichier dans l'explorer
    Renommer un fichier dans l'explorer
    Renommer un dossier dans l'explorer
    Vérifier la synchronisation d'un fichier
    Récupérer un lien vers un fichierNot implemented
    Utiliser un lien vers un fichierNot implemented
    Récupérer un lien timestamped vers un fichierNot implemented
    Utiliser un lien timestamped vers un fichierNot implemented
    Users
    Lister les users
    Masquer les utilisateurs révoquésNot implemented
    Inviter un utilisateur
    Masquer les invitationsNot implemented
    Filter les utilisateursNot implemented
    Accueillir un utilisateur
    Révoquer un utilisateur
    Se faire accueillir
    Devices
    Lister les devicesNot implemented
    Accueillir un nouveau device
    Claim un nouveau device
    Organisation
    Créer une nouvelle organisation
    Recovery
    Créer un recovery device
    Utiliser un recovery device
    LiensNot implemented
    Divers
    Ouvrir Resana
    Sequester client
    Bootstrap avec une clé de séquestre
    Ajouter un fichier avec un service de séquestre actif
    GUI: affichage séquestre présentNot implemented
    Webhook client
    Ajouter un fichier avec un service webhook actif
    Webhook backend
    Ajouter un service webhook
    Lister les services webhook
    Désactiver un service webhook
    Activer un service webhook
    Sequester backend
    Ajouter un service de séquestre avec clé générée sur Windows
    Ajouter un service un séquestre avec clé générée sur Linux
    Lister les services de séquestre
    Désactiver un service de séquestre
    Activer un service de séquestre
    Lister les accès utilisateur
    Exporter et valider les données de séquestre
    Exporter et valider les données de séquestre avec des données créées avec le service désactivé
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/misc/test_book/invitation.js b/misc/test_book/invitation.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/mount.js b/misc/test_book/mount.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/offline.js b/misc/test_book/offline.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/recovery.js b/misc/test_book/recovery.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/release.js b/misc/test_book/release.js new file mode 100644 index 000000000..ef16422a0 --- /dev/null +++ b/misc/test_book/release.js @@ -0,0 +1,9 @@ +let releaseVersion = "vX.Y.Z"; + +function releaseVersionChange(event) { + releaseVersion = document.getElementById("release-version").value; + const elements = document.getElementsByClassName("release-version"); + for (const element of elements) { + element.innerHTML = releaseVersion; + } +} diff --git a/misc/test_book/search.js b/misc/test_book/search.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/shamir.js b/misc/test_book/shamir.js new file mode 100644 index 000000000..e69de29bb diff --git a/misc/test_book/test_book.css b/misc/test_book/test_book.css new file mode 100644 index 000000000..2d57feeb2 --- /dev/null +++ b/misc/test_book/test_book.css @@ -0,0 +1,178 @@ +html { + --color-primary: #4092FF; + --color-secondary: #f0f7ff; + --color-tertiary: #004299; + --color-success: #28BA62; + --color-warning: #f6bd09; + --color-danger: #cd3246; + --color-light: #d7d8da; + --color-medium: #92949c; + + /* Primary */ + --parsec-color-light-primary-30-opacity15: hsla(212, 100%, 97.1%, 0.15000000596046448); + --parsec-color-light-primary-30: hsla(212, 100%, 97.1%, 1); + --parsec-color-light-primary-50: hsla(212, 100%, 94.9%, 1); + --parsec-color-light-primary-100: hsla(214, 100%, 90%, 1); + --parsec-color-light-primary-200: hsla(214, 100%, 80%, 1); + --parsec-color-light-primary-300: hsla(214, 100%, 70%, 1); + --parsec-color-light-primary-400: hsla(214, 100%, 60%, 1); + --parsec-color-light-primary-500: hsla(214, 100%, 62.5%, 1); + --parsec-color-light-primary-600: hsla(214, 100%, 40%, 1); + --parsec-color-light-primary-700: hsla(214, 100%, 30%, 1); + --parsec-color-light-primary-800: hsla(214, 100%, 20%, 1); + --parsec-color-light-primary-900: hsla(214, 100%, 10%, 1); + + /* Secondary */ + --parsec-color-light-secondary-contrast: hsla(240, 19.4%, 13.1%, 1); + --parsec-color-light-secondary-text: hsla(240, 20.2%, 34.9%, 1); + --parsec-color-light-secondary-grey: hsla(240, 19.6%, 60%, 1); + --parsec-color-light-secondary-light: hsla(240, 19.5%, 84.9%, 1); + --parsec-color-light-secondary-disabled: hsla(240, 20%, 93.1%, 1); + --parsec-color-light-secondary-medium: hsla(240, 20%, 96.1%, 1); + --parsec-color-light-secondary-premiere: hsla(240, 20%, 96.1%, 1); + --parsec-color-light-secondary-background: hsla(240, 20%, 98%, 1); + --parsec-color-light-secondary-inversed-contrast: hsla(0, 0%, 99.6%, 1); + + /* ------ alert light------ */ + --parsec-color-light-success-100: hsla(146, 61.5%, 94.9%, 1); + --parsec-color-light-success-500: hsla(144, 64.6%, 44.3%, 1); + --parsec-color-light-success-700: hsla(144, 64.7%, 40%, 1); + + --parsec-color-light-warning-100: hsla(45, 92.3%, 94.9%, 1); + --parsec-color-light-warning-500: hsla(46, 92.9%, 50%, 1); + --parsec-color-light-warning-700: hsla(45, 93.1%, 40%, 1); + + --parsec-color-light-danger-100: hsla(352, 60%, 95.1%, 1); + --parsec-color-light-danger-500: hsla(352, 60.8%, 50%, 1); + --parsec-color-light-danger-700: hsla(352, 60.8%, 40%, 1); + + /* ------ tags light------ */ + --parsec-color-tags-blue100: hsla(198, 100%, 92%, 1); + --parsec-color-tags-blue500: hsla(198, 82%, 36%, 1); + --parsec-color-tags-indigo100: hsla(247, 100%, 95%, 1); + --parsec-color-tags-indigo500: hsla(245, 100%, 36%, 1); + --parsec-color-tags-orange100: hsla(37, 100%, 95%, 1); + --parsec-color-tags-orange500: hsla(36, 100%, 36%, 1); + --parsec-color-tags-green100: hsla(68, 100%, 92%, 1); + --parsec-color-tags-green500: hsla(72, 90%, 32%, 1); + + /* **** Parsec shadow **** */ + --parsec-shadow-light: 0px 4px 12px rgba(0, 0, 0, 0.08); + --parsec-shadow-strong: 0px 4px 20px rgba(0, 0, 0, 0.15); + --parsec-shadow-toggle: -2px 1px 6px rgba(0, 0, 0, 0.24); +} + +body { + margin: 0; +} + +nav { + top: 56px; +} + +#account-menu { + top: 56px; + right: 0; +} + +main { + margin: 0.5em 1em; +} + +section { + display: none; +} + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 35px; + height: 20px; +} +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--parsec-color-light-secondary-grey); + -webkit-transition: .2s; + transition: .2s; +} +.slider:before { + position: absolute; + content: ""; + height: 14px; + width: 14px; + left: 3px; + bottom: 3px; + background-color: var(--parsec-color-light-primary-30); + -webkit-transition: .2s; + transition: .2s; +} +input:checked + .slider { + background-color: var(--parsec-color-light-primary-600); +} +input:focus + .slider { + box-shadow: 0 0 1px var(--parsec-color-light-primary-600); +} +input:checked + .slider:before { + -webkit-transform: translateX(15px); + -ms-transform: translateX(15px); + transform: translateX(15px); +} +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} +.slider.round:before { + border-radius: 50%; +} + +/***** modal *****/ +.modal { + display: none; +} +.modal-back { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba( 0, 0, 0, .25 ) +} +.modal-container { + position: fixed; + top: 50%; + left: 50%; + padding: 25px; + background: white; + transform: translate(-50%, -50%) +} + +table { + text-align: left; + margin-top: 0.5em; +} + +table, th, td { + border: 1px solid black; + border-collapse: collapse; +} + +.result-section { + background-color: #92949c; +} + +.not-implemented { + background-color: #f6bd09; +} diff --git a/misc/test_book/test_book.js b/misc/test_book/test_book.js new file mode 100644 index 000000000..2f44b471e --- /dev/null +++ b/misc/test_book/test_book.js @@ -0,0 +1,892 @@ +// NAV +function openNav() { + const nav = document.getElementsByTagName("nav")[0]; + if (nav.style.opacity === "") { + nav.style.display = "block"; + nav.style.opacity = "initial"; + } else { + nav.style.display = "none"; + nav.style.opacity = ""; + } +} + +function openAccount(forceClose = false) { + const nav = document.getElementById("account-menu"); + if (nav.style.opacity !== "" || forceClose) { + nav.style.display = "none"; + nav.style.opacity = ""; + } else { + nav.style.display = "block"; + nav.style.opacity = "initial"; + } +} + +const sections = [ + "release", + "bootstrap", + "installation", + "auth", + "workspace", + "offline", + "mount", + "folder", + "file", + "search", + "invitation", + "invitation-greeter", + "invitation-claimer", + "human", + "share", + "recovery", + "shamir", + "sequester", + "logout", + "result" +]; + +function navTo(className) { + sections.forEach(section => { + const elm = document.getElementById(section); + elm.style.display = section === className ? "block" : "none"; + }); + openNav(); +} + +// AUTH +let tokenSession = null; + +const saltDerive2 = new Uint8Array("122,205,180,252,110,57,134,101,147,170,189,150,191,228,84,206".split(",")); + +function getKeyParsecMaterialParsec(passwordACharger) { + let enc = new TextEncoder(); + return window.crypto.subtle.importKey( + "raw", + enc.encode(passwordACharger), + {name: "PBKDF2"}, + false, + ["deriveBits", "deriveKey"] + ); +} + +async function getKeyParsec(keyMaterial, salt) { + return window.crypto.subtle.deriveKey( + { + "name": "PBKDF2", + salt: salt, + "iterations": 100000, + "hash": "SHA-256" + }, + keyMaterial, + { "name": "AES-GCM", "length": 256}, + true, + [ "encrypt", "decrypt" ] + ); +} + +async function derivationPassword(passwordADeriver, saltDerivation) { + let keyMaterial = await getKeyParsecMaterialParsec(passwordADeriver); + let key = await getKeyParsec(keyMaterial, saltDerivation); + return key; +} + +async function exportCryptoKey(key) { + const exported = await window.crypto.subtle.exportKey( + "raw", key + ); + const exportedKeyBuffer = new Uint8Array(exported); + return exportedKeyBuffer; +} + +async function deriverPasswordParsec(passwordADeriver) { + let keyDerive = await derivationPassword(passwordADeriver, saltDerive2); + keyDerive = await exportCryptoKey(keyDerive); + return keyDerive; +} + +function genererParsecKey() { + let parsec_key = window.btoa(window.crypto.getRandomValues(new Uint8Array(8))); + return parsec_key; +} + +function importParsecDerivationKey(derivation) { + return window.crypto.subtle.importKey( + "raw", + derivation, + "AES-GCM", + true, + ["encrypt", "decrypt"] + ); +} + +function getMessageEncodingParsec(message) { + let enc = new TextEncoder(); + return enc.encode(message); +} + +async function cryptageMessageParsec(keyDerive, message) { + let iv = window.crypto.getRandomValues(new Uint8Array(12)); + let messageEncapsule = getMessageEncodingParsec(message); + let ciphertext = await window.crypto.subtle.encrypt( + { + name: "AES-GCM", + iv: iv + }, + keyDerive, + messageEncapsule + ); + let buffer = new Uint8Array(ciphertext); + return iv.toString()+"/"+buffer.toString(); +} + +async function crypterParsecKey(parsec_key, passwordADeriver) { + try{ + let keyDerive = await deriverPasswordParsec(passwordADeriver); + keyDerive = keyDerive.buffer; + keyDerive = await importParsecDerivationKey(keyDerive); + const parsecKeyChiffre = await cryptageMessageParsec(keyDerive, parsec_key); + return window.btoa(parsecKeyChiffre) + } + catch(error){ + if(error?.code){ + return Promise.reject(error) + } + return Promise.reject({code: 'crypt-error'}) + } +} + +async function generateKey() { + const password = document.getElementById("generate-password").value; + const parsecKey = genererParsecKey(); + const encryptedKey = await crypterParsecKey(parsecKey, password); + document.getElementById("generate-key-result").innerHTML = ` + parsecKey : ${parsecKey} + encryptedKey : ${encryptedKey} + `; +} + +async function decryptageMessageParsec(keyDerive, message) { + message = message.split("/"); + let iv = new Uint8Array(message[0].split(",")); + let parsec_key_crypte = message[1]; + let ciphertext = new Uint8Array(parsec_key_crypte.split(",")).buffer; + let decrypted = await window.crypto.subtle.decrypt( + { + name: "AES-GCM", + iv: iv + }, + keyDerive, + ciphertext + ) + let dec = new TextDecoder(); + return dec.decode(decrypted); +} + +async function decryptParsecKey() { + const password = document.getElementById("generate-password").value; + let encryptedKey = "=="; + let keyDerive = await deriverPasswordParsec(password); + keyDerive = keyDerive.buffer; + keyDerive = await importParsecDerivationKey(keyDerive); + encryptedKey = window.atob(encryptedKey); + let parsecKey = await decryptageMessageParsec(keyDerive, encryptedKey); + return parsecKey; +} + +// WORKSPACES +let workspaces = []; + +function updateWorkspacesSelect() { + const elms = document.getElementsByClassName("workspaces-select"); + for (const elm of elms) { + elm.innerHTML = ""; + elm.innerHTML += ``; + elm.innerHTML += ``; + for (const workspace of workspaces) { + elm.innerHTML += ``; + } + } +} + +function createWorkspace() { + const name = document.getElementById("workspace-name").value; + const http = new XMLHttpRequest(); + http.open("POST", "http://localhost:5775/workspaces"); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + name + })); + http.onreadystatechange = (e) => { + document.getElementById("workspace-create-result").innerHTML = getHttpResult(http); + if (http.status === 201) { + listWorkspaces(); + } + } +} + +function listWorkspaces() { + const http = new XMLHttpRequest(); + http.open("GET", "http://localhost:5775/workspaces"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("workspaces-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + workspaces = JSON.parse(http.response).workspaces; + updateWorkspacesSelect(); + } + } +} + +function renameWorkspace() { + const workspaceID = document.getElementById("workspace-rename-id").value; + const oldName = document.getElementById("workspace-rename-old-name").value; + const newName = document.getElementById("workspace-rename-new-name").value; + const http = new XMLHttpRequest(); + http.open("PATCH", `http://localhost:5775/workspaces/${workspaceID}`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + old_name: oldName, + new_name: newName + })); + http.onreadystatechange = (e) => { + document.getElementById("workspace-rename-result").innerHTML = getHttpResult(http); + } +} + +// OFFLINE AVAILABILITY +function checkOffline() { + const workspace = document.getElementById("offine-workspace").value; + http.open("GET", `http://localhost:5775/workspaces/${workspace}/get_offline_availability_status`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("offine-get-result").innerHTML = getHttpResult(http); + } +} + +// MOUNT/UNMOUNT +function mountWorkspace() { + // TODO timestamped workspace json={"timestamp": timestamp} + const workspace = document.getElementById("mount-workspace").value; + http.open("POST", `http://localhost:5775/workspaces/${workspace}/mount`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("mount-workspace-result").innerHTML = getHttpResult(http); + } +} + +function unmountWorkspace() { + // TODO timestamped workspace json={"timestamp": timestamp} + const workspace = document.getElementById("unmount-workspace").value; + http.open("POST", `http://localhost:5775/workspaces/${workspace}/unmount`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("unmount-workspace-result").innerHTML = getHttpResult(http); + } +} + +function listMountpoints() { + // TODO timestamped workspace json={"timestamp": timestamp} + http.open("GET", `http://localhost:5775/workspaces/mountpoints`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("unmount-workspace-result").innerHTML = getHttpResult(http); + } +} + +// FOLDER +let folders = [] +let workspaceID = null; + +function updateFoldersSelect() { + const elms = document.getElementsByClassName("folders-select"); + for (const elm of elms) { + elm.innerHTML = ""; + elm.innerHTML += ``; + elm.innerHTML += ``; + for (const folder of folders) { + elm.innerHTML += ``; + } + } +} + +function createFolder() { + const workspace = document.getElementById("workspace-folders-list-id").value; + const parent = document.getElementById("folder-parent").value; + const name = document.getElementById("folder-name").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/folders`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + name, + parent + })); + http.onreadystatechange = (e) => { + document.getElementById("folder-create-result").innerHTML = getHttpResult(http); + if (http.status === 201) { + listFolders(); + } + } +} + +function parseFolderNode(node, path = "") { + let result = []; + let fullPath = path + node.name; + fullPath = fullPath.endsWith("/") ? fullPath : fullPath + "/"; + result.push({ id: node.id, name: fullPath }); + if (node.children) { + for (const [key, value] of Object.entries(node.children)) { + result = result.concat(parseFolderNode(value, fullPath)); + } + } + return result; +} + +function listFolders(fromFile = false) { + let workspace = document.getElementById("workspace-folders-list-id").value; + if (fromFile) { + workspace = document.getElementById("workspace-files-list-id").value; + } + const http = new XMLHttpRequest(); + http.open("GET", `http://localhost:5775/workspaces/${workspace}/folders`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("folders-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + const root = JSON.parse(http.response); + folders = parseFolderNode(root); + updateFoldersSelect(); + } + } +} + +function renameFolder() { + const workspace = document.getElementById("workspace-folders-list-id").value; + const id = document.getElementById("rename-folder-id").value; + const newName = document.getElementById("rename-folder-name").value; + const newParent = document.getElementById("rename-folder-parent").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/folders/rename`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + id, + new_name: newName, + new_parent: newParent + })); + http.onreadystatechange = (e) => { + document.getElementById("rename-folder-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + listFolders(); + } + } +} + +function deleteFolder() { + const workspace = document.getElementById("workspace-folders-list-id").value; + const folder = document.getElementById("delete-folder-input").value; + const http = new XMLHttpRequest(); + http.open("DELETE", `http://localhost:5775/workspaces/${workspace}/folders/${folder}`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("delete-folder-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + listFolders(); + } + } +} + +// FILE +let files = []; +let smallFileContent = ""; +const smallFileSize = 1024; +const largeFileSize = 2**20 * 20 // 20mB + +function loadSmallFile(e) { + const file = e.target.files[0]; + if (!file) { + return; + } + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function(e) { + smallFileContent = e.target.result.split(",")[1]; + } +} + +function loadLargeFile(e) { + console.log(e); +} + +document.getElementById('small-file').addEventListener('change', loadSmallFile, false); +document.getElementById('large-file').addEventListener('change', loadLargeFile, false); + +function updateFilesSelect() { + const elms = document.getElementsByClassName("files-select"); + for (const elm of elms) { + elm.innerHTML = ""; + elm.innerHTML += ``; + elm.innerHTML += ``; + for (const file of files) { + elm.innerHTML += ``; + } + } +} + +function createSmallFile() { + const workspace = document.getElementById("workspace-files-list-id").value; + const parent = document.getElementById("folder-file-list-id").value; + const name = document.getElementById("small-filename").value; + // const fileContent = random.randbytes(smallFileSize); + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/files`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ + name, + parent, + content: smallFileContent + })); + http.onreadystatechange = (e) => { + document.getElementById("create-small-file-result").innerHTML = getHttpResult(http); + } +} + +function createLargeFile() { + const workspace = document.getElementById("workspace-files-list-id").value; + const parent = document.getElementById("folder-file-list-id").value; + const name = document.getElementById("large-filename").value; + // const fileContent = random.randbytes(largeFileSize); + const formData = new FormData(); + formData.append("data", JSON.stringify({parent})); + formData.append("files"); + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/files`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + name, + parent, + content: "" + })); + http.onreadystatechange = (e) => { + document.getElementById("create-large-file-result").innerHTML = getHttpResult(http); + } +} + +function listFiles() { + const workspace = document.getElementById("workspace-files-list-id").value; + const folder = document.getElementById("folder-file-list-id").value; + const http = new XMLHttpRequest(); + http.open("GET", `http://localhost:5775/workspaces/${workspace}/files/${folder}`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + + http.onreadystatechange = (e) => { + document.getElementById("files-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + files = JSON.parse(http.responseText).files; + updateFilesSelect(); + } + } +} + +function openFile() { + const workspace = document.getElementById("workspace-files-list-id").value; + const fileID = document.getElementById("open-file-id").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/open/${fileID}`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + + http.onreadystatechange = (e) => { + document.getElementById("open-file-result").innerHTML = getHttpResult(http); + } +} + +function renameFile() { + const workspace = document.getElementById("workspace-files-list-id").value; + const id = document.getElementById("file-id-input").value; + const newName = document.getElementById("file-id-input").value; + const newParent = document.getElementById("file-id-input").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/workspaces/${workspace}/files/rename`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + id, + new_name: newName, + new_parent: newParent + })); + http.onreadystatechange = (e) => { + document.getElementById("rename-file-result").innerHTML = getHttpResult(http); + } +} + +function deleteFiles() { + const workspace = document.getElementById("workspace-input").value; + for (const fileId of files) { + const http = new XMLHttpRequest(); + http.open("DELETE", `http://localhost:5775/workspaces/${workspace}/files/${fileId}`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("delete-result").innerHTML = getHttpResult(http); + } + } +} + +// INVITATION +const inviteeEmail = "eli.vance@blackmesa.nm"; + +function updateInvitationsSelect(usersInvitations, deviceInvitation) { + const elms = document.getElementsByClassName("invitations-select"); + for (const elm of elms) { + elm.innerHTML = ""; + elm.innerHTML += ``; + elm.innerHTML += ``; + for (const invitation of usersInvitations) { + elm.innerHTML += ``; + } + if (deviceInvitation) { + elm.innerHTML += ``; + } + } +} + +function createInvitation() { + const email = document.getElementById("claimer-email").value; + const http = new XMLHttpRequest(); + http.open("POST", "http://localhost:5775/invitations"); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + type: "user", + claimer_email: email + })); + http.onreadystatechange = (e) => { + document.getElementById("create-invitation-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + listInvitations(); + } + } +} + +function createInvitationDevice() { + const http = new XMLHttpRequest(); + http.open("POST", "http://localhost:5775/invitations"); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + type: "device" + })); + http.onreadystatechange = (e) => { + document.getElementById("create-invitation-device-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + listInvitations(); + } + } +} + +function listInvitations() { + const http = new XMLHttpRequest(); + http.open("GET", "http://localhost:5775/invitations"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("invitations-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + const usersInvitations = JSON.parse(http.response).users; + const deviceInvitation = JSON.parse(http.response).device; + updateInvitationsSelect(usersInvitations, deviceInvitation); + } + } +} + +function deleteInvitation() { + const invitationToken = document.getElementById("delete-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("DELETE", `http://localhost:5775/invitations/${invitationToken}`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("delete-invitation-result").innerHTML = getHttpResult(http); + if (http.status === 204) { + listInvitations(); + } + } +} + +function claimerRetreiveInfo() { + const invitationToken = document.getElementById("claimer-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/claimer/0-retreive-info`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("claimer-retreive-info-result").innerHTML = getHttpResult(http); + } +} + +function claimerWaitPeerReady() { + const invitationToken = document.getElementById("claimer-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/claimer/1-wait-peer-ready`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("claimer-wait-peer-ready-result").innerHTML = getHttpResult(http); + } +} + +function claimerCheckTrust() { + const invitationToken = document.getElementById("claimer-invitation-token").value; + const greeterSAS = document.getElementById("greeter-sas").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/claimer/2-check-trust`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ + greeter_sas: greeterSAS + })); + http.onreadystatechange = (e) => { + document.getElementById("claimer-check-trust-result").innerHTML = getHttpResult(http); + } +} + +function claimerWaitPeerTrust() { + const invitationToken = document.getElementById("claimer-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/claimer/3-wait-peer-trust`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("claimer-wait-peer-trust-result").innerHTML = getHttpResult(http); + } +} + +function claimerFinalize() { + const invitationToken = document.getElementById("claimer-invitation-token").value; + const key = document.getElementById("claimer-key").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/claimer/4-finalize`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ key })); + http.onreadystatechange = (e) => { + document.getElementById("claimer-finalize-result").innerHTML = getHttpResult(http); + } +} + +function greeterWaitPeerReady() { + const invitationToken = document.getElementById("greeter-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/greeter/1-wait-peer-ready`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("greeter-wait-peer-ready-result").innerHTML = getHttpResult(http); + } +} + +function greeterCheckTrust() { + const invitationToken = document.getElementById("greeter-invitation-token").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/greeter/2-wait-peer-trust`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("greeter-check-trust-result").innerHTML = getHttpResult(http); + } +} + +function greeterWaitPeerTrust() { + const invitationToken = document.getElementById("greeter-invitation-token").value; + const claimerSAS = document.getElementById("claimer-sas").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/greeter/3-check-trust`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + claimer_sas: claimerSAS + })); + http.onreadystatechange = (e) => { + document.getElementById("greeter-wait-peer-trust-result").innerHTML = getHttpResult(http); + } +} + +function greeterFinalize() { + const invitationToken = document.getElementById("greeter-invitation-token").value; + const grantedProfile = document.getElementById("granted-profile").value || null; + const email = document.getElementById("greeter-claimer-email").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/invitations/${invitationToken}/greeter/4-finalize`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + claimer_email: email, + granted_profile: grantedProfile + })); + http.onreadystatechange = (e) => { + document.getElementById("greeter-finalize-result").innerHTML = getHttpResult(http); + } +} + +// HUMANS + +function updateHumansSelect(humans) { + const elms = document.getElementsByClassName("humans-select"); + for (const elm of elms) { + elm.innerHTML = ""; + elm.innerHTML += ``; + elm.innerHTML += ``; + for (const human of humans) { + elm.innerHTML += ``; + } + } +} + +function getHumans() { + const http = new XMLHttpRequest(); + http.open("GET", "http://localhost:5775/humans"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("humans-result").innerHTML = getHttpResult(http); + if (http.status === 200) { + updateHumansSelect(JSON.parse(http.response).users); + } + } +} + +function revoke() { + const email = document.getElementById("revoke-email").value; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/humans/${email}/revoke`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("revoke-result").innerHTML = getHttpResult(http); + } +} + +// SHARE +function share() { + const workspace = document.getElementById("share-workspace").value; + const email = document.getElementById("share-email").value; + const role = document.getElementById("share-role").value || null; + const http = new XMLHttpRequest(); + http.open("PATCH", `http://localhost:5775/workspaces/${workspace}/share`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + email, + role + })); + http.onreadystatechange = (e) => { + document.getElementById("share-result").innerHTML = getHttpResult(http); + } +} + +function listShares() { + const workspace = document.getElementById("share-workspace").value; + const http = new XMLHttpRequest(); + http.open("GET", `http://localhost:5775/workspaces/${workspace}/share`); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(); + http.onreadystatechange = (e) => { + document.getElementById("shares-result").innerHTML = getHttpResult(http); + } +} + +// RECOVERY +function exportRecovery() { + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/recovery/export`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("export-result").innerHTML = getHttpResult(http); + } +} + +function importRecovery() { + const workspace = document.getElementById("workspace").value; + const recoveryDeviceFileContent = ""; + const recoveryDevicePassphrase = ""; + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/recovery/import`); + http.setRequestHeader("Content-type", "application/json"); + http.send(JSON.stringify({ + recovery_device_file_content: recoveryDeviceFileContent, + recovery_device_passphrase: recoveryDevicePassphrase + })); + http.onreadystatechange = (e) => { + document.getElementById("import-result").innerHTML = getHttpResult(http); + } +} + + +// SHAMIR +function shamirSetup() { + const http = new XMLHttpRequest(); + http.open("POST", `http://localhost:5775/recovery/shamir/setup`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({ + threshold: 0, + recipients: [{ + email: "", + weight: 1 + }] + })); + http.onreadystatechange = (e) => { + document.getElementById("shamir-setup-result").innerHTML = getHttpResult(http); + } +} + +function shamirDelete() { + const http = new XMLHttpRequest(); + http.open("DELETE", `http://localhost:5775/recovery/shamir/setup`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("shamir-delete-result").innerHTML = getHttpResult(http); + } +} + +function shamirGetCurrent() { + const http = new XMLHttpRequest(); + http.open("GET", `http://localhost:5775/recovery/shamir/setup`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("shamir-get-current-result").innerHTML = getHttpResult(http); + } +} + +function shamirGetOthers() { + const http = new XMLHttpRequest(); + http.open("GET", `http://localhost:5775/recovery/shamir/setup/others`); + http.setRequestHeader("Content-type", "application/json"); + http.setRequestHeader("Authorization", `bearer ${tokenSession}`); + http.send(JSON.stringify({})); + http.onreadystatechange = (e) => { + document.getElementById("shamir-get-others-result").innerHTML = getHttpResult(http); + } +} + + +// RESULT + +(function() {})(); diff --git a/misc/test_book/workspace.js b/misc/test_book/workspace.js new file mode 100644 index 000000000..e69de29bb