From 0c59007fa82443955f7630a433298004a836320a Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:23 +0100 Subject: [PATCH 01/25] feat(lang): translated activity.json --- frontend/public/locales/fr/activity.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/activity.json diff --git a/frontend/public/locales/fr/activity.json b/frontend/public/locales/fr/activity.json new file mode 100644 index 000000000..4360d7d0c --- /dev/null +++ b/frontend/public/locales/fr/activity.json @@ -0,0 +1,11 @@ +{ + "title": "Journaux d'activité", + "subtitle": "Historique des événements pour ce projet Infisical.", + "event": { + "readSecrets": "Secrets Visualisés", + "updateSecrets": "Secrets Mis à jour", + "addSecrets": "Secrets Ajoutés", + "deleteSecrets": "Secrets Supprimés" + }, + "ip-address": "Adresse IP" +} From 295e93ac170bbd9135759259c50429cccda42a77 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:35 +0100 Subject: [PATCH 02/25] feat(lang): translated billing.json --- frontend/public/locales/fr/billing.json | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/public/locales/fr/billing.json diff --git a/frontend/public/locales/fr/billing.json b/frontend/public/locales/fr/billing.json new file mode 100644 index 000000000..1acb44339 --- /dev/null +++ b/frontend/public/locales/fr/billing.json @@ -0,0 +1,28 @@ +{ + "title": "Utilisation et Facturation", + "description": "Voir et gérer l'abonnement de votre organisation ici", + "subscription": "Abonnement", + "starter": { + "name": "Starter", + "price-explanation": "jusqu'à 5 membres de l'équipe", + "text": "Gérez n'importe quel projet jusqu'à 5 membres gratuitement!", + "subtext": "$5 par membre / mois par la suite." + }, + "professional": { + "name": "Professionnel", + "price-explanation": "/membre/mois", + "subtext": "Comprend des projets et des membres illimités.", + "text": "Suivez la gestion clé à mesure que vous grandissez." + }, + "enterprise": { + "name": "Entreprise", + "text": "Suivez la gestion clé à mesure que vous grandissez." + }, + "current-usage": "Utilisation actuelle", + "free": "Gratuit", + "downgrade": "Rétrograder", + "upgrade": "Améliorer", + "learn-more": "En savoir plus", + "custom-pricing": "Prix personnalisés", + "schedule-demo": "Planifier une démo" +} From de1b75d99efaaed8911066bf45400855bd0f8cbc Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:44 +0100 Subject: [PATCH 03/25] feat(lang): translated common.json --- frontend/public/locales/fr/common.json | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 frontend/public/locales/fr/common.json diff --git a/frontend/public/locales/fr/common.json b/frontend/public/locales/fr/common.json new file mode 100644 index 000000000..8552668aa --- /dev/null +++ b/frontend/public/locales/fr/common.json @@ -0,0 +1,34 @@ +{ + "head-title": "{{title}} | Infiscal", + "error_project-already-exists": "Un projet avec ce nom existe déjà.", + "no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ", + "email": "Email", + "password": "Mot de passe", + "first-name": "Prénom", + "last-name": "Nom", + "logout": "Déconnexion", + "validate-required": "Veuillez saisir votre {{name}}", + "maintenance-alert": "Nous rencontrons des difficultés techniques mineures. Nous travaillons sur leurs résolution dès maintenant. Revenez dans quelques minutes.", + "click-to-copy": "Cliquez pour copiez", + "project-id": "Identifiant du Projet", + "save-changes": "Sauvegarder les modifications", + "saved": "Enregistrée", + "drop-zone": "Glissez et déposez un fichier .env ou .yml ici.", + "drop-zone-keys": "Glissez et déposez un fichier .env ou .yml ici pour ajouter plus de clés.", + "role": "Rôle", + "role_admin": "administrateur", + "display-name": "Nom d'affichage", + "environment": "Environnement", + "expired-in": "Expire dans", + "language": "Langue", + "search": "Recherche...", + "note": "Note", + "view-more": "Voir plus", + "end-of-history": "Fin de l'historique", + "select-event": "Sélectionnez un événement", + "event": "Événement", + "user": "Utilisateur", + "source": "Source", + "time": "Heure", + "timestamp": "Horodatage" +} From 1a90f27d6a15ee83fd1ae872ff3bbd355a3ea38e Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:56 +0100 Subject: [PATCH 04/25] feat(lang): translated dashboard.json --- frontend/public/locales/fr/dashboard.json | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frontend/public/locales/fr/dashboard.json diff --git a/frontend/public/locales/fr/dashboard.json b/frontend/public/locales/fr/dashboard.json new file mode 100644 index 000000000..f90bf1fc2 --- /dev/null +++ b/frontend/public/locales/fr/dashboard.json @@ -0,0 +1,36 @@ +{ + "title": "Secrets", + "og-title": "Gérez vos fichiers .env rapidement", + "og-description": "Infisical une plate-forme simple et chiffré de bout en bout qui permet aux équipes de synchroniser et de gérer leurs fichiers .env.", + "search-keys": "Recherche les clefs...", + "add-key": "Ajouter une clef", + "personal": "Personnel", + "personal-description": "Les clés personnelles ne sont visibles que pour vous", + "shared": "Partagé", + "shared-description": "Les clés partagées sont visibles à toute votre équipe", + "make-shared": "Rendre Partagé", + "make-personal": "Rendre Personnel", + "add-secret": "Ajouter un nouveau secret", + "check-docs": { + "button": "Vérifier la documentation", + "title": "Bon travail!", + "line1": "Félicitations pour avoir ajouté plus de secrets.", + "line2": "Voici comment les connecter à votre base de code." + }, + "sidebar": { + "secret": "Secret", + "key": "Clef", + "value": "Valeur", + "override": "Remplacer la valeur avec une valeur personnelle", + "version-history": "Historique des versions", + "comments": "Commentaires & Notes", + "personal-explanation": "Ce secret est personnel. Il n'est partagé avec aucun de vos coéquipiers.", + "generate-random-hex": "Générer un Hex aléatoire", + "digits": "chiffres", + "delete-key-dialog": { + "title": "Supprimer la clef", + "confirm-delete-message": "Êtes-vous sûr de vouloir supprimer ce secret? Cela ne peut pas être annulé." + } + } + +} From f1c52fe332562c7b8a0efbe499673bd03c3ce614 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:45:07 +0100 Subject: [PATCH 05/25] feat(lang): translated integrations.json --- frontend/public/locales/fr/integrations.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 frontend/public/locales/fr/integrations.json diff --git a/frontend/public/locales/fr/integrations.json b/frontend/public/locales/fr/integrations.json new file mode 100644 index 000000000..887e79dfd --- /dev/null +++ b/frontend/public/locales/fr/integrations.json @@ -0,0 +1,16 @@ +{ + "title": "Intégrations de Projet", + "description": "Gérez vos intégrations d'Infisical avec des services tiers.", + "no-integrations1": "Vous n'avez pas encore d'intégration. Quand vous en aurez, elles apparaîtront ici.", + "no-integrations2": "Pour commencer, cliquez sur l'une des options ci-dessous. La configuration se fait en 5 clics.", + "available": "Intégrations de plate-forme et cloud", + "available-text1": "Cliquez sur l'intégration que vous souhaitez connecter. Cela permettra à vos variables d'environnement de circuler automatiquement dans les services tiers sélectionnés.", + "available-text2": "Remarque: Lors d'une intégration avec Heroku, pour des raisons de sécurité, il est impossible de maintenir le chiffrage de bout en bout. En théorie, cela permet à Infisical de déchiffrer les variables d'environnement. En pratique, nous pouvons vous assurer que cela ne sera jamais fait, et cela nous permet de protéger vos secrets des mauvais acteurs en ligne. Le service Infisical de base restera toujours chiffré de bout en bout. Pour toutes vos intérogations, contactez support@infisical.com.", + "cloud-integrations": "Intégrations Cloud", + "framework-integrations": "Intégrations Framework", + "click-to-start": "Cliquez sur une intégration pour commencer à synchroniser les secrets avec elle.", + "click-to-setup": "Cliquez sur un framework pour obtenir les instructions de configuration.", + "grant-access-to-secrets": "Accordez un accès Infisical à vos secrets", + "why-infisical-needs-access": "La plupart des intégrations cloud nécessitent qu'Infisical puisse déchiffrer vos secrets afin qu'ils puissent être transmis.", + "grant-access-button": "Autoriser l'accès" +} From 2f6702537642bf901b1983e0c704430a729d8e7a Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:45:18 +0100 Subject: [PATCH 06/25] feat(lang): translated login.json --- frontend/public/locales/fr/login.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 frontend/public/locales/fr/login.json diff --git a/frontend/public/locales/fr/login.json b/frontend/public/locales/fr/login.json new file mode 100644 index 000000000..1200d09b9 --- /dev/null +++ b/frontend/public/locales/fr/login.json @@ -0,0 +1,8 @@ +{ + "title": "Connexion", + "og-title": "Connectez-vous à Infisical", + "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout permettant aux équipes de synchroniser et de gérer leurs fichiers .env.", + "login": "Se connecter", + "need-account": "Besoin d'un compte Infisical?", + "create-account": "Créer un compte" +} From aaa771a7b77b591d41bb25715310cb63b434ec12 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:12 +0100 Subject: [PATCH 07/25] feat(lang): translated section-incident.json --- frontend/public/locales/fr/section-incident.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/section-incident.json diff --git a/frontend/public/locales/fr/section-incident.json b/frontend/public/locales/fr/section-incident.json new file mode 100644 index 000000000..95b77ea13 --- /dev/null +++ b/frontend/public/locales/fr/section-incident.json @@ -0,0 +1,11 @@ +{ + "incident-contacts": "Contacts Incidents", + "incident-contacts-description": "Ces contacts seront informés dans le cas improbable d'un incident grave.", + "no-incident-contacts": "Aucun contact incident trouvé.", + "add-contact": "Ajouter un contact", + "add-dialog": { + "title": "Ajouter un contact incident", + "description": "Ce contact sera informé dans le cas improbable d'un incident grave.", + "add-incident": "Ajouter un contact incident" + } +} From 1d0f51bb4229be34d338345cc00d59193daeb77f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:22 +0100 Subject: [PATCH 08/25] feat(lang): translated nav.json --- frontend/public/locales/fr/nav.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/public/locales/fr/nav.json diff --git a/frontend/public/locales/fr/nav.json b/frontend/public/locales/fr/nav.json new file mode 100644 index 000000000..1fda27af9 --- /dev/null +++ b/frontend/public/locales/fr/nav.json @@ -0,0 +1,22 @@ +{ + "support": { + "slack": "[NEW] Rejoignez le forum Slack", + "docs": "Lire les documentations", + "issue": "Ouvrir une issue Github", + "email": "Envoyez-nous un email" + }, + "user": { + "signed-in-as": "CONNECTÉ EN TANT QUE", + "current-organization": "ORGANISATION ACTUELLE", + "usage-billing": "Utilisation & Facturation", + "invite": "Inviter des membres", + "other-organizations": "AUTRE ORGANISATION" + }, + "menu": { + "project": "PROJET", + "secrets": "Secrets", + "members": "Membres", + "integrations": "Intégrations", + "project-settings": "Paramètres du Projet" + } +} From 1423d05b52b2b0ab11e3506e8c7c81c406237631 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:36 +0100 Subject: [PATCH 09/25] feat(lang): translated section-members.json --- frontend/public/locales/fr/section-members.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 frontend/public/locales/fr/section-members.json diff --git a/frontend/public/locales/fr/section-members.json b/frontend/public/locales/fr/section-members.json new file mode 100644 index 000000000..8faed80e7 --- /dev/null +++ b/frontend/public/locales/fr/section-members.json @@ -0,0 +1,14 @@ +{ + "add-member": "Ajouter un Membre", + "org-members": "Membres de l'organisation", + "org-members-description": "Gérer les membres de votre organisation. Ces utilisateurs pourraient ensuite être répartis en projets.", + "search-members": "Recherche des membres...", + "add-dialog": { + "add-member-to-project": "Ajoutez un membre à votre projet", + "already-all-invited": "Tous les utilisateurs de votre organisation sont déjà invités.", + "add-user-org-first": "Ajoutez d'abord plus d'utilisateurs à l'organisation.", + "user-will-email": "L'utilisateur recevra un email avec les instructions.", + "looking-add": "<0>Si vous cherchez à ajouter des utilisateurs à votre organisation,<1>cliquez ici", + "add-user-to-org": "Ajouter des Utilisateurs à l'Organisation" + } +} From 5bc8046f3f56c5d833aa1a9b661e3dc5aa599339 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:46 +0100 Subject: [PATCH 10/25] feat(lang): translated section-password.json --- frontend/public/locales/fr/section-password.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/section-password.json diff --git a/frontend/public/locales/fr/section-password.json b/frontend/public/locales/fr/section-password.json new file mode 100644 index 000000000..0928233e6 --- /dev/null +++ b/frontend/public/locales/fr/section-password.json @@ -0,0 +1,11 @@ +{ + "password": "Mot de passe", + "change": "Changer le mot de passe", + "current": "Mot de passe actuel", + "current-wrong": "Le mot de passe actuel peut être érroné", + "new": "Nouveau mot de passe", + "validate-base": "Le mot de passe doit contenir au moins:", + "validate-length": "14 caractères", + "validate-case": "1 caractère miniscule", + "validate-number": "1 chiffre" +} From ee7cf7920d46125694740a6d0c0e103a834bfe75 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:55 +0100 Subject: [PATCH 11/25] feat(lang): translated section-token.json --- frontend/public/locales/fr/section-token.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/public/locales/fr/section-token.json diff --git a/frontend/public/locales/fr/section-token.json b/frontend/public/locales/fr/section-token.json new file mode 100644 index 000000000..1f2a3b233 --- /dev/null +++ b/frontend/public/locales/fr/section-token.json @@ -0,0 +1,13 @@ +{ + "service-tokens": "Jetons de service", + "service-tokens-description": "Chaque jeton de service vous est spécifique, à un certain projet et à un certain environnement dans ce projet.", + "add-new": "Ajouter un nouveau jeton", + "add-dialog": { + "title": "Ajouter un jeton de service pour {{target}}", + "description": "Spécifiez le nom, l'environnement et la période d'expiration. Lorsqu'un jeton est généré, vous ne pourrez le voir qu'une seule fois avant qu'il ne disparaisse. Assurez-vous de le sauvegarder quelque part.", + "name": "Nom du jeton de service", + "add": "Ajouter un jeton de service", + "copy-service-token": "Copiez votre jeton de service", + "copy-service-token-description": "Une fois que vous aurez fermé cette fenêtre, vous ne reverrez plus jamais votre jeton de service" + } +} From 77b34467b9823ba99882bf2033282410d706278f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:47:19 +0100 Subject: [PATCH 12/25] feat(lang): translated settings-members.json --- frontend/public/locales/fr/settings-members.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/public/locales/fr/settings-members.json diff --git a/frontend/public/locales/fr/settings-members.json b/frontend/public/locales/fr/settings-members.json new file mode 100644 index 000000000..9f34c7a68 --- /dev/null +++ b/frontend/public/locales/fr/settings-members.json @@ -0,0 +1,4 @@ +{ + "title": "Membres du projet", + "description": "Cette page affiche les membres du projet sélectionné." +} From 1394368a4386354648ac9fd824bc77de99613da1 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:47:50 +0100 Subject: [PATCH 13/25] feat(lang): translated settings-org.json --- frontend/public/locales/fr/settings-org.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/public/locales/fr/settings-org.json diff --git a/frontend/public/locales/fr/settings-org.json b/frontend/public/locales/fr/settings-org.json new file mode 100644 index 000000000..952bd68c8 --- /dev/null +++ b/frontend/public/locales/fr/settings-org.json @@ -0,0 +1,4 @@ +{ + "title": "Paramètres d'Organisation", + "description": "Gérer les membres de votre organisation. Ces utilisateurs pourraient ensuite être répartis en projets." +} From ea9e185a650e3fd80f7d523a5ce92f332ea0604f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:00 +0100 Subject: [PATCH 14/25] feat(lang): translated settings-personal.json --- frontend/public/locales/fr/settings-personal.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/settings-personal.json diff --git a/frontend/public/locales/fr/settings-personal.json b/frontend/public/locales/fr/settings-personal.json new file mode 100644 index 000000000..30a3d6fa3 --- /dev/null +++ b/frontend/public/locales/fr/settings-personal.json @@ -0,0 +1,11 @@ +{ + "title": "Paramètres Personnels", + "description": "Consultez et gérez vos informations personnelles ici.", + "emergency": { + "name": "Kit d'urgence", + "text1": "Votre kit d'urgence contient les informations dont vous aurez besoin pour vous connecter à votre compte Infisical.", + "text2": "Seul le dernier kit d'urgence émis reste valide. Pour obtenir un nouveau kit d'urgence, vérifiez votre mot de passe.", + "download": "Télécharger le kit d'urgence" + }, + "change-language": "Changer de langue" +} From 878ca69f436e713a06327ee32ddfd88f7a6d5e41 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:08 +0100 Subject: [PATCH 15/25] feat(lang): translated settings-project.json --- frontend/public/locales/fr/settings-project.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/public/locales/fr/settings-project.json diff --git a/frontend/public/locales/fr/settings-project.json b/frontend/public/locales/fr/settings-project.json new file mode 100644 index 000000000..a5a3f1ee3 --- /dev/null +++ b/frontend/public/locales/fr/settings-project.json @@ -0,0 +1,13 @@ +{ + "title": "Paramètres du Projet", + "description": "Ces paramètres ne s'appliquent qu'au Projet actuellement sélectionné.", + "danger-zone": "Zone de danger", + "delete-project": "Supprimer le Projet", + "project-to-delete": "Projet à Supprimer", + "danger-zone-note": "Dès que vous supprimez ce projet, vous ne pourrez plus revenir en arrière. Cela supprimera immédiatement toutes les clefs. Si vous voulez toujours le faire, veuillez saisir le nom du projet ci-dessous.", + "delete-project-note": "Remarque: Vous ne pouvez supprimer qu'un projet que si vous en avez plus d'un.", + "project-id-description": "Pour intégrer Infisical dans votre base de code et obtenir une injection automatique de variables d'environnement, vous devez utiliser l'ID du projet suivant.", + "project-id-description2": "Pour plus de conseils, y compris des extraits de code pour diverses langues et frameworks, voir ", + "auto-generated": "Ceci est l'identifiant unique généré automatiquement pour votre projet. Il ne peut pas être modifié.", + "docs": "Documentation Infisical" +} From a686462392af399cfe5e5723f2e25073268994d1 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:20 +0100 Subject: [PATCH 16/25] feat(lang): translated signup.json --- frontend/public/locales/fr/signup.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/public/locales/fr/signup.json diff --git a/frontend/public/locales/fr/signup.json b/frontend/public/locales/fr/signup.json new file mode 100644 index 000000000..3c74476b3 --- /dev/null +++ b/frontend/public/locales/fr/signup.json @@ -0,0 +1,25 @@ +{ + "title": "S'inscrire", + "og-title": "Remplacez les fichiers .env par 1 ligne de code. Inscrivez-vous à Infisical en 3 minutes.", + "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout qui permet aux équipes de synchroniser et de gérer des clefs API et des variables d'environnement. Fonctionne avec Node.js, Next.js, Gatsby, Nest.js ...", + "signup": "S'inscrire", + "already-have-account": "Déjà inscris? Se connecter", + "forgot-password": "Mot de passe oublié?", + "verify": "Vérifier", + "step1-start": "Commençons", + "step1-privacy": "En créant votre compte, vous acceptez nos conditions et avez lu et reconnu notre politique de confidentialité.", + "step1-submit": "C'est parti", + "step2-message": "Nous avons envoyé un email de vérification à{{email}}", + "step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.", + "step2-spam-alert": "Assurez-vous de vérifier vos spam.", + "step3-message": "Nous y sommes presque!", + "step4-message": "Enregistrez votre kit d'urgence", + "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver'.", + "step4-description2": "Nous vous recommandons de le télécharger et de le garder en sécurité.", + "step4-description3": "Il contient votre clef secrète que nous ne pouvons pas récupérer pour vous si vous la perdez.", + "step4-download": "Téléchargez le PDF", + "step5-send-invites": "Envoyer des invitations", + "step5-invite-team": "Invitez votre équipe", + "step5-subtitle": "Infisical a pour but d'être utilisé avec vos coéquipiers. Invitez-les à le tester.", + "step5-skip": "Passer" +} From 7fe706ad0d5979ac89c8804cb93a29a19ca4b18c Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:50:28 +0100 Subject: [PATCH 17/25] fix(lang): configured fr locale --- frontend/next-i18next.config.js | 2 +- frontend/pages/login.tsx | 2 +- frontend/pages/settings/personal/[id].js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/next-i18next.config.js b/frontend/next-i18next.config.js index 5de9c97bc..3feba4318 100644 --- a/frontend/next-i18next.config.js +++ b/frontend/next-i18next.config.js @@ -8,7 +8,7 @@ module.exports = { debug: process.env.NODE_ENV === "development", i18n: { defaultLocale: "en", - locales: ["en", "ko"], + locales: ["en", "ko", "fr"], }, fallbackLng: { default: ["en"], diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index e72aa3b25..84d539034 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -160,7 +160,7 @@ export default function Login() { diff --git a/frontend/pages/settings/personal/[id].js b/frontend/pages/settings/personal/[id].js index 5eebb8328..a592bf45b 100644 --- a/frontend/pages/settings/personal/[id].js +++ b/frontend/pages/settings/personal/[id].js @@ -126,7 +126,7 @@ export default function PersonalSettings() { From 16d2746749bf8d9aa61b78280a12aae868e9ba1d Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:30:15 +0100 Subject: [PATCH 18/25] fix(lang): add langugageMap fr --- frontend/const.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/const.js b/frontend/const.js index 195d54a6d..0e5ed9516 100644 --- a/frontend/const.js +++ b/frontend/const.js @@ -20,4 +20,5 @@ export const publicPaths = [ export const languageMap = { en: "English", ko: "한국어", + fr: "Français", }; From d9b7f698383d14374a5f9157b8668087bc9f18b3 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:32:53 +0100 Subject: [PATCH 19/25] fix(lang): add remaining translation on login + signin --- frontend/components/signup/CodeInputStep.tsx | 6 +++--- frontend/components/signup/EnterEmailStep.tsx | 6 +++--- frontend/pages/login.tsx | 8 ++++++-- frontend/public/locales/en/login.json | 4 +++- frontend/public/locales/en/signup.json | 5 ++++- frontend/public/locales/fr/login.json | 4 +++- frontend/public/locales/fr/signup.json | 13 ++++++++----- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/frontend/components/signup/CodeInputStep.tsx b/frontend/components/signup/CodeInputStep.tsx index 05c4973a8..9cb1aa550 100644 --- a/frontend/components/signup/CodeInputStep.tsx +++ b/frontend/components/signup/CodeInputStep.tsx @@ -81,7 +81,7 @@ export default function CodeInputStep({ email, incrementStep, setCode, codeError return (

- {"We've"} sent a verification email to{" "} + {t("signup:step2-message")}

{email}{" "} @@ -119,11 +119,11 @@ export default function CodeInputStep({ email, incrementStep, setCode, codeError

- Not seeing an email? + {t("signup:step2-resend-alert")}
diff --git a/frontend/components/signup/EnterEmailStep.tsx b/frontend/components/signup/EnterEmailStep.tsx index 5f98710b7..85598a8a2 100644 --- a/frontend/components/signup/EnterEmailStep.tsx +++ b/frontend/components/signup/EnterEmailStep.tsx @@ -59,11 +59,11 @@ export default function EnterEmailStep({ email, setEmail, incrementStep }: Downl

- {'Let\''}s get started + {t("signup:step1-start")}

-
diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 84d539034..69126e6af 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -117,11 +117,15 @@ export default function Login() { id="current-password" />
- Forgot password? + + +
{!isLoading && errorLogin && ( - + )}
diff --git a/frontend/public/locales/en/login.json b/frontend/public/locales/en/login.json index ee097fd92..75b56a014 100644 --- a/frontend/public/locales/en/login.json +++ b/frontend/public/locales/en/login.json @@ -4,5 +4,7 @@ "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", "login": "Log In", "need-account": "Need an Infisical account?", - "create-account": "Create an account" + "create-account": "Create an account", + "forgot-password": "Forgot your password?", + "error-login": "Wrong credentials." } diff --git a/frontend/public/locales/en/signup.json b/frontend/public/locales/en/signup.json index 0cdbe310b..bee993727 100644 --- a/frontend/public/locales/en/signup.json +++ b/frontend/public/locales/en/signup.json @@ -9,8 +9,11 @@ "step1-start": "Let's get started", "step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.", "step1-submit": "Get Started", - "step2-message": "We've sent a verification email to{{email}}", + "step2-message": "We've sent a verification email to", "step2-code-error": "Oops. Your code is wrong. Please try again.", + "step2-resend-alert": "Don't see the email?", + "step2-resend-submit": "Resend", + "step2-resend-progress": "Resending...", "step2-spam-alert": "Make sure to check your spam inbox.", "step3-message": "Almost there!", "step4-message": "Save your Emergency Kit", diff --git a/frontend/public/locales/fr/login.json b/frontend/public/locales/fr/login.json index 1200d09b9..8c93da9fb 100644 --- a/frontend/public/locales/fr/login.json +++ b/frontend/public/locales/fr/login.json @@ -4,5 +4,7 @@ "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout permettant aux équipes de synchroniser et de gérer leurs fichiers .env.", "login": "Se connecter", "need-account": "Besoin d'un compte Infisical?", - "create-account": "Créer un compte" + "create-account": "Créer un compte", + "forgot-password": "Mot de passe oublié?", + "error-login": "Mauvais identifiants." } diff --git a/frontend/public/locales/fr/signup.json b/frontend/public/locales/fr/signup.json index 3c74476b3..48b36e203 100644 --- a/frontend/public/locales/fr/signup.json +++ b/frontend/public/locales/fr/signup.json @@ -6,19 +6,22 @@ "already-have-account": "Déjà inscris? Se connecter", "forgot-password": "Mot de passe oublié?", "verify": "Vérifier", - "step1-start": "Commençons", + "step1-start": "Bon, on commence!", "step1-privacy": "En créant votre compte, vous acceptez nos conditions et avez lu et reconnu notre politique de confidentialité.", "step1-submit": "C'est parti", - "step2-message": "Nous avons envoyé un email de vérification à{{email}}", + "step2-message": "Nous avons envoyé un email de vérification à", "step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.", - "step2-spam-alert": "Assurez-vous de vérifier vos spam.", + "step2-resend-alert": "Vous ne voyez pas l'email?", + "step2-resend-submit": "Renvoyer", + "step2-resend-progress": "Envoie en cours...", + "step2-spam-alert": "Assurez-vous de vérifier vos spams.", "step3-message": "Nous y sommes presque!", "step4-message": "Enregistrez votre kit d'urgence", - "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver'.", + "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver.", "step4-description2": "Nous vous recommandons de le télécharger et de le garder en sécurité.", "step4-description3": "Il contient votre clef secrète que nous ne pouvons pas récupérer pour vous si vous la perdez.", "step4-download": "Téléchargez le PDF", - "step5-send-invites": "Envoyer des invitations", + "step5-send-invites": "Envoyer les invitations", "step5-invite-team": "Invitez votre équipe", "step5-subtitle": "Infisical a pour but d'être utilisé avec vos coéquipiers. Invitez-les à le tester.", "step5-skip": "Passer" From 0b281a02d00dc5dc7eb3ab99b1d95ed0d3ea086d Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:54:16 +0100 Subject: [PATCH 20/25] fix(i18n): add default empty string --- frontend/components/signup/EnterEmailStep.tsx | 4 ++-- frontend/pages/login.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/components/signup/EnterEmailStep.tsx b/frontend/components/signup/EnterEmailStep.tsx index 85598a8a2..1b9177580 100644 --- a/frontend/components/signup/EnterEmailStep.tsx +++ b/frontend/components/signup/EnterEmailStep.tsx @@ -63,7 +63,7 @@ export default function EnterEmailStep({ email, setEmail, incrementStep }: Downl

-
diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 69126e6af..458271066 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -125,7 +125,7 @@ export default function Login() {
{!isLoading && errorLogin && ( - + )}
From 07c65ded40dad509d24b188882e313392b488b83 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Thu, 12 Jan 2023 01:05:13 -0800 Subject: [PATCH 21/25] Refactored the logic for frontend dashboard --- frontend/components/basic/Toggle.tsx | 42 +---- frontend/components/basic/buttons/Button.tsx | 3 +- .../dashboard/DashboardInputField.tsx | 6 +- .../dashboard/DownloadSecretsMenu.tsx | 11 +- frontend/components/dashboard/KeyPair.tsx | 23 +-- frontend/components/dashboard/SideBar.tsx | 53 ++---- .../utilities/secrets/checkOverrides.ts | 15 +- .../utilities/secrets/downloadDotEnv.ts | 11 +- .../utilities/secrets/downloadYaml.ts | 11 +- .../utilities/secrets/encryptSecrets.ts | 13 +- .../utilities/secrets/getSecretsForProject.ts | 18 +- frontend/ee/components/PITRecoverySidebar.tsx | 61 +++++-- frontend/ee/components/SecretVersionList.tsx | 47 ++--- frontend/pages/dashboard/[id].tsx | 164 +++++++++--------- frontend/public/data/frequentInterfaces.ts | 8 + 15 files changed, 219 insertions(+), 267 deletions(-) create mode 100644 frontend/public/data/frequentInterfaces.ts diff --git a/frontend/components/basic/Toggle.tsx b/frontend/components/basic/Toggle.tsx index c9957cc3c..1a5ddaa82 100644 --- a/frontend/components/basic/Toggle.tsx +++ b/frontend/components/basic/Toggle.tsx @@ -1,27 +1,11 @@ import React from "react"; import { Switch } from "@headlessui/react"; - -interface OverrideProps { - id: string; - keyName: string; - value: string; - pos: number; - comment: string; -} - interface ToggleProps { enabled: boolean; setEnabled: (value: boolean) => void; - addOverride: (value: OverrideProps) => void; - keyName: string; - value: string; + addOverride: (value: string | undefined, pos: number) => void; pos: number; - id: string; - comment: string; - deleteOverride: (id: string) => void; - sharedToHide: string[]; - setSharedToHide: (values: string[]) => void; } /** @@ -30,41 +14,23 @@ interface ToggleProps { * @param {boolean} obj.enabled - whether the toggle is turned on or off * @param {function} obj.setEnabled - change the state of the toggle * @param {function} obj.addOverride - a function that adds an override to a certain secret - * @param {string} obj.keyName - key of a certain secret - * @param {string} obj.value - value of a certain secret * @param {number} obj.pos - position of a certain secret - #TODO: make the secret id persistent? - * @param {string} obj.id - id of a certain secret (NOTE: THIS IS THE ID OF THE MAIN SECRET - NOT OF AN OVERRIDE) - * @param {function} obj.deleteOverride - a function that deleted an override for a certain secret - * @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden. - * @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually * @returns */ export default function Toggle ({ enabled, setEnabled, addOverride, - keyName, - value, - pos, - id, - comment, - deleteOverride, - sharedToHide, - setSharedToHide + pos }: ToggleProps): JSX.Element { return ( { if (enabled == false) { - addOverride({ id, keyName, value, pos, comment }); - setSharedToHide([ - ...sharedToHide!, - id - ]) + addOverride('', pos); } else { - deleteOverride(id); + addOverride(undefined, pos); } setEnabled(!enabled); }} diff --git a/frontend/components/basic/buttons/Button.tsx b/frontend/components/basic/buttons/Button.tsx index 939d9b17d..2405abce7 100644 --- a/frontend/components/basic/buttons/Button.tsx +++ b/frontend/components/basic/buttons/Button.tsx @@ -3,7 +3,6 @@ import Image from "next/image"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon, - FontAwesomeIconProps, } from "@fortawesome/react-fontawesome"; const classNames = require("classnames"); @@ -101,7 +100,7 @@ export default function Button(props: ButtonProps): JSX.Element {
void; - value: string; + value: string | undefined; type: 'varName' | 'value'; blurred?: boolean; isDuplicate?: boolean; @@ -47,7 +47,7 @@ const DashboardInputField = ({ }; if (type === 'varName') { - const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != ''; + const startsWithNumber = !isNaN(Number(value?.charAt(0))) && value != ''; const error = startsWithNumber || isDuplicate; return ( @@ -141,7 +141,7 @@ const DashboardInputField = ({ {blurred && (
- {value.split('').map(() => ( + {value?.split('').map(() => ( void; modifyValue: (value: string, position: number) => void; + modifyValueOverride: (value: string, position: number) => void; isBlurred: boolean; isDuplicate: boolean; toggleSidebar: (id: string) => void; @@ -30,6 +23,7 @@ interface KeyPairProps { * @param {String[]} obj.keyPair - data related to the environment variable (id, pos, key, value, public/private) * @param {function} obj.modifyKey - modify the key of a certain environment variable * @param {function} obj.modifyValue - modify the value of a certain environment variable + * @param {function} obj.modifyValueOverride - modify the value of a certain environment variable if it is overriden * @param {boolean} obj.isBlurred - if the blurring setting is turned on * @param {boolean} obj.isDuplicate - list of all the duplicates secret names on the dashboard * @param {function} obj.toggleSidebar - open/close/switch sidebar @@ -41,6 +35,7 @@ const KeyPair = ({ keyPair, modifyKey, modifyValue, + modifyValueOverride, isBlurred, isDuplicate, toggleSidebar, @@ -50,7 +45,7 @@ const KeyPair = ({ return (
- {keyPair.type == "personal" &&
+ {keyPair.valueOverride &&
This secret is overriden @@ -70,12 +65,12 @@ const KeyPair = ({
diff --git a/frontend/components/dashboard/SideBar.tsx b/frontend/components/dashboard/SideBar.tsx index 33b936f52..32cdf7341 100644 --- a/frontend/components/dashboard/SideBar.tsx +++ b/frontend/components/dashboard/SideBar.tsx @@ -16,18 +16,15 @@ import GenerateSecretMenu from './GenerateSecretMenu'; interface SecretProps { key: string; value: string; + valueOverride: string | undefined; pos: number; - type: string; id: string; comment: string; } interface OverrideProps { id: string; - keyName: string; - value: string; - pos: number; - comment: string; + valueOverride: string; } export interface DeleteRowFunctionProps { ids: string[]; @@ -39,9 +36,8 @@ interface SideBarProps { data: SecretProps[]; modifyKey: (value: string, position: number) => void; modifyValue: (value: string, position: number) => void; + modifyValueOverride: (value: string | undefined, position: number) => void; modifyComment: (value: string, position: number) => void; - addOverride: (value: OverrideProps) => void; - deleteOverride: (id: string) => void; buttonReady: boolean; savePush: () => void; sharedToHide: string[]; @@ -55,12 +51,9 @@ interface SideBarProps { * @param {SecretProps[]} obj.data - data of a certain key valeu pair * @param {function} obj.modifyKey - function that modifies the secret key * @param {function} obj.modifyValue - function that modifies the secret value - * @param {function} obj.addOverride - override a certain secret - * @param {function} obj.deleteOverride - delete the personal override for a certain secret + * @param {function} obj.modifyValueOverride - function that modifies the secret value if it is an override * @param {boolean} obj.buttonReady - is the button for saving chagnes active * @param {function} obj.savePush - save changes andp ush secrets - * @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden. - * @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually * @param {function} obj.deleteRow - a function to delete a certain keyPair * @returns the sidebar with 'secret's settings' */ @@ -69,17 +62,14 @@ const SideBar = ({ data, modifyKey, modifyValue, + modifyValueOverride, modifyComment, - addOverride, - deleteOverride, buttonReady, savePush, - sharedToHide, - setSharedToHide, deleteRow }: SideBarProps) => { const [isLoading, setIsLoading] = useState(false); - const [overrideEnabled, setOverrideEnabled] = useState(data.map(secret => secret.type).includes("personal")); + const [overrideEnabled, setOverrideEnabled] = useState(data[0].valueOverride != undefined); const { t } = useTranslation(); return
@@ -111,19 +101,19 @@ const SideBar = ({ blurred={false} />
- {data.filter(secret => secret.type == "shared")[0]?.value + {data[0]?.value ?

{t("dashboard:sidebar.value")}

secret.type == "shared")[0]?.pos} - value={data.filter(secret => secret.type == "shared")[0]?.value} + position={data[0].pos} + value={data[0]?.value} isDuplicate={false} blurred={true} />
- secret.type == "shared")[0]?.pos} /> +
:
@@ -131,39 +121,32 @@ const SideBar = ({ {t("dashboard:sidebar.personal-explanation")}
}
- {data.filter(secret => secret.type == "shared")[0]?.value && + {data[0]?.value &&

{t("dashboard:sidebar.override")}

}
secret.type == "personal")[0]?.pos : data[0]?.pos} - value={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.value : data[0]?.value} + position={data[0]?.pos} + value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value} isDuplicate={false} blurred={true} />
- secret.type == "personal")[0]?.pos : data[0]?.pos} /> +
- secret.type == "shared")[0]?.comment} modifyComment={modifyComment} position={data.filter(secret => secret.type == "shared")[0]?.pos} /> +
)}
@@ -176,7 +159,7 @@ const SideBar = ({ textDisabled="Saved" /> deleteRow({ ids: overrideEnabled ? data.map(secret => secret.id) : [data.filter(secret => secret.type == "shared")[0]?.id], secretName: data[0]?.key })} + onSubmit={() => deleteRow({ ids: data.map(secret => secret.id), secretName: data[0]?.key })} />
diff --git a/frontend/components/utilities/secrets/checkOverrides.ts b/frontend/components/utilities/secrets/checkOverrides.ts index fae465d88..83a779a85 100644 --- a/frontend/components/utilities/secrets/checkOverrides.ts +++ b/frontend/components/utilities/secrets/checkOverrides.ts @@ -1,11 +1,4 @@ -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} +import { SecretDataProps } from "public/data/frequentInterfaces"; /** * This function downloads the secrets as a .env file @@ -16,16 +9,16 @@ interface SecretDataProps { const checkOverrides = async ({ data }: { data: SecretDataProps[]; }) => { let secrets : SecretDataProps[] = data!.map((secret) => Object.create(secret)); const overridenSecrets = data!.filter( - (secret) => secret.type === 'personal' + (secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal' ); if (overridenSecrets.length) { overridenSecrets.forEach((secret) => { const index = secrets!.findIndex( - (_secret) => _secret.key === secret.key && _secret.type === 'shared' + (_secret) => _secret.key === secret.key && (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ); secrets![index].value = secret.value; }); - secrets = secrets!.filter((secret) => secret.type === 'shared'); + secrets = secrets!.filter((secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride)); } return secrets; } diff --git a/frontend/components/utilities/secrets/downloadDotEnv.ts b/frontend/components/utilities/secrets/downloadDotEnv.ts index dbb29497b..93ac774ba 100644 --- a/frontend/components/utilities/secrets/downloadDotEnv.ts +++ b/frontend/components/utilities/secrets/downloadDotEnv.ts @@ -1,16 +1,9 @@ +import { SecretDataProps } from "public/data/frequentInterfaces"; + import { envMapping } from "../../../public/data/frequentConstants"; import checkOverrides from './checkOverrides'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - /** * This function downloads the secrets as a .env file * @param {object} obj diff --git a/frontend/components/utilities/secrets/downloadYaml.ts b/frontend/components/utilities/secrets/downloadYaml.ts index 1c17b1410..26fca5f00 100644 --- a/frontend/components/utilities/secrets/downloadYaml.ts +++ b/frontend/components/utilities/secrets/downloadYaml.ts @@ -1,19 +1,12 @@ // import YAML from 'yaml'; // import { YAMLSeq } from 'yaml/types'; +import { SecretDataProps } from "public/data/frequentInterfaces"; + // import { envMapping } from "../../../public/data/frequentConstants"; // import checkOverrides from './checkOverrides'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - /** * This function downloads the secrets as a .yml file * @param {object} obj diff --git a/frontend/components/utilities/secrets/encryptSecrets.ts b/frontend/components/utilities/secrets/encryptSecrets.ts index 46c3150ef..a0f56990a 100644 --- a/frontend/components/utilities/secrets/encryptSecrets.ts +++ b/frontend/components/utilities/secrets/encryptSecrets.ts @@ -1,3 +1,5 @@ +import { SecretDataProps } from "public/data/frequentInterfaces"; + import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; const crypto = require("crypto"); @@ -9,15 +11,6 @@ const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - interface EncryptedSecretProps { id: string; createdAt: string; @@ -106,7 +99,7 @@ const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsT secretCommentCiphertext, secretCommentIV, secretCommentTag, - type: secret.type, + type: (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal', }; return result; diff --git a/frontend/components/utilities/secrets/getSecretsForProject.ts b/frontend/components/utilities/secrets/getSecretsForProject.ts index 81de27be0..d2c4ddf54 100644 --- a/frontend/components/utilities/secrets/getSecretsForProject.ts +++ b/frontend/components/utilities/secrets/getSecretsForProject.ts @@ -117,15 +117,19 @@ const getSecretsForProject = async ({ }); } - const result = tempDecryptedSecrets.map((secret, index) => { + const secretKeys = [...new Set(tempDecryptedSecrets.map(secret => secret.key))]; + + + const result = secretKeys.map((key, index) => { return { - id: secret['id'], + id: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.id, + idOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.id, pos: index, - key: secret['key'], - value: secret['value'], - type: secret['type'], - comment: secret['comment'] - }; + key: key, + value: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.value, + valueOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.value, + comment: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.comment, + } }); setData(result); diff --git a/frontend/ee/components/PITRecoverySidebar.tsx b/frontend/ee/components/PITRecoverySidebar.tsx index 085c483ed..e8a1041cd 100644 --- a/frontend/ee/components/PITRecoverySidebar.tsx +++ b/frontend/ee/components/PITRecoverySidebar.tsx @@ -13,6 +13,15 @@ import { decryptAssymmetric, decryptSymmetric } from "~/components/utilities/cry import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; +export interface SecretDataProps { + pos: number; + key: string; + value: string; + type: string; + id: string; + environment: string; +} + interface SideBarProps { toggleSidebar: (value: boolean) => void; setSnapshotData: (value: any) => void; @@ -43,8 +52,6 @@ interface EncrypetedSecretVersionListProps { * @param {function} obj.toggleSidebar - function that opens or closes the sidebar * @param {function} obj.setSnapshotData - state manager for snapshot data * @param {string} obj.chosenSnaphshot - the snapshot id which is currently selected - * - * * @returns the sidebar with the options for point-in-time recovery (commits) */ const PITRecoverySidebar = ({ @@ -111,7 +118,21 @@ const PITRecoverySidebar = ({ } }) - setSnapshotData({ id: secretSnapshotData._id, version: secretSnapshotData.version, createdAt: secretSnapshotData.createdAt, secretVersions: decryptedSecretVersions }) + + const secretKeys = [...new Set(decryptedSecretVersions.map((secret: SecretDataProps) => secret.key))]; + + const result = secretKeys.map((key, index) => { + return { + id: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0].id, + pos: index, + key: key, + environment: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0].environment, + value: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0]?.value, + valueOverride: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'personal')[0]?.value, + } + }); + + setSnapshotData({ id: secretSnapshotData._id, version: secretSnapshotData.version, createdAt: secretSnapshotData.createdAt, secretVersions: result, comment: '' }) } return
@@ -125,31 +146,35 @@ const PITRecoverySidebar = ({ >
) : ( -
+

{t("Point-in-time Recovery")}

toggleSidebar(false)}>
-
- {secretSnapshotsMetadata?.map((snapshot: SnaphotProps, id: number) =>
-
-
{timeSince(new Date(snapshot.createdAt))}
-
{" - " + snapshot.secretVersions.length + " Secrets"}
-
+
+ {secretSnapshotsMetadata?.map((snapshot: SnaphotProps, id: number) =>
exploreSnapshot({ snapshotId: snapshot._id })} - className={`${chosenSnapshot == snapshot._id || (id == 0 && chosenSnapshot === "") ? "text-bunker-800 pointer-events-none" : "text-bunker-200 hover:text-primary duration-200 cursor-pointer"} text-sm`}> - {id == 0 ? "Current Version" : chosenSnapshot == snapshot._id ? "Currently Viewing" : "Explore"} + key={snapshot._id} + onClick={() => exploreSnapshot({ snapshotId: snapshot._id })} + className={`${chosenSnapshot == snapshot._id || (id == 0 && chosenSnapshot === "") ? "bg-primary text-black pointer-events-none" : "bg-mineshaft-700 hover:bg-mineshaft-500 duration-200 cursor-pointer"} py-3 px-4 mb-2 rounded-md flex flex-row justify-between items-center`} + > +
+
{timeSince(new Date(snapshot.createdAt))}
+
{" - " + snapshot.secretVersions.length + " Secrets"}
+
+
+ {id == 0 ? "Current Version" : chosenSnapshot == snapshot._id ? "Currently Viewing" : "Explore"} +
+
)} +
+
+
-
)} -
-
-
-
)}
diff --git a/frontend/ee/components/SecretVersionList.tsx b/frontend/ee/components/SecretVersionList.tsx index 3eb40005f..d07198dc0 100644 --- a/frontend/ee/components/SecretVersionList.tsx +++ b/frontend/ee/components/SecretVersionList.tsx @@ -52,7 +52,7 @@ const SecretVersionList = ({ secretId }: { secretId: string; }) => { }); } - const decryptedSecretVersions = encryptedSecretVersions.secretVersions.map((encryptedSecretVersion: EncrypetedSecretVersionListProps) => { + const decryptedSecretVersions = encryptedSecretVersions?.secretVersions.map((encryptedSecretVersion: EncrypetedSecretVersionListProps) => { return { createdAt: encryptedSecretVersion.createdAt, value: decryptSymmetric({ @@ -87,28 +87,33 @@ const SecretVersionList = ({ secretId }: { secretId: string; }) => {
) : (
- {secretVersions?.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) - .map((version: DecryptedSecretVersionListProps, index: number) => -
-
-
-
-
-
-
- {(new Date(version.createdAt)).toLocaleDateString('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - })} + {secretVersions + ? secretVersions?.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) + .map((version: DecryptedSecretVersionListProps, index: number) => +
+
+
+
+
+
+
+ {(new Date(version.createdAt)).toLocaleDateString('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + })} +
+

Value:{version.value}

-

Value:{version.value}

-
- )} + ) + : ( +
No version history yet.
+ ) + }
)}
diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 4ce91002d..07b4133a0 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -45,11 +45,12 @@ import getWorkspaces from '../api/workspace/getWorkspaces'; interface SecretDataProps { - type: 'personal' | 'shared'; pos: number; key: string; value: string; + valueOverride: string | undefined; id: string; + idOverride: string | undefined; comment: string; } @@ -68,10 +69,11 @@ interface SnapshotProps { secretVersions: { id: string; pos: number; - type: "personal" | "shared"; environment: string; key: string; value: string; + valueOverride: string; + comment: string; }[]; } @@ -99,7 +101,7 @@ function findDuplicates(arr: any[]) { */ export default function Dashboard() { const [data, setData] = useState(); - const [initialData, setInitialData] = useState([]); + const [initialData, setInitialData] = useState([]); const [buttonReady, setButtonReady] = useState(false); const router = useRouter(); const [workspaceId, setWorkspaceId] = useState(''); @@ -119,6 +121,7 @@ export default function Dashboard() { const [sharedToHide, setSharedToHide] = useState([]); const [snapshotData, setSnapshotData] = useState(); const [numSnapshots, setNumSnapshots] = useState(); + const [saveLoading, setSaveLoading] = useState(false); const { t } = useTranslation(); const { createNotification } = useNotificationContext(); @@ -213,16 +216,6 @@ export default function Dashboard() { setInitialData(dataToSort); reorderRows(dataToSort); - setSharedToHide( - dataToSort?.filter(row => (dataToSort - ?.map((item) => item.key) - .filter( - (item, index) => - index !== - dataToSort?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id) - ) - setIsLoading(false); } catch (error) { console.log('Error', error); @@ -238,39 +231,16 @@ export default function Dashboard() { ...data!, { id: guidGenerator(), + idOverride: guidGenerator(), pos: data!.length, key: '', value: '', - type: 'shared', + valueOverride: undefined, comment: '', } ]); }; - /** - * This function add an ovverrided version of a certain secret to the current user - * @param {object} obj - * @param {string} obj.id - if of this secret that is about to be overriden - * @param {string} obj.keyName - key name of this secret - * @param {string} obj.value - value of this secret - * @param {string} obj.pos - position of this secret on the dashboard - */ - const addOverride = ({ id, keyName, value, pos, comment }: overrideProps) => { - setIsNew(false); - const tempdata: SecretDataProps[] | 1 = [ - ...data!, - { - id: id, - pos: pos, - key: keyName, - value: value, - type: 'personal', - comment: comment - } - ]; - sortValuesHandler(tempdata, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical"); - }; - const deleteRow = ({ ids, secretName }: { ids: string[]; secretName: string; }) => { setButtonReady(true); toggleSidebar("None"); @@ -289,15 +259,15 @@ export default function Dashboard() { setButtonReady(true); // find which shared secret corresponds to the overriden version - const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id; + // const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id; // change the sidebar to this shared secret; and unhide it - toggleSidebar(sharedVersionOfOverride) - setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride)) + // toggleSidebar(sharedVersionOfOverride) + // setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride)) // resort secrets - const tempData = data!.filter((row: SecretDataProps) => !(row.key == data!.filter(row => row.id == id)[0]?.key && row.type == 'personal')) - sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical") + // const tempData = data!.filter((row: SecretDataProps) => !(row.key == data!.filter(row => row.id == id)[0]?.key && row.type == 'personal')) + // sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical") }; const modifyValue = (value: string, pos: number) => { @@ -308,6 +278,14 @@ export default function Dashboard() { setButtonReady(true); }; + const modifyValueOverride = (value: string | undefined, pos: number) => { + setData((oldData) => { + oldData![pos].valueOverride = value; + return [...oldData!]; + }); + setButtonReady(true); + }; + const modifyKey = (value: string, pos: number) => { setData((oldData) => { oldData![pos].key = value; @@ -329,6 +307,10 @@ export default function Dashboard() { modifyValue(value, pos); }, []); + const listenChangeValueOverride = useCallback((value: string | undefined, pos: number) => { + modifyValueOverride(value, pos); + }, []); + const listenChangeKey = useCallback((value: string, pos: number) => { modifyKey(value, pos); }, []); @@ -341,6 +323,7 @@ export default function Dashboard() { * Save the changes of environment variables and push them to the database */ const savePush = async (dataToPush?: SecretDataProps[]) => { + setSaveLoading(true); let newData: SecretDataProps[] | null | undefined; // dataToPush is mostly used for rollbacks, otherwise we always take the current state data if ((dataToPush ?? [])?.length > 0) { @@ -349,16 +332,11 @@ export default function Dashboard() { newData = data; } - const obj = Object.assign( - {}, - ...newData!.map((row: SecretDataProps) => ({ [row.type.charAt(0) + row.key]: [row.value, row.comment ?? ''] })) - ); - // Checking if any of the secret keys start with a number - if so, don't do anything - const nameErrors = !Object.keys(obj) - .map((key) => !isNaN(Number(key[0].charAt(0)))) + const nameErrors = !newData! + .map((secret) => !isNaN(Number(secret.key.charAt(0)))) .every((v) => v === false); - const duplicatesExist = findDuplicates(data!.map((item: SecretDataProps) => item.key + item.type)).length > 0; + const duplicatesExist = findDuplicates(data!.map((item: SecretDataProps) => item.key)).length > 0; if (nameErrors) { return createNotification({ @@ -378,34 +356,64 @@ export default function Dashboard() { setButtonReady(false); const secretsToBeDeleted - = initialData + = initialData! .filter(initDataPoint => !newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) .map(secret => secret.id); + console.log('delete', secretsToBeDeleted.length) const secretsToBeAdded = newData! - .filter(newDataPoint => !initialData.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)); + .filter(newDataPoint => !initialData!.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)); + console.log('add', secretsToBeAdded.length) const secretsToBeUpdated - = newData!.filter(newDataPoint => initialData + = newData!.filter(newDataPoint => initialData! .filter(initDataPoint => newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id) && (newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].value != initDataPoint.value || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].key != initDataPoint.key || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].comment != initDataPoint.comment)) .map(secret => secret.id).includes(newDataPoint.id)); + console.log('update', secretsToBeUpdated.length) + + const newOverrides = newData!.filter(newDataPoint => newDataPoint.valueOverride != undefined) + const initOverrides = initialData!.filter(initDataPoint => initDataPoint.valueOverride != undefined) + + const overridesToBeDeleted + = initOverrides + .filter(initDataPoint => !newOverrides!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) + .map(secret => String(secret.idOverride)); + console.log('override delete', overridesToBeDeleted.length) + + const overridesToBeAdded + = newOverrides! + .filter(newDataPoint => !initOverrides.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)) + .map(override => ({pos: override.pos, key: override.key, value: String(override.valueOverride), valueOverride: override.valueOverride, comment: '', id: String(override.idOverride), idOverride: String(override.idOverride)})); + console.log('override add', overridesToBeAdded.length) + + const overridesToBeUpdated + = newOverrides!.filter(newDataPoint => initOverrides + .filter(initDataPoint => newOverrides!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id) + && (newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].valueOverride != initDataPoint.valueOverride + || newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].key != initDataPoint.key + || newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].comment != initDataPoint.comment)) + .map(secret => secret.id).includes(newDataPoint.id)) + .map(override => ({pos: override.pos, key: override.key, value: String(override.valueOverride), valueOverride: override.valueOverride, comment: '', id: String(override.idOverride), idOverride: String(override.idOverride)})); + console.log('override update', overridesToBeUpdated.length) - if (secretsToBeDeleted.length > 0) { - await deleteSecrets({ secretIds: secretsToBeDeleted }); + if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) { + await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) }); } - if (secretsToBeAdded.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId, env: envMapping[env] }) + if (secretsToBeAdded.concat(overridesToBeAdded).length > 0) { + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded), workspaceId, env: envMapping[env] }); secrets && await addSecrets({ secrets, env: envMapping[env], workspaceId }); } - if (secretsToBeUpdated.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated, workspaceId, env: envMapping[env] }) + if (secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) { + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated), workspaceId, env: envMapping[env] }); secrets && await updateSecrets({ secrets }); } + setInitialData(newData); + // If this user has never saved environment variables before, show them a prompt to read docs if (!hasUserEverPushed) { setCheckDocsPopUpVisible(true); @@ -414,6 +422,7 @@ export default function Dashboard() { // increasing the number of project commits setNumSnapshots((numSnapshots ?? 0) + 1); + setSaveLoading(false); }; const addData = (newData: SecretDataProps[]) => { @@ -462,9 +471,8 @@ export default function Dashboard() { data={data.filter((row: SecretDataProps) => row.key == data.filter(row => row.id == sidebarSecretId)[0]?.key)} modifyKey={listenChangeKey} modifyValue={listenChangeValue} + modifyValueOverride={listenChangeValueOverride} modifyComment={listenChangeComment} - addOverride={addOverride} - deleteOverride={deleteOverride} buttonReady={buttonReady} savePush={savePush} sharedToHide={sharedToHide} @@ -533,6 +541,7 @@ export default function Dashboard() { active={buttonReady} iconDisabled={faCheck} textDisabled={String(t("common:saved"))} + loading={saveLoading} />
)} @@ -545,21 +554,11 @@ export default function Dashboard() { .filter(row => reverseEnvMapping[row.environment] == env) .map((sv, position) => { return { - id: sv.id, pos: position, type: sv.type, key: sv.key, value: sv.value, comment: '' + id: sv.id, idOverride: sv.id, pos: position, valueOverride: sv.valueOverride, key: sv.key, value: sv.value, comment: '' } }); setData(rolledBackSecrets); - setSharedToHide( - rolledBackSecrets?.filter(row => (rolledBackSecrets - ?.map((item) => item.key) - .filter( - (item, index) => - index !== - rolledBackSecrets?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id) - ) - // Perform the rollback globally performSecretRollback({ workspaceId, version: snapshotData.version }) @@ -663,16 +662,17 @@ export default function Dashboard() { >
{!snapshotData && data?.filter(row => row.key?.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(sharedToHide.includes(row.id) && row.type == 'shared')).map((keyPair) => ( + .filter(row => !sharedToHide.includes(row.id)).map((keyPair) => ( item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} + data?.map((item) => item.key) + )?.includes(keyPair.key)} toggleSidebar={toggleSidebar} sidebarSecretId={sidebarSecretId} isSnapshot={false} @@ -681,22 +681,26 @@ export default function Dashboard() { {snapshotData && snapshotData.secretVersions?.sort((a, b) => a.key.localeCompare(b.key)) .filter(row => reverseEnvMapping[row.environment] == snapshotEnv) .filter(row => row.key.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions + .filter( + row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions ?.map((item) => item.key) .filter( (item, index) => index !== snapshotData.secretVersions?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id).includes(row.id) && row.type == 'shared')).map((keyPair) => ( + ).includes(row.key)))?.map((item) => item.id).includes(row.id)) + ) + .map((keyPair) => ( item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} + data?.map((item) => item.key) + )?.includes(keyPair.key)} toggleSidebar={toggleSidebar} sidebarSecretId={sidebarSecretId} isSnapshot={true} diff --git a/frontend/public/data/frequentInterfaces.ts b/frontend/public/data/frequentInterfaces.ts new file mode 100644 index 000000000..c2fa797fd --- /dev/null +++ b/frontend/public/data/frequentInterfaces.ts @@ -0,0 +1,8 @@ +export interface SecretDataProps { + pos: number; + key: string; + value: string; + valueOverride: string | undefined; + id: string; + comment: string; +} \ No newline at end of file From 47fd48b7b01b8013073a3666ce5220324c492c1b Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Thu, 12 Jan 2023 13:57:00 -0800 Subject: [PATCH 22/25] Fixed the TS error during signup --- frontend/components/utilities/attemptLogin.ts | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/frontend/components/utilities/attemptLogin.ts b/frontend/components/utilities/attemptLogin.ts index c40dbb548..f1aa03ffb 100644 --- a/frontend/components/utilities/attemptLogin.ts +++ b/frontend/components/utilities/attemptLogin.ts @@ -1,3 +1,5 @@ +import { SecretDataProps } from 'public/data/frequentInterfaces'; + import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm'; import login1 from '~/pages/api/auth/Login1'; import login2 from '~/pages/api/auth/Login2'; @@ -13,14 +15,6 @@ import Telemetry from './telemetry/Telemetry'; import { saveTokenToLocalStorage } from './saveTokenToLocalStorage'; import SecurityClient from './SecurityClient'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} const crypto = require("crypto"); const nacl = require('tweetnacl'); @@ -145,53 +139,53 @@ const attemptLogin = async ( ); const secretsToBeAdded: SecretDataProps[] = [{ - type: "shared", pos: 0, key: "DATABASE_URL", value: "mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net", + valueOverride: undefined, comment: "This is an example of secret referencing.", id: '' }, { - type: "shared", pos: 1, key: "DB_USERNAME", value: "OVERRIDE_THIS", + valueOverride: undefined, comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", id: '' }, { - type: "personal", pos: 2, + key: "DB_PASSWORD", + value: "OVERRIDE_THIS", + valueOverride: undefined, + comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", + id: '' + }, { + pos: 3, key: "DB_USERNAME", value: "user1234", + valueOverride: "user1234", comment: "", id: '' }, { - type: "shared", - pos: 3, - key: "DB_PASSWORD", - value: "OVERRIDE_THIS", - comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", - id: '' - }, { - type: "personal", pos: 4, key: "DB_PASSWORD", value: "example_password", + valueOverride: "example_password", comment: "", id: '' }, { - type: "shared", pos: 5, key: "TWILIO_AUTH_TOKEN", value: "example_twillio_token", - comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", + valueOverride: undefined, + comment: "", id: '' }, { - type: "shared", pos: 6, key: "WEBSITE_URL", value: "http://localhost:3000", - comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", + valueOverride: undefined, + comment: "", id: '' }] const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId: String(localStorage.getItem('projectData.id')), env: 'dev' }) From 71f60f1589c62ef3f6d7953a6e0e0299bd9636c6 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 15:31:23 -0800 Subject: [PATCH 23/25] Update modify secrets api v2 so that fields are optional --- .../src/controllers/v2/secretsController.ts | 113 +++++++++--------- backend/src/ee/helpers/secret.ts | 92 +++++++------- backend/src/ee/models/secretVersion.ts | 50 ++++---- backend/src/routes/v2/secrets.ts | 44 +++---- 4 files changed, 141 insertions(+), 158 deletions(-) diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index ff9574711..f92fc4cee 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -2,8 +2,8 @@ import to from 'await-to-js'; import { Types } from 'mongoose'; import { Request, Response } from 'express'; import { ISecret, Secret } from '../../models'; -import { - SECRET_PERSONAL, +import { + SECRET_PERSONAL, SECRET_SHARED, ACTION_ADD_SECRETS, ACTION_READ_SECRETS, @@ -23,9 +23,9 @@ import { BadRequestError } from '../../utils/errors'; * @param res */ export const createSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; const { workspaceId, environment } = req.body; - + let toAdd; if (Array.isArray(req.body.secrets)) { // case: create multiple secrets @@ -34,7 +34,7 @@ export const createSecrets = async (req: Request, res: Response) => { // case: create 1 secret toAdd = [req.body.secrets]; } - + const newSecrets = await Secret.insertMany( toAdd.map(({ type, @@ -66,7 +66,7 @@ export const createSecrets = async (req: Request, res: Response) => { secretValueTag })) ); - + // (EE) add secret versions for new secrets EESecretService.addSecretVersions({ secretVersions: newSecrets.map(({ @@ -160,7 +160,7 @@ export const createSecrets = async (req: Request, res: Response) => { */ export const getSecrets = async (req: Request, res: Response) => { const { workspaceId, environment } = req.query; - + let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user if (req.user) { userId = req.user._id; @@ -169,13 +169,13 @@ export const getSecrets = async (req: Request, res: Response) => { if (req.serviceTokenData) { userId = req.serviceTokenData.user._id } - + const [err, secrets] = await to(Secret.find( { workspace: workspaceId, environment, $or: [ - { user: userId }, + { user: userId }, { user: { $exists: false } } ], type: { $in: [SECRET_SHARED, SECRET_PERSONAL] } @@ -183,9 +183,9 @@ export const getSecrets = async (req: Request, res: Response) => { ).then()) if (err) throw ValidationError({ message: 'Failed to get secrets', stack: err.stack }); - + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; - + const readAction = await EELogService.createActionSecret({ name: ACTION_READ_SECRETS, userId: req.user._id.toString(), @@ -214,7 +214,7 @@ export const getSecrets = async (req: Request, res: Response) => { } }); } - + return res.status(200).send({ secrets }); @@ -226,8 +226,8 @@ export const getSecrets = async (req: Request, res: Response) => { * @param res */ export const updateSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; - + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + // TODO: move type interface PatchSecret { id: string; @@ -242,7 +242,7 @@ export const updateSecrets = async (req: Request, res: Response) => { secretCommentTag: string; } - const ops = req.body.secrets.map((secret: PatchSecret) => { + const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => { const { secretKeyCiphertext, secretKeyIV, @@ -254,6 +254,7 @@ export const updateSecrets = async (req: Request, res: Response) => { secretCommentIV, secretCommentTag } = secret; + return ({ updateOne: { filter: { _id: new Types.ObjectId(secret.id) }, @@ -268,8 +269,8 @@ export const updateSecrets = async (req: Request, res: Response) => { secretValueIV, secretValueTag, ...(( - secretCommentCiphertext && - secretCommentIV && + secretCommentCiphertext && + secretCommentIV && secretCommentTag ) ? { secretCommentCiphertext, @@ -280,15 +281,17 @@ export const updateSecrets = async (req: Request, res: Response) => { } }); }); - await Secret.bulkWrite(ops); - - const newSecretsObj: { [key: string]: PatchSecret } = {}; + + await Secret.bulkWrite(updateOperationsToPerform); + + const secretModificationsBySecretId: { [key: string]: PatchSecret } = {}; req.body.secrets.forEach((secret: PatchSecret) => { - newSecretsObj[secret.id] = secret; + secretModificationsBySecretId[secret.id] = secret; }); - await EESecretService.addSecretVersions({ - secretVersions: req.secrets.map((secret: ISecret) => { + const ListOfSecretsBeforeModifications = req.secrets + const secretVersions = { + secretVersions: ListOfSecretsBeforeModifications.map((secret: ISecret) => { const { secretKeyCiphertext, secretKeyIV, @@ -298,37 +301,29 @@ export const updateSecrets = async (req: Request, res: Response) => { secretValueTag, secretCommentCiphertext, secretCommentIV, - secretCommentTag - } = newSecretsObj[secret._id.toString()] + secretCommentTag, + } = secretModificationsBySecretId[secret._id.toString()] + return ({ secret: secret._id, version: secret.version + 1, workspace: secret.workspace, type: secret.type, environment: secret.environment, - isDeleted: false, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - ...(( - secretCommentCiphertext && - secretCommentIV && - secretCommentTag - ) ? { - secretCommentCiphertext, - secretCommentIV, - secretCommentTag - } : { - secretCommentCiphertext: '', - secretCommentIV: '', - secretCommentTag: '' - }) + secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext, + secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV, + secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag, + secretValueCiphertext: secretValueCiphertext ? secretValueCiphertext : secret.secretValueCiphertext, + secretValueIV: secretValueIV ? secretValueIV : secret.secretValueIV, + secretValueTag: secretValueTag ? secretValueTag : secret.secretValueTag, + secretCommentCiphertext: secretCommentCiphertext ? secretCommentCiphertext : secret.secretCommentCiphertext, + secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV, + secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag, }); }) - }); + } + + await EESecretService.addSecretVersions(secretVersions); // group secrets into workspaces so updated secrets can @@ -355,7 +350,7 @@ export const updateSecrets = async (req: Request, res: Response) => { userId: req.user._id.toString(), workspaceId: key, secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) - }); + }); // (EE) create (audit) log updateAction && await EELogService.createLog({ @@ -367,9 +362,9 @@ export const updateSecrets = async (req: Request, res: Response) => { }); // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: key - }) + await EESecretService.takeSecretSnapshot({ + workspaceId: key + }) if (postHogClient) { postHogClient.capture({ @@ -385,7 +380,7 @@ export const updateSecrets = async (req: Request, res: Response) => { }); } }); - + return res.status(200).send({ secrets: await Secret.find({ _id: { @@ -401,15 +396,15 @@ export const updateSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; const toDelete = req.secrets.map((s: any) => s._id); - + await Secret.deleteMany({ _id: { $in: toDelete } }); - + await EESecretService.markDeletedSecretVersions({ secretIds: toDelete }); @@ -437,7 +432,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { userId: req.user._id.toString(), workspaceId: key, secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) - }); + }); // (EE) create (audit) log deleteAction && await EELogService.createLog({ @@ -449,9 +444,9 @@ export const deleteSecrets = async (req: Request, res: Response) => { }); // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: key - }) + await EESecretService.takeSecretSnapshot({ + workspaceId: key + }) if (postHogClient) { postHogClient.capture({ @@ -467,7 +462,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { }); } }); - + return res.status(200).send({ secrets: req.secrets }); diff --git a/backend/src/ee/helpers/secret.ts b/backend/src/ee/helpers/secret.ts index 529c9a980..7edee915f 100644 --- a/backend/src/ee/helpers/secret.ts +++ b/backend/src/ee/helpers/secret.ts @@ -1,11 +1,11 @@ import { Types } from 'mongoose'; import * as Sentry from '@sentry/node'; import { - Secret, + Secret, ISecret } from '../../models'; import { - SecretSnapshot, + SecretSnapshot, SecretVersion, ISecretVersion } from '../models'; @@ -18,24 +18,24 @@ import { * @param {String} obj.workspaceId * @returns {SecretSnapshot} secretSnapshot - new secret snapshot */ - const takeSecretSnapshotHelper = async ({ +const takeSecretSnapshotHelper = async ({ workspaceId }: { workspaceId: string; }) => { - + let secretSnapshot; try { const secretIds = (await Secret.find({ workspace: workspaceId }, '_id')).map((s) => s._id); - + const latestSecretVersions = (await SecretVersion.aggregate([ { - $match: { - secret: { - $in: secretIds - } + $match: { + secret: { + $in: secretIds + } } }, { @@ -48,14 +48,14 @@ import { { $sort: { version: -1 } } - ]) + ]) .exec()) .map((s) => s.versionId); - + const latestSecretSnapshot = await SecretSnapshot.findOne({ workspace: workspaceId }).sort({ version: -1 }); - + secretSnapshot = await new SecretSnapshot({ workspace: workspaceId, version: latestSecretSnapshot ? latestSecretSnapshot.version + 1 : 1, @@ -66,7 +66,7 @@ import { Sentry.captureException(err); throw new Error('Failed to take a secret snapshot'); } - + return secretSnapshot; } @@ -87,9 +87,9 @@ const addSecretVersionsHelper = async ({ } catch (err) { Sentry.setUser(null); Sentry.captureException(err); - throw new Error('Failed to add secret versions'); + throw new Error(`Failed to add secret versions [err=${err}]`); } - + return newSecretVersions; } @@ -120,39 +120,39 @@ const markDeletedSecretVersionsHelper = async ({ const initSecretVersioningHelper = async () => { try { - await Secret.updateMany( + await Secret.updateMany( { version: { $exists: false } }, { $set: { version: 1 } } ); - - const unversionedSecrets: ISecret[] = await Secret.aggregate([ - { - $lookup: { - from: 'secretversions', - localField: '_id', - foreignField: 'secret', - as: 'versions', - }, - }, - { - $match: { - versions: { $size: 0 }, - }, - }, - ]); - - if (unversionedSecrets.length > 0) { - await addSecretVersionsHelper({ - secretVersions: unversionedSecrets.map((s, idx) => ({ - ...s, - secret: s._id, - version: s.version ? s.version : 1, - isDeleted: false, - workspace: s.workspace, - environment: s.environment - })) - }); - } + + const unversionedSecrets: ISecret[] = await Secret.aggregate([ + { + $lookup: { + from: 'secretversions', + localField: '_id', + foreignField: 'secret', + as: 'versions', + }, + }, + { + $match: { + versions: { $size: 0 }, + }, + }, + ]); + + if (unversionedSecrets.length > 0) { + await addSecretVersionsHelper({ + secretVersions: unversionedSecrets.map((s, idx) => ({ + ...s, + secret: s._id, + version: s.version ? s.version : 1, + isDeleted: false, + workspace: s.workspace, + environment: s.environment + })) + }); + } } catch (err) { Sentry.setUser(null); @@ -162,7 +162,7 @@ const initSecretVersioningHelper = async () => { } export { - takeSecretSnapshotHelper, + takeSecretSnapshotHelper, addSecretVersionsHelper, markDeletedSecretVersionsHelper, initSecretVersioningHelper diff --git a/backend/src/ee/models/secretVersion.ts b/backend/src/ee/models/secretVersion.ts index 616d44fbd..391c0faec 100644 --- a/backend/src/ee/models/secretVersion.ts +++ b/backend/src/ee/models/secretVersion.ts @@ -10,14 +10,14 @@ import { export interface ISecretVersion { _id: Types.ObjectId; - secret: Types.ObjectId; - version: number; + secret: Types.ObjectId; + version: number; workspace: Types.ObjectId; // new type: string; // new user: Types.ObjectId; // new environment: string; // new - isDeleted: boolean; - secretKeyCiphertext: string; + isDeleted: boolean; + secretKeyCiphertext: string; secretKeyIV: string; secretKeyTag: string; secretKeyHash: string; @@ -28,17 +28,17 @@ export interface ISecretVersion { } const secretVersionSchema = new Schema( - { - secret: { // could be deleted - type: Schema.Types.ObjectId, - ref: 'Secret', - required: true - }, - version: { - type: Number, - default: 1, - required: true - }, + { + secret: { // could be deleted + type: Schema.Types.ObjectId, + ref: 'Secret', + required: true + }, + version: { + type: Number, + default: 1, + required: true + }, workspace: { type: Schema.Types.ObjectId, ref: 'Workspace', @@ -59,12 +59,12 @@ const secretVersionSchema = new Schema( enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, - isDeleted: { // consider removing field - type: Boolean, - default: false, - required: true - }, - secretKeyCiphertext: { + isDeleted: { // consider removing field + type: Boolean, + default: false, + required: true + }, + secretKeyCiphertext: { type: String, required: true }, @@ -94,10 +94,10 @@ const secretVersionSchema = new Schema( secretValueHash: { type: String } - }, - { - timestamps: true - } + }, + { + timestamps: true + } ); const SecretVersion = model('SecretVersion', secretVersionSchema); diff --git a/backend/src/routes/v2/secrets.ts b/backend/src/routes/v2/secrets.ts index 29eac042f..085d487cd 100644 --- a/backend/src/routes/v2/secrets.ts +++ b/backend/src/routes/v2/secrets.ts @@ -8,8 +8,8 @@ import { } from '../../middleware'; import { query, check, body } from 'express-validator'; import { secretsController } from '../../controllers/v2'; -import { - ADMIN, +import { + ADMIN, MEMBER, SECRET_PERSONAL, SECRET_SHARED @@ -27,7 +27,7 @@ router.post( if (value.length === 0) throw new Error('secrets cannot be an empty array') for (const secret of value) { if ( - !secret.type || + !secret.type || !(secret.type === SECRET_PERSONAL || secret.type === SECRET_SHARED) || !secret.secretKeyCiphertext || !secret.secretKeyIV || @@ -42,7 +42,7 @@ router.post( } else if (typeof value === 'object') { // case: update 1 secret if ( - !value.type || + !value.type || !(value.type === SECRET_PERSONAL || value.type === SECRET_SHARED) || !value.secretKeyCiphertext || !value.secretKeyIV || @@ -52,13 +52,13 @@ router.post( !value.secretValueTag ) { throw new Error('secrets object is missing required secret properties'); - } + } } else { throw new Error('secrets must be an object or an array of objects') } - + return true; - }), + }), validateRequest, requireAuth({ acceptedAuthModes: ['jwt'] @@ -95,36 +95,24 @@ router.patch( if (value.length === 0) throw new Error('secrets cannot be an empty array') for (const secret of value) { if ( - !secret.id || - !secret.secretKeyCiphertext || - !secret.secretKeyIV || - !secret.secretKeyTag || - !secret.secretValueCiphertext || - !secret.secretValueIV || - !secret.secretValueTag + !secret.id ) { - throw new Error('secrets array must contain objects that have required secret properties'); + throw new Error('Each secret must contain a ID property'); } } } else if (typeof value === 'object') { // case: update 1 secret if ( - !value.id || - !value.secretKeyCiphertext || - !value.secretKeyIV || - !value.secretKeyTag || - !value.secretValueCiphertext || - !value.secretValueIV || - !value.secretValueTag + !value.id ) { - throw new Error('secrets object is missing required secret properties'); - } + throw new Error('secret must contain a ID property'); + } } else { throw new Error('secrets must be an object or an array of objects') } - + return true; - }), + }), validateRequest, requireAuth({ acceptedAuthModes: ['jwt'] @@ -142,13 +130,13 @@ router.delete( .custom((value) => { // case: delete 1 secret if (typeof value === 'string') return true; - + if (Array.isArray(value)) { // case: delete multiple secrets if (value.length === 0) throw new Error('secrets cannot be an empty array'); return value.every((id: string) => typeof id === 'string') } - + throw new Error('secretIds must be a string or an array of strings'); }) .not() From a707fe14982a620b314ab23e56afda1d180fb555 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 15:56:49 -0800 Subject: [PATCH 24/25] disable integration --- backend/src/variables/integration.ts | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 1625c3954..baea2b093 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -11,10 +11,10 @@ const INTEGRATION_VERCEL = 'vercel'; const INTEGRATION_NETLIFY = 'netlify'; const INTEGRATION_GITHUB = 'github'; const INTEGRATION_SET = new Set([ - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB + INTEGRATION_HEROKU, + INTEGRATION_VERCEL, + INTEGRATION_NETLIFY, + INTEGRATION_GITHUB ]); // integration types @@ -23,10 +23,10 @@ const INTEGRATION_OAUTH2 = 'oauth2'; // integration oauth endpoints const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token'; const INTEGRATION_VERCEL_TOKEN_URL = - 'https://api.vercel.com/v2/oauth/access_token'; + 'https://api.vercel.com/v2/oauth/access_token'; const INTEGRATION_NETLIFY_TOKEN_URL = 'https://api.netlify.com/oauth/token'; const INTEGRATION_GITHUB_TOKEN_URL = - 'https://github.com/login/oauth/access_token'; + 'https://github.com/login/oauth/access_token'; // integration apps endpoints const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com'; @@ -37,7 +37,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Heroku', slug: 'heroku', - image: 'Heroku', + image: 'Heroku', isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_HEROKU, @@ -46,8 +46,8 @@ const INTEGRATION_OPTIONS = [ { name: 'Vercel', slug: 'vercel', - image: 'Vercel', - isAvailable: true, + image: 'Vercel', + isAvailable: false, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -56,8 +56,8 @@ const INTEGRATION_OPTIONS = [ { name: 'Netlify', slug: 'netlify', - image: 'Netlify', - isAvailable: true, + image: 'Netlify', + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -65,17 +65,17 @@ const INTEGRATION_OPTIONS = [ { name: 'GitHub', slug: 'github', - image: 'GitHub', - isAvailable: true, + image: 'GitHub', + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' - + }, { name: 'Google Cloud Platform', slug: 'gcp', - image: 'Google Cloud Platform', + image: 'Google Cloud Platform', isAvailable: false, type: '', clientId: '', @@ -84,7 +84,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Amazon Web Services', slug: 'aws', - image: 'Amazon Web Services', + image: 'Amazon Web Services', isAvailable: false, type: '', clientId: '', @@ -93,7 +93,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Microsoft Azure', slug: 'azure', - image: 'Microsoft Azure', + image: 'Microsoft Azure', isAvailable: false, type: '', clientId: '', @@ -102,7 +102,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Travis CI', slug: 'travisci', - image: 'Travis CI', + image: 'Travis CI', isAvailable: false, type: '', clientId: '', @@ -111,7 +111,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Circle CI', slug: 'circleci', - image: 'Circle CI', + image: 'Circle CI', isAvailable: false, type: '', clientId: '', @@ -120,18 +120,18 @@ const INTEGRATION_OPTIONS = [ ] export { - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_SET, - INTEGRATION_OAUTH2, - INTEGRATION_HEROKU_TOKEN_URL, - INTEGRATION_VERCEL_TOKEN_URL, - INTEGRATION_NETLIFY_TOKEN_URL, - INTEGRATION_GITHUB_TOKEN_URL, - INTEGRATION_HEROKU_API_URL, - INTEGRATION_VERCEL_API_URL, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_OPTIONS + INTEGRATION_HEROKU, + INTEGRATION_VERCEL, + INTEGRATION_NETLIFY, + INTEGRATION_GITHUB, + INTEGRATION_SET, + INTEGRATION_OAUTH2, + INTEGRATION_HEROKU_TOKEN_URL, + INTEGRATION_VERCEL_TOKEN_URL, + INTEGRATION_NETLIFY_TOKEN_URL, + INTEGRATION_GITHUB_TOKEN_URL, + INTEGRATION_HEROKU_API_URL, + INTEGRATION_VERCEL_API_URL, + INTEGRATION_NETLIFY_API_URL, + INTEGRATION_OPTIONS }; From 1ac94ee940bb432deeab6360232c95533e5aa954 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 16:37:59 -0800 Subject: [PATCH 25/25] selectively get user email from service toke/jwt --- .../src/controllers/v2/secretController.ts | 191 +++++++++--------- .../src/controllers/v2/secretsController.ts | 5 +- 2 files changed, 101 insertions(+), 95 deletions(-) diff --git a/backend/src/controllers/v2/secretController.ts b/backend/src/controllers/v2/secretController.ts index c2cf45f9a..89567e616 100644 --- a/backend/src/controllers/v2/secretController.ts +++ b/backend/src/controllers/v2/secretController.ts @@ -7,7 +7,7 @@ const { ValidationError } = mongoose.Error; import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors'; import { AnyBulkWriteOperation } from 'mongodb'; import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables"; -// import { postHogClient } from '../../services'; +import { postHogClient } from '../../services'; /** * Create secret for workspace with id [workspaceId] and environment [environment] @@ -42,19 +42,19 @@ export const createSecret = async (req: Request, res: Response) => { throw RouteValidationError({ message: error.message, stack: error.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets added', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // workspaceId, - // environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets added', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + workspaceId, + environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secret @@ -103,19 +103,19 @@ export const createSecrets = async (req: Request, res: Response) => { throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets added', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secretsToCreate ?? []).length, - // workspaceId, - // environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets added', + distinctId: req.user.email, + properties: { + numberOfSecrets: (secretsToCreate ?? []).length, + workspaceId, + environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secrets @@ -158,19 +158,19 @@ export const deleteSecrets = async (req: Request, res: Response) => { throw InternalServerError() } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets deleted', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: numSecretsDeleted, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets deleted', + distinctId: req.user.email, + properties: { + numberOfSecrets: numSecretsDeleted, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send() } @@ -183,19 +183,19 @@ export const deleteSecrets = async (req: Request, res: Response) => { export const deleteSecret = async (req: Request, res: Response) => { await Secret.findByIdAndDelete(req._secret._id) - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets deleted', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // workspaceId: req._secret.workspace.toString(), - // environment: req._secret.environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets deleted', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + workspaceId: req._secret.workspace.toString(), + environment: req._secret.environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secret: req._secret @@ -252,19 +252,19 @@ export const updateSecrets = async (req: Request, res: Response) => { throw InternalServerError() } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets modified', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secretsModificationsRequested ?? []).length, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets modified', + distinctId: req.user.email, + properties: { + numberOfSecrets: (secretsModificationsRequested ?? []).length, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.status(200).send() } @@ -304,19 +304,19 @@ export const updateSecret = async (req: Request, res: Response) => { throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets modified', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets modified', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.status(200).send(singleModificationUpdate) } @@ -332,13 +332,16 @@ export const getSecrets = async (req: Request, res: Response) => { const { environment } = req.query; const { workspaceId } = req.params; - let userId: string | undefined = undefined // used for getting personal secrets for user + let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user + let userEmail: Types.ObjectId | undefined = undefined // used for posthog if (req.user) { - userId = req.user._id.toString(); + userId = req.user._id; + userEmail = req.user.email; } if (req.serviceTokenData) { userId = req.serviceTokenData.user._id + userEmail = req.serviceTokenData.user.email; } const [err, secrets] = await to(Secret.find( @@ -354,19 +357,19 @@ export const getSecrets = async (req: Request, res: Response) => { throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets pulled', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secrets ?? []).length, - // environment, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets pulled', + distinctId: userEmail, + properties: { + numberOfSecrets: (secrets ?? []).length, + environment, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.json(secrets) } diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index f92fc4cee..b5a27874f 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -162,12 +162,15 @@ export const getSecrets = async (req: Request, res: Response) => { const { workspaceId, environment } = req.query; let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user + let userEmail: Types.ObjectId | undefined = undefined // used for posthog if (req.user) { userId = req.user._id; + userEmail = req.user.email; } if (req.serviceTokenData) { userId = req.serviceTokenData.user._id + userEmail = req.serviceTokenData.user.email; } const [err, secrets] = await to(Secret.find( @@ -204,7 +207,7 @@ export const getSecrets = async (req: Request, res: Response) => { if (postHogClient) { postHogClient.capture({ event: 'secrets added', - distinctId: req.user.email, + distinctId: userEmail, properties: { numberOfSecrets: secrets.length, environment,