diff --git a/.eslintignore b/.eslintignore index 2dff9d9aebc..930b468de83 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,6 @@ **/build **/dist +**/coverage .angular **/node_modules @@ -21,3 +22,7 @@ apps/web/src/theme.js apps/web/tailwind.config.js apps/cli/config/config.js + +tailwind.config.js +libs/components/tailwind.config.base.js +libs/components/tailwind.config.js diff --git a/.eslintrc.json b/.eslintrc.json index 161bc9ec744..72459521605 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,13 +4,19 @@ "browser": true, "webextensions": true }, - "plugins": ["@typescript-eslint"], + "plugins": ["@typescript-eslint", "rxjs", "rxjs-angular"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": ["./tsconfig.eslint.json"], + "sourceType": "module" + }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:import/recommended", "plugin:import/typescript", - "prettier" + "prettier", + "plugin:rxjs/recommended" ], "rules": { "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled @@ -50,6 +56,7 @@ ], "pathGroupsExcludedImportTypes": ["builtin"] } - ] + ], + "rxjs-angular/prefer-takeuntil": "error" } } diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 77303768f7a..0320dbee331 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -138,10 +138,13 @@ jobs: run: npm ci working-directory: ./ - - name: Build & Test + - name: Build run: | npm run dist - npm run test + + - name: Build Manifest v3 + run: | + npm run dist:chrome:mv3 - name: Gulp run: gulp ci @@ -181,6 +184,13 @@ jobs: path: apps/browser/dist/dist-chrome.zip if-no-files-found: error + - name: Upload Chrome MV3 artifact + uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + with: + name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip + path: apps/browser/dist/dist-chrome-MV3.zip + if-no-files-found: error + - name: Upload Firefox artifact uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 with: diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 96ccea4ef86..4dd90efb2a4 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -155,9 +155,6 @@ jobs: run: npm ci working-directory: ./ - - name: Run tests - run: npm run test - - name: Build & Package run: npm run dist --quiet diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index aec2402d9e7..0d98b26ec4d 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -652,7 +652,7 @@ jobs: run: npm run build - name: Download artifact from hotfix-rc - if: github.ref == 'refs/heads/hotfix-rc') + if: github.ref == 'refs/heads/hotfix-rc' uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 with: workflow: build-browser.yml @@ -854,7 +854,7 @@ jobs: run: npm run build - name: Download artifact from hotfix-rc - if: github.ref == 'refs/heads/hotfix-rc') + if: github.ref == 'refs/heads/hotfix-rc' uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 with: workflow: build-browser.yml diff --git a/.github/workflows/release-qa-web.yml b/.github/workflows/release-qa-web.yml index 14eb5a4c670..fc0cd5231eb 100644 --- a/.github/workflows/release-qa-web.yml +++ b/.github/workflows/release-qa-web.yml @@ -81,7 +81,7 @@ jobs: environment-url: http://vault.qa.bitwarden.pw environment: 'Web Vault - QA' description: 'Deployment from branch ${{ github.ref_name }}' - + - name: Update deployment status to In Progress uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 with: @@ -94,7 +94,7 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a with: workflow: build-web.yml path: apps/web diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000000..36e4157f05b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +--- +name: Run tests + +on: + workflow_dispatch: + pull_request: + branches-ignore: + - 'l10n_master' + - 'cf-pages' + paths: + - 'apps/**' + - 'libs/**' + - '*' + - '!*.md' + - '!*.txt' + - '.github/workflows/test.yml' + +defaults: + run: + shell: bash + +jobs: + test: + name: Run tests + runs-on: ubuntu-20.04 + steps: + - name: Checkout repo + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + + - name: Set up Node + uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + with: + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + node-version: '16' + + - name: Print environment + run: | + node --version + npm --version + + - name: Install Node dependencies + run: npm ci + + - name: Run tests + run: | + export NODE_OPTIONS=--max_old_space_size=6144 + npm run test diff --git a/.gitignore b/.gitignore index 2e389245b2f..0774f26cc3d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ coverage # Storybook documentation.json +.eslintcache diff --git a/.prettierignore b/.prettierignore index 0a23f5ce733..8790a1e73ff 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,7 @@ **/dist **/coverage .angular +documentation.json # External libraries / auto synced locales apps/browser/src/_locales diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..fe586d49e99 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["Popout", "Reprompt", "takeuntil"] +} diff --git a/README.md b/README.md index 960f1223041..c426251905b 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,15 @@ Github Workflow browser build on master + + Github Workflow CLI build on master + Github Workflow desktop build on master + + Github Workflow web build on master + gitter chat diff --git a/apps/browser/gulpfile.js b/apps/browser/gulpfile.js index 87fb086ca68..29bca0f8954 100644 --- a/apps/browser/gulpfile.js +++ b/apps/browser/gulpfile.js @@ -34,6 +34,9 @@ const filters = { function buildString() { var build = ""; + if (process.env.MANIFEST_VERSION) { + build = `-mv${process.env.MANIFEST_VERSION}`; + } if (process.env.BUILD_NUMBER && process.env.BUILD_NUMBER !== "") { build = `-${process.env.BUILD_NUMBER}`; } @@ -57,6 +60,7 @@ function dist(browserName, manifest) { function distFirefox() { return dist("firefox", (manifest) => { delete manifest.content_security_policy; + delete manifest.storage; removeShortcuts(manifest); return manifest; }); diff --git a/apps/browser/package.json b/apps/browser/package.json index f3a2b8b00b4..1be11430684 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -10,7 +10,7 @@ "build:prod:watch": "cross-env NODE_ENV=production webpack --watch", "dist": "npm run build:prod && gulp dist", "dist:chrome": "npm run build:prod && gulp dist:chrome", - "dist:chrome:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && gulp dist:chrome", + "dist:chrome:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && cross-env MANIFEST_VERSION=3 gulp dist:chrome", "dist:firefox": "npm run build:prod && gulp dist:firefox", "dist:opera": "npm run build:prod && gulp dist:opera", "dist:safari": "npm run build:prod && gulp dist:safari", diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index fd34a760f92..a1d5706e642 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -53,13 +53,13 @@ "message": "Karta" }, "vault": { - "message": "Vault" + "message": "Trezor" }, "myVault": { "message": "Můj trezor" }, "allVaults": { - "message": "All Vaults" + "message": "Všechny trezory" }, "tools": { "message": "Nástroje" @@ -424,13 +424,13 @@ "message": "Neplatná e-mailová adresa." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Je vyžadováno hlavní heslo." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Je vyžadováno zopakovat zadání hlavního hesla." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Hlavní heslo musí obsahovat alespoň 8 znaků." }, "masterPassDoesntMatch": { "message": "Potvrzení hlavního hesla se neshoduje." @@ -603,7 +603,7 @@ "message": "Ano, uložit nyní" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Zeptat se na aktualizaci existujícího přihlášení" }, "changedPasswordNotificationDesc": { "message": "Ask to update a login's password when a change is detected on a website." @@ -810,13 +810,13 @@ "message": "Obnova je dokončena" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Automaticky kopírovat TOTP" }, "disableAutoTotpCopyDesc": { "message": "Pokud mají vaše přihlašovací údaje přidán autentizační klíč pro TOTP, vygenerovaný ověřovací kód (TOTP) se automaticky zkopíruje do schránky při každém automatickém vyplnění přihlašovacích údajů." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Ověřit biometrické údaje při spuštění" }, "premiumRequired": { "message": "Vyžaduje prémiové členství" @@ -1037,16 +1037,16 @@ "message": "Tento prohlížeč nemůže zpracovat U2F požadavky ve vyskakovacím okně. Chcete otevřít toto vyskakovací okno v novém okně, abyste se mohli přihlásit pomocí U2F?" }, "enableFavicon": { - "message": "Show website icons" + "message": "Zobrazit ikony webových stránek" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Zobrazit rozeznatelný obrázek vedle každého přihlášení." }, "enableBadgeCounter": { "message": "Show badge counter" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku." }, "cardholderName": { "message": "Jméno držitele karty" @@ -1907,7 +1907,7 @@ } }, "error": { - "message": "Error" + "message": "Chyba" }, "regenerateUsername": { "message": "Znovu vygenerovat uživatelské jméno" @@ -1969,7 +1969,7 @@ "message": "Key Connector error: make sure Key Connector is available and working correctly." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Vyžadováno prémiové předplatné" }, "organizationIsDisabled": { "message": "Organization is disabled." @@ -1990,10 +1990,10 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Nastavení byla upravena" }, "environmentEditedClick": { - "message": "Click here" + "message": "Klikněte zde" }, "environmentEditedReset": { "message": "to reset to pre-configured settings" diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 7191c492e4b..1476952e07e 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1969,7 +1969,7 @@ "message": "Key Connector-fejl: Sørg for, at Key Connector er tilgængelig og fungerer korrekt." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Premium-abonnement kræves" }, "organizationIsDisabled": { "message": "Organisationen er deaktiveret." @@ -1981,7 +1981,7 @@ "message": "Mir" }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Logger ind på $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -1990,12 +1990,12 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Indstillinger er blevet redigeret" }, "environmentEditedClick": { - "message": "Click here" + "message": "Klik her" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "for at nulstille til forudkonfigurerede indstillinger" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index c85a71848b2..fb5c9a6abdc 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1565,7 +1565,7 @@ "message": "Eine Organisationsrichtlinie beeinflusst deine Eigentümer-Optionen." }, "excludedDomains": { - "message": "Ausgeschlossene Domänen" + "message": "Ausgeschlossene Domains" }, "excludedDomainsDesc": { "message": "Bitwarden wird keine Login-Daten für diese Domäne speichern. Du musst die Seite aktualisieren, damit die Änderungen wirksam werden." diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 4f879ede07a..653988ec120 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -424,13 +424,13 @@ "message": "Μη έγκυρη διεύθυνση e-mail." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Απαιτείται κύριος κωδικός πρόσβασης." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Απαιτείται ξανά ο κύριος κωδικός πρόσβασης." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Ο κύριος κωδικός πρέπει να έχει μήκος τουλάχιστον 8 χαρακτήρες." }, "masterPassDoesntMatch": { "message": "Η επιβεβαίωση κύριου κωδικού δεν ταιριάζει." @@ -571,22 +571,22 @@ "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "Ζητήστε να προσθέσετε σύνδεση" }, "addLoginNotificationDesc": { "message": "Η \"Προσθήκη Ειδοποίησης Σύνδεσης\" σας προτρέπει αυτόματα να αποθηκεύσετε νέες συνδέσεις στο vault σας κάθε φορά που θα συνδεθείτε για πρώτη φορά." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Εμφάνιση καρτών στη σελίδα Καρτέλας" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Λίστα αντικειμένων καρτών στη σελίδα Καρτέλας για εύκολη αυτόματη συμπλήρωση." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Εμφάνιση ταυτοτήτων στη σελίδα καρτέλας" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Λίστα στοιχείων ταυτότητας στη σελίδα Καρτέλας για εύκολη αυτόματη συμπλήρωση." }, "clearClipboard": { "message": "Εκκαθάριση Πρόχειρου", @@ -603,10 +603,10 @@ "message": "Ναι, Αποθήκευση Τώρα" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Ζητήστε να ενημερώσετε την υπάρχουσα σύνδεση" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Ζητήστε να ενημερώσετε τον κωδικό πρόσβασης μιας σύνδεσης όταν εντοπιστεί μια αλλαγή σε μια ιστοσελίδα." }, "notificationChangeDesc": { "message": "Θέλετε να ενημερώσετε αυτό τον κωδικό στο Bitwarden ;" @@ -615,10 +615,10 @@ "message": "Ναι, Ενημέρωση Τώρα" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "Εμφάνιση επιλογών μενού περιβάλλοντος" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Χρησιμοποιήστε ένα δευτερεύον κλικ για να αποκτήσετε πρόσβαση στη δημιουργία κωδικού πρόσβασης και να ταιριάξετε τις συνδέσεις για την ιστοσελίδα. " }, "defaultUriMatchDetection": { "message": "Προεπιλεγμένη ανίχνευση αντιστοιχίας URI", @@ -810,13 +810,13 @@ "message": "Επιτυχής ανανέωση" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Αυτόματη αντιγραφή TOTP" }, "disableAutoTotpCopyDesc": { "message": "Εάν η σύνδεση έχει συνημμένο κάποιο κλειδί επαλήθευσης, ο κωδικός επαλήθευσης TOTP αντιγράφεται αυτόματα στο πρόχειρο κάθε φορά που συμπληρώνετε αυτόματα τα στοιχεία σύνδεσης." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Ζητήστε βιομετρικά κατά την εκκίνηση" }, "premiumRequired": { "message": "Απαιτείται Έκδοση Premium" @@ -966,7 +966,7 @@ "message": "Προεπιλεγμένη ρύθμιση αυτόματης συμπλήρωσης για στοιχεία σύνδεσης" }, "defaultAutoFillOnPageLoadDesc": { - "message": "Αφού ενεργοποιήσετε την αυτόματη συμπλήρωση κατά τη Φόρτωση Σελίδας, μπορείτε να ενεργοποιήσετε ή να απενεργοποιήσετε τη λειτουργία για μεμονωμένα στοιχεία σύνδεσης. Αυτή είναι η προεπιλεγμένη ρύθμιση για στοιχεία σύνδεσης που δεν έχουν ρυθμιστεί ξεχωριστά." + "message": "Μπορείτε να απενεργοποιήσετε την αυτόματη συμπλήρωση φόρτωσης σελίδας για μεμονωμένα στοιχεία σύνδεσης από την προβολή Επεξεργασία στοιχείου." }, "itemAutoFillOnPageLoad": { "message": "Αυτόματη συμπλήρωση της Φόρτισης Σελίδας (αν είναι ενεργοποιημένη στις Επιλογές)" @@ -1037,16 +1037,16 @@ "message": "Αυτό το πρόγραμμα περιήγησης δεν μπορεί να επεξεργαστεί αιτήματα του U2F σε αυτό το αναδυόμενο παράθυρο. Θέλετε να ανοίξετε το αναδυόμενο σε νέο παράθυρο, ώστε να μπορείτε να συνδεθείτε χρησιμοποιώντας U2F;" }, "enableFavicon": { - "message": "Show website icons" + "message": "Εμφάνιση εικονιδίων ιστοσελίδας" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Εμφάνιση μιας αναγνωρίσιμης εικόνας δίπλα σε κάθε σύνδεση." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Εμφάνιση μετρητή εμβλημάτων" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "Υποδείξτε πόσες συνδέσεις έχετε για την τρέχουσα ιστοσελίδα." }, "cardholderName": { "message": "Όνομα κατόχου της κάρτας" @@ -1484,7 +1484,7 @@ "message": "Επιλέγοντας αυτό το πλαίσιο, συμφωνείτε με τα εξής:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Οι Όροι Παροχής Υπηρεσιών και η Πολιτική Απορρήτου δεν έχουν αναγνωριστεί." }, "termsOfService": { "message": "Όροι Χρήσης" @@ -1850,7 +1850,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Το χρονικό όριο του vault σας υπερβαίνει τους περιορισμούς που έχει ορίσει ο οργανισμός σας." }, "vaultExportDisabled": { "message": "Εξαγωγή vault Απενεργοποιημένη" @@ -1969,19 +1969,19 @@ "message": "Σφάλμα Key Connector: βεβαιωθείτε ότι το Key Connector είναι διαθέσιμο και λειτουργεί σωστά." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Απαιτείται συνδρομή Premium" }, "organizationIsDisabled": { - "message": "Organization is disabled." + "message": "Ο οργανισμός είναι απενεργοποιημένος." }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Δεν είναι δυνατή η πρόσβαση σε αντικείμενα σε απενεργοποιημένους οργανισμούς. Επικοινωνήστε με τον ιδιοκτήτη του Οργανισμού για βοήθεια." }, "cardBrandMir": { "message": "Mir" }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Σύνδεση στο $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -1990,12 +1990,12 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Οι ρυθμίσεις έχουν επεξεργαστεί" }, "environmentEditedClick": { - "message": "Click here" + "message": "Κάντε κλικ εδώ" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "επαναφορά στις προ-ρυθμισμένες ρυθμίσεις" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index ac25f4cf293..f5b3e798aff 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1967,7 +1967,7 @@ }, "ssoKeyConnectorError": { "message": "Key Connector error: make sure Key Connector is available and working correctly." - }, + }, "premiumSubcriptionRequired": { "message": "Premium subscription required" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 8203f36fe4a..65183ac1655 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1969,7 +1969,7 @@ "message": "Error en el conector de claves: asegúrate de que el conector de claves está disponible y que funciona correctamente." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Se requiere una Suscripción Premium" }, "organizationIsDisabled": { "message": "La organización está desactivada." diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index a91f2bcdb3c..a7bc7aecdc4 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -128,7 +128,7 @@ "message": "Jatka" }, "sendVerificationCode": { - "message": "Lähetä vahvistuskoodi sähköpostiisi" + "message": "Lähetä todennuskoodi sähköpostiisi" }, "sendCode": { "message": "Lähetä koodi" @@ -1031,7 +1031,7 @@ "description": "This describes a value that is 'linked' (tied) to another value." }, "popup2faCloseMessage": { - "message": "Klikkaus ponnahdusikkunan ulkopuolelle todennuskoodin sähköpostista noutoa varten sulkee ikkunan. Haluatko avata näkymän uuteen ikkunaan, jotta se pysyy avoinna?" + "message": "Painallus ponnahdusikkunan ulkopuolelle todennuskoodin sähköpostista noutoa varten sulkee ikkunan. Haluatko avata näkymän uuteen ikkunaan, jotta se pysyy avoinna?" }, "popupU2fCloseMessage": { "message": "Tämä selain ei voi käsitellä U2F-pyyntöjä tässä ponnahdusikkunassa. Haluatko avata näkymän uuteen ikkunaan, jotta voit vahvistaa kirjautumisen U2F-todennuslaitteella?" @@ -1996,6 +1996,6 @@ "message": "Paina tästä" }, "environmentEditedReset": { - "message": "palauttaaksesi esimääritetyt asetukset" + "message": "palauttaaksesi oletusasetukset" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 292e448322b..a04d893f3fb 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -603,10 +603,10 @@ "message": "Enregistrer" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Demander à mettre à jour l'identifiant existant" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Demander à mettre à jour le mot de passe d'un identifiant lorsqu'un changement est détecté sur un site Web." }, "notificationChangeDesc": { "message": "Souhaitez-vous mettre à jour ce mot de passe dans Bitwarden ?" @@ -615,7 +615,7 @@ "message": "Mettre à jour" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "Afficher les options du menu contextuel" }, "contextMenuItemDesc": { "message": "Use a secondary click to access password generation and matching logins for the website. " @@ -810,7 +810,7 @@ "message": "Actualisation terminée" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Copier le code TOTP automatiquement" }, "disableAutoTotpCopyDesc": { "message": "Si une clé d'authentification est rattachée à votre identifiant, alors le code de vérification TOTP est automatiquement copié dans le presse-papiers lorsque vous renseignez l'identifiant." @@ -1037,10 +1037,10 @@ "message": "Ce navigateur ne peut pas traiter les requêtes U2F dans cette popup. Voulez-vous ouvrir cette popup dans une nouvelle fenêtre pour que vous puissiez vous connecter en utilisant U2F ?" }, "enableFavicon": { - "message": "Show website icons" + "message": "Afficher les icônes du site web" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Afficher une image reconnaissable à côté de chaque identifiant." }, "enableBadgeCounter": { "message": "Show badge counter" @@ -1484,7 +1484,7 @@ "message": "En cochant cette case, vous acceptez les éléments suivants :" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Les Conditions d'Utilisation et la Politique de Confidentialité n'ont pas été acceptées." }, "termsOfService": { "message": "Conditions d'utilisation" @@ -1850,7 +1850,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Le délai d'expiration de votre coffre-fort dépasse les restrictions définies par votre organisation." }, "vaultExportDisabled": { "message": "Export du coffre désactivé" @@ -1975,7 +1975,7 @@ "message": "L'organisation est désactivée." }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Les éléments des Organisations désactivées ne sont pas accessibles. Contactez le propriétaire de votre Organisation pour obtenir de l'aide." }, "cardBrandMir": { "message": "Mir" diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 6117948f427..81da03b0904 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -342,13 +342,13 @@ "message": "本人確認を行う" }, "yourVaultIsLocked": { - "message": "保管庫がロックされています。開くにはマスターパスワードを入力してください。" + "message": "保管庫がロックされています。続行するには本人確認を行ってください。" }, "unlock": { "message": "ロック解除" }, "loggedInAsOn": { - "message": "$HOSTNAME$ の $EMAIL$ としてログインしました。", + "message": "$EMAIL$ として $HOSTNAME$ にログインしました。", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 011a7bea482..8de1c306b0f 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1981,7 +1981,7 @@ "message": "Mir" }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Пријављивање на $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -1990,12 +1990,12 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Поставке су уређене" }, "environmentEditedClick": { - "message": "Click here" + "message": "Кликните овде" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "за рисетовање на подразумевана подешавања" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 70672f31728..a8157d03714 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -577,16 +577,16 @@ "message": "Aviseringar för nya inloggningar frågar dig om du vill lägga till en inloggning automatiskt till ditt valv, när du använder en inloggning som inte redan finns i ditt valv." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Visa kort på fliksida" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Lista kortobjekt på fliksidan för enkel automatisk fyllning." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Visa identiteter på fliksidan" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Lista identitetsobjekt på fliksidan för enkel automatisk fyllning." }, "clearClipboard": { "message": "Rensa urklipp", @@ -618,7 +618,7 @@ "message": "Visa alternativ för snabbmenyn" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Använd ett andra klick för att komma åt lösenordsgenerering och matchande inloggningar för webbplatsen. " }, "defaultUriMatchDetection": { "message": "Standardmatchning för URI", @@ -1969,7 +1969,7 @@ "message": "Key Connector-fel: säkerställ att Key Connector är tillgänglig och fungerar korrekt." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Premiumabonnemang krävs" }, "organizationIsDisabled": { "message": "Organisationen är inaktiverad." @@ -1981,7 +1981,7 @@ "message": "Mir" }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Loggar in på $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -1990,12 +1990,12 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Inställningarna har ändrats" }, "environmentEditedClick": { - "message": "Click here" + "message": "Klicka här" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "för att återställa till förkonfigurerade inställningar" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 396c29a1cb5..7f723394e1a 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -699,7 +699,7 @@ } }, "moveToOrgDesc": { - "message": "Виберіть організацію, до якої ви бажаєте перемістити цей запис. При переміщенні до організації власність запису передається тій організації. Ви більше не будете єдиним власником цього запису після переміщення." + "message": "Виберіть організацію, до якої ви бажаєте перемістити цей запис. Переміщуючи до організації, власність запису передається тій організації. Ви більше не будете єдиним власником цього запису після переміщення." }, "learnMore": { "message": "Докладніше" diff --git a/apps/browser/src/background/commands.background.ts b/apps/browser/src/background/commands.background.ts index 860f1bf0777..4d85c91c0bc 100644 --- a/apps/browser/src/background/commands.background.ts +++ b/apps/browser/src/background/commands.background.ts @@ -60,7 +60,7 @@ export default class CommandsBackground { await this.openPopup(); break; case "lock_vault": - await this.vaultTimeoutService.lock(true); + await this.vaultTimeoutService.lock(); break; default: break; diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 65712f5df2e..aecadd31fc3 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -48,7 +48,7 @@ export default class IdleBackground { if (action === "logOut") { await this.vaultTimeoutService.logOut(); } else { - await this.vaultTimeoutService.lock(true); + await this.vaultTimeoutService.lock(); } } } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index b4edd387db8..e771740e91a 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -235,7 +235,6 @@ export default class MainBackground { }); this.platformUtilsService = new BrowserPlatformUtilsService( this.messagingService, - this.stateService, (clipboardValue, clearMs) => { if (this.systemService != null) { this.systemService.clearClipboard(clipboardValue, clearMs); diff --git a/apps/browser/src/decorators/dev-flag.decorator.ts b/apps/browser/src/decorators/dev-flag.decorator.ts index da0c9934465..4a67d6239e6 100644 --- a/apps/browser/src/decorators/dev-flag.decorator.ts +++ b/apps/browser/src/decorators/dev-flag.decorator.ts @@ -1,6 +1,6 @@ -import { devFlagEnabled, DevFlagName } from "../flags"; +import { devFlagEnabled, DevFlags } from "../flags"; -export function devFlag(flag: DevFlagName) { +export function devFlag(flag: keyof DevFlags) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts index 40ba412cd4a..f2df60ad7a4 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts @@ -29,7 +29,7 @@ describe("session syncer", () => { afterEach(() => { jest.resetAllMocks(); - behaviorSubject.unsubscribe(); + behaviorSubject.complete(); }); describe("constructor", () => { diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts index 80eb07289f4..0c97b983f75 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject, Subscription } from "rxjs"; +import { BehaviorSubject, concatMap, Subscription } from "rxjs"; import { Utils } from "@bitwarden/common/misc/utils"; @@ -41,13 +41,17 @@ export class SessionSyncer { // This may be a memory leak. // There is no good time to unsubscribe from this observable. Hopefully Manifest V3 clears memory from temporary // contexts. If so, this is handled by destruction of the context. - this.subscription = this.behaviorSubject.subscribe(async (next) => { - if (this.ignoreNextUpdate) { - this.ignoreNextUpdate = false; - return; - } - await this.updateSession(next); - }); + this.subscription = this.behaviorSubject + .pipe( + concatMap(async (next) => { + if (this.ignoreNextUpdate) { + this.ignoreNextUpdate = false; + return; + } + await this.updateSession(next); + }) + ) + .subscribe(); } private listenForUpdates() { diff --git a/apps/browser/src/flags.ts b/apps/browser/src/flags.ts index 23012f3f5ea..39aad1809c6 100644 --- a/apps/browser/src/flags.ts +++ b/apps/browser/src/flags.ts @@ -1,61 +1,32 @@ +import { + flagEnabled as baseFlagEnabled, + devFlagEnabled as baseDevFlagEnabled, + devFlagValue as baseDevFlagValue, + SharedFlags, + SharedDevFlags, +} from "@bitwarden/common/misc/flags"; + import { GroupPolicyEnvironment } from "./types/group-policy-environment"; -function getFlags(envFlags: string | T): T { - if (typeof envFlags === "string") { - return JSON.parse(envFlags) as T; - } else { - return envFlags as T; - } -} +// required to avoid linting errors when there are no flags +/* eslint-disable-next-line @typescript-eslint/ban-types */ +export type Flags = {} & SharedFlags; -/* Placeholder for when we have a relevant feature flag -export type Flags = { test?: boolean }; -export type FlagName = keyof Flags; -export function flagEnabled(flag: FlagName): boolean { - const flags = getFlags(process.env.FLAGS); - return flags[flag] == null || flags[flag]; -} -*/ - -/** - * These flags are useful for development and testing. - * Dev Flags are always OFF in production. - */ +// required to avoid linting errors when there are no flags +/* eslint-disable-next-line @typescript-eslint/ban-types */ export type DevFlags = { storeSessionDecrypted?: boolean; managedEnvironment?: GroupPolicyEnvironment; -}; +} & SharedDevFlags; -export type DevFlagName = keyof DevFlags; - -/** - * Gets whether the given dev flag is truthy. - * Gets the value of a dev flag from environment. - * Will always return false unless in development. - * @param flag The name of the dev flag to check - * @returns The value of the flag - */ -export function devFlagEnabled(flag: DevFlagName): boolean { - if (process.env.ENV !== "development") { - return false; - } - - const devFlags = getFlags(process.env.DEV_FLAGS); - return devFlags[flag] == null || !!devFlags[flag]; +export function flagEnabled(flag: keyof Flags): boolean { + return baseFlagEnabled(flag); } -/** - * Gets the value of a dev flag from environment. - * Will always return false unless in development. - * @param flag The name of the dev flag to check - * @returns The value of the flag - * @throws Error if the flag is not enabled - */ -export function devFlagValue(flag: K): DevFlags[K] { - if (!devFlagEnabled(flag)) { - throw new Error(`This method should not be called, it is protected by a disabled dev flag.`); - } - - const devFlags = getFlags(process.env.DEV_FLAGS); - return devFlags[flag]; +export function devFlagEnabled(flag: keyof DevFlags) { + return baseDevFlagEnabled(flag); +} + +export function devFlagValue(flag: keyof DevFlags) { + return baseDevFlagValue(flag); } diff --git a/apps/browser/src/listeners/onCommandListener.ts b/apps/browser/src/listeners/onCommandListener.ts index 0e64f8b77e8..3abcd69e807 100644 --- a/apps/browser/src/listeners/onCommandListener.ts +++ b/apps/browser/src/listeners/onCommandListener.ts @@ -66,7 +66,6 @@ const doAutoFillLogin = async (tab: chrome.tabs.Tab): Promise => { const platformUtils = new BrowserPlatformUtilsService( null, // MessagingService - stateService, null, // clipboardWriteCallback null // biometricCallback ); diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 10e8382fae5..8004f199791 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -124,5 +124,8 @@ "default_title": "Bitwarden", "default_panel": "popup/index.html?uilocation=sidebar", "default_icon": "images/icon19.png" + }, + "storage": { + "managed_schema": "managed_schema.json" } } diff --git a/apps/browser/src/popup/accounts/set-password.component.ts b/apps/browser/src/popup/accounts/set-password.component.ts index 5d59bbb3f67..c4837f6a8e3 100644 --- a/apps/browser/src/popup/accounts/set-password.component.ts +++ b/apps/browser/src/popup/accounts/set-password.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; @@ -30,7 +31,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { policyService: PolicyService, router: Router, syncService: SyncService, - route: ActivatedRoute + route: ActivatedRoute, + organizationApiService: OrganizationApiServiceAbstraction ) { super( i18nService, @@ -44,7 +46,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { apiService, syncService, route, - stateService + stateService, + organizationApiService ); } } diff --git a/apps/browser/src/popup/accounts/two-factor.component.ts b/apps/browser/src/popup/accounts/two-factor.component.ts index 01e44909629..0a0bbcaec85 100644 --- a/apps/browser/src/popup/accounts/two-factor.component.ts +++ b/apps/browser/src/popup/accounts/two-factor.component.ts @@ -109,6 +109,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { } } + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.sso === "true") { super.onSuccessfulLogin = () => { diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 7524aec22d9..f1a69a6d6f3 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -35,7 +35,7 @@ export class AppComponent implements OnInit, OnDestroy { private lastActivity: number = null; private activeUserId: string; - private destroy$: Subject = new Subject(); + private destroy$ = new Subject(); constructor( private toastrService: ToastrService, @@ -132,6 +132,7 @@ export class AppComponent implements OnInit, OnDestroy { BrowserApi.messageListener("app.component", (window as any).bitwardenPopupMainMessageListener); + // eslint-disable-next-line rxjs/no-async-subscribe this.router.events.pipe(takeUntil(this.destroy$)).subscribe(async (event) => { if (event instanceof NavigationEnd) { const url = event.urlAfterRedirects || event.url || ""; diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 1be18d3df95..685c1d1db2c 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -18,6 +18,7 @@ import localeEnGb from "@angular/common/locales/en-GB"; import localeEnIn from "@angular/common/locales/en-IN"; import localeEs from "@angular/common/locales/es"; import localeEt from "@angular/common/locales/et"; +import localeEu from "@angular/common/locales/eu"; import localeFa from "@angular/common/locales/fa"; import localeFi from "@angular/common/locales/fi"; import localeFil from "@angular/common/locales/fil"; @@ -129,6 +130,7 @@ registerLocaleData(localeEnGb, "en-GB"); registerLocaleData(localeEnIn, "en-IN"); registerLocaleData(localeEs, "es"); registerLocaleData(localeEt, "et"); +registerLocaleData(localeEu, "eu"); registerLocaleData(localeFa, "fa"); registerLocaleData(localeFi, "fi"); registerLocaleData(localeFil, "fil"); diff --git a/apps/browser/src/popup/generator/generator.component.html b/apps/browser/src/popup/generator/generator.component.html index 121f9a88378..c3ae6c6ab53 100644 --- a/apps/browser/src/popup/generator/generator.component.html +++ b/apps/browser/src/popup/generator/generator.component.html @@ -349,6 +349,18 @@ /> + +
+ + +
+
@@ -383,6 +395,18 @@ />
+ +
+ + +
+
diff --git a/apps/browser/src/popup/send/send-add-edit.component.ts b/apps/browser/src/popup/send/send-add-edit.component.ts index 1877549ba6d..c04e180dbf5 100644 --- a/apps/browser/src/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/popup/send/send-add-edit.component.ts @@ -19,6 +19,7 @@ import { PopupUtilsService } from "../services/popup-utils.service"; selector: "app-send-add-edit", templateUrl: "send-add-edit.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class SendAddEditComponent extends BaseAddEditComponent { // Options header showOptions = false; @@ -98,6 +99,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { this.isUnsupportedMac = this.platformUtilsService.isChrome() && window?.navigator?.appVersion.includes("Mac OS X 11"); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (params) => { if (params.sendId) { this.sendId = params.sendId; diff --git a/apps/browser/src/popup/send/send-type.component.ts b/apps/browser/src/popup/send/send-type.component.ts index 36e0dce89a8..3e4054bf723 100644 --- a/apps/browser/src/popup/send/send-type.component.ts +++ b/apps/browser/src/popup/send/send-type.component.ts @@ -70,6 +70,7 @@ export class SendTypeComponent extends BaseSendComponent { async ngOnInit() { // Let super class finish await super.ngOnInit(); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (params) => { if (this.applySavedState) { this.state = await this.stateService.getBrowserSendTypeComponentState(); diff --git a/apps/browser/src/popup/settings/folder-add-edit.component.ts b/apps/browser/src/popup/settings/folder-add-edit.component.ts index c601292bb81..8ff8ba3b1a2 100644 --- a/apps/browser/src/popup/settings/folder-add-edit.component.ts +++ b/apps/browser/src/popup/settings/folder-add-edit.component.ts @@ -13,6 +13,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti selector: "app-folder-add-edit", templateUrl: "folder-add-edit.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class FolderAddEditComponent extends BaseFolderAddEditComponent { constructor( folderService: FolderService, @@ -27,6 +28,7 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (params) => { if (params.folderId) { this.folderId = params.folderId; diff --git a/apps/browser/src/popup/settings/settings.component.ts b/apps/browser/src/popup/settings/settings.component.ts index 3d9603a48e8..e91e8e47d4c 100644 --- a/apps/browser/src/popup/settings/settings.component.ts +++ b/apps/browser/src/popup/settings/settings.component.ts @@ -37,6 +37,7 @@ const RateUrls = { selector: "app-settings", templateUrl: "settings.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class SettingsComponent implements OnInit { @ViewChild("vaultTimeoutActionSelect", { read: ElementRef, static: true }) vaultTimeoutActionSelectRef: ElementRef; @@ -102,6 +103,7 @@ export class SettingsComponent implements OnInit { this.vaultTimeout.setValue(timeout); } this.previousVaultTimeout = this.vaultTimeout.value; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.vaultTimeout.valueChanges.subscribe(async (value) => { await this.saveVaultTimeout(value); }); @@ -302,7 +304,7 @@ export class SettingsComponent implements OnInit { } async lock() { - await this.vaultTimeoutService.lock(true); + await this.vaultTimeoutService.lock(); } async logOut() { diff --git a/apps/browser/src/popup/vault/add-edit-custom-fields.component.html b/apps/browser/src/popup/vault/add-edit-custom-fields.component.html index c6129233e1f..def0af30031 100644 --- a/apps/browser/src/popup/vault/add-edit-custom-fields.component.html +++ b/apps/browser/src/popup/vault/add-edit-custom-fields.component.html @@ -6,11 +6,13 @@
+ +
+ + +
+
@@ -416,6 +428,18 @@ />
+ +
+ + +
+
diff --git a/apps/desktop/src/app/vault/vault.component.ts b/apps/desktop/src/app/vault/vault.component.ts index 3907f7b4af9..3deae9fc725 100644 --- a/apps/desktop/src/app/vault/vault.component.ts +++ b/apps/desktop/src/app/vault/vault.component.ts @@ -216,6 +216,7 @@ export class VaultComponent implements OnInit, OnDestroy { } async load() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (params) => { if (params.cipherId) { const cipherView = new CipherView(); @@ -458,9 +459,12 @@ export class VaultComponent implements OnInit, OnDestroy { this.modal = modal; let madeAttachmentChanges = false; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil childComponent.onUploadedAttachment.subscribe(() => (madeAttachmentChanges = true)); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil childComponent.onDeletedAttachment.subscribe(() => (madeAttachmentChanges = true)); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.modal.onClosed.subscribe(async () => { this.modal = null; if (madeAttachmentChanges) { @@ -482,11 +486,13 @@ export class VaultComponent implements OnInit, OnDestroy { ); this.modal = modal; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe childComponent.onSharedCipher.subscribe(async () => { this.modal.close(); this.viewCipher(cipher); await this.ciphersComponent.refresh(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.modal.onClosed.subscribe(async () => { this.modal = null; }); @@ -504,10 +510,12 @@ export class VaultComponent implements OnInit, OnDestroy { ); this.modal = modal; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil childComponent.onSavedCollections.subscribe(() => { this.modal.close(); this.viewCipher(cipher); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.modal.onClosed.subscribe(async () => { this.modal = null; }); @@ -524,6 +532,7 @@ export class VaultComponent implements OnInit, OnDestroy { (comp) => (comp.cipherId = cipher.id) ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.modal.onClosed.subscribe(async () => { this.modal = null; }); @@ -596,6 +605,7 @@ export class VaultComponent implements OnInit, OnDestroy { ); this.modal = modal; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil childComponent.onSelected.subscribe((value: string) => { this.modal.close(); if (loginType) { @@ -608,6 +618,7 @@ export class VaultComponent implements OnInit, OnDestroy { } }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.modal.onClosed.subscribe(() => { this.modal = null; }); @@ -629,15 +640,18 @@ export class VaultComponent implements OnInit, OnDestroy { ); this.modal = modal; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe childComponent.onSavedFolder.subscribe(async (folder: FolderView) => { this.modal.close(); await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe childComponent.onDeletedFolder.subscribe(async (folder: FolderView) => { this.modal.close(); await this.vaultFilterComponent.reloadCollectionsAndFolders(this.activeFilter); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.modal.onClosed.subscribe(() => { this.modal = null; }); diff --git a/apps/desktop/src/flags.ts b/apps/desktop/src/flags.ts new file mode 100644 index 00000000000..d8692db0871 --- /dev/null +++ b/apps/desktop/src/flags.ts @@ -0,0 +1,27 @@ +import { + flagEnabled as baseFlagEnabled, + devFlagEnabled as baseDevFlagEnabled, + devFlagValue as baseDevFlagValue, + SharedFlags, + SharedDevFlags, +} from "@bitwarden/common/misc/flags"; + +// required to avoid linting errors when there are no flags +/* eslint-disable-next-line @typescript-eslint/ban-types */ +export type Flags = {} & SharedFlags; + +// required to avoid linting errors when there are no flags +/* eslint-disable-next-line @typescript-eslint/ban-types */ +export type DevFlags = {} & SharedDevFlags; + +export function flagEnabled(flag: keyof Flags): boolean { + return baseFlagEnabled(flag); +} + +export function devFlagEnabled(flag: keyof DevFlags) { + return baseDevFlagEnabled(flag); +} + +export function devFlagValue(flag: keyof DevFlags) { + return baseDevFlagValue(flag); +} diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 347028a59fa..42fdf47753b 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1980,16 +1980,16 @@ "message": "API-sleutel" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Premie-intekening word vereis" }, "organizationIsDisabled": { "message": "Organisasie is gedeaktiveer." }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Items in gedeaktiveerde organisasies is ontoeganklik. Kontak u organisasie-eienaar vir bystand." }, "neverLockWarning": { - "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + "message": "Is u seker u wil die “Nooit”-opsie gebruik? Deur u vergrendelopsies op “Nooit” te stel word u kluis se enkripsie op u toestel bewaar. Indien u hierdie opsie gebruik moet u verseker dat u toestel behoorlik beskerm is." }, "cardBrandMir": { "message": "Mir" diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index fcc68d0b2dd..1f8eacd1d55 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1394,19 +1394,19 @@ "message": "قفل مع كلمة المرور الرئيسية عند إعادة تشغيل" }, "deleteAccount": { - "message": "Delete account" + "message": "حذف الحساب" }, "deleteAccountDesc": { - "message": "Proceed below to delete your account and all vault data." + "message": "تابع أدناه لحذف حسابك وجميع بيانات خزنتك." }, "deleteAccountWarning": { - "message": "Deleting your account is permanent. It cannot be undone." + "message": "حذف حسابك دائم. لا يمكن التراجع عنه." }, "accountDeleted": { - "message": "Account deleted" + "message": "تم حذف الحساب" }, "accountDeletedDesc": { - "message": "Your account has been closed and all associated data has been deleted." + "message": "لقد تم إغلاق حسابك وتم حذف جميع البيانات المرتبطة به." }, "preferences": { "message": "التفضيلات" @@ -1980,7 +1980,7 @@ "message": "مفتاح API" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "الاشتراك المميز مطلوب" }, "organizationIsDisabled": { "message": "تم تعطيل المؤسسة." diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 28a9676d03e..42f68a95699 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -361,7 +361,7 @@ "message": "Выдаліць далучэнне" }, "deleteItemConfirmation": { - "message": "Вы ўпэўнены, што хочаце выдаліць гэты элемент?" + "message": "Вы сапраўды хочаце адправіць элемент у сметніцу?" }, "deletedItem": { "message": "Элемент адпраўлены ў сметніцу" @@ -560,7 +560,7 @@ "message": "Няма элементаў для паказу." }, "sendVerificationCode": { - "message": "Адправіць код праверкі на вашу электронную пошту" + "message": "Адправіць праверачны код на вашу электронную пошту" }, "sendCode": { "message": "Адправіць код" @@ -608,7 +608,7 @@ "message": "Запомніць мяне" }, "sendVerificationCodeEmailAgain": { - "message": "Адправіць код пацвярджэння зноў" + "message": "Адправіць праверачны код яшчэ раз" }, "useAnotherTwoStepMethod": { "message": "Выкарыстоўваць іншы метад двухэтапнага ўваходу" @@ -1305,26 +1305,26 @@ "message": "Гатова" }, "accessibilityCookieSaved": { - "message": "Accessibility cookie saved!" + "message": "Cookie са спецыяльнымі магчымасцямі захаваны!" }, "noAccessibilityCookieSaved": { - "message": "No accessibility cookie saved" + "message": "Адсутнічаюць захаваныя cookie са спецыяльнымі магчымасцямі" }, "warning": { "message": "УВАГА", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Confirm Vault Export" + "message": "Пацвердзіць экспартаванне сховішча" }, "exportWarningDesc": { "message": "Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання." }, "encExportKeyWarningDesc": { - "message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file." + "message": "Падчас экспартавання даныя шыфруюцца з дапамогай ключа шыфравання ўліковага запісу. Калі вы калі-небудзь зменіце ключ шыфравання ўліковага запісу, вам неабходна будзе экспартаваць даныя паўторна, паколькі вы не зможаце расшыфраваць гэты файл экспартавання." }, "encExportAccountWarningDesc": { - "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + "message": "Ключы шыфравання з'яўляюцца ўнікальнымі для кожнага ўліковага запісу Bitwarden, таму нельга імпартаваць зашыфраванае сховішча ў іншы ўліковы запіс." }, "noOrganizationsList": { "message": "Вы не з'яўляецеся членам якой-небудзь арганізацыі. Арганізацыі дазваляюць бяспечна абменьвацца элементамі з іншымі карыстальнікамі." @@ -1385,16 +1385,16 @@ "message": "Праверыць на Bitwarden." }, "autoPromptWindowsHello": { - "message": "Ask for Windows Hello on launch" + "message": "Запытваць Windows Hello пры запуску" }, "autoPromptTouchId": { - "message": "Ask for Touch ID on launch" + "message": "Запытваць Touch ID пры запуску" }, "lockWithMasterPassOnRestart": { "message": "Блакіраваць асноўным паролем пры перазапуску" }, "deleteAccount": { - "message": "Delete account" + "message": "Выдаліць уліковы запіс" }, "deleteAccountDesc": { "message": "Proceed below to delete your account and all vault data." @@ -1613,7 +1613,7 @@ "message": "An organization policy is affecting your ownership options." }, "allSends": { - "message": "Усе адпраўленні", + "message": "Усе Send'ы", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeFile": { @@ -1623,11 +1623,11 @@ "message": "Тэкст" }, "searchSends": { - "message": "Пошук адпраўленняў", + "message": "Пошук Send'аў", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { - "message": "Рэдагаваць адпраўленне", + "message": "Рэдагаваць Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "myVault": { @@ -1640,7 +1640,7 @@ "message": "Дата выдалення" }, "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", + "message": "Send будзе незваротна выдалены ў азначаныя дату і час.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -1651,7 +1651,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCount": { - "message": "Maximum Access Count", + "message": "Максімальная колькасць доступаў", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "maxAccessCountDesc": { @@ -1674,27 +1674,27 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Даслаць спасылку", + "message": "Адправіць спасылку", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send Link", + "message": "Адправіць спасылку", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { - "message": "When accessing the Send, hide the text by default", + "message": "Прадвызначана хаваць тэкст, калі адбываецца доступ да Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Created Send", + "message": "Створаны Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Edited Send", + "message": "Адрэдагаваны Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "Deleted Send", + "message": "Выдалены Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { @@ -1705,7 +1705,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "Стварыць адпраўленне", + "message": "Стварыць Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { @@ -1734,7 +1734,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "Скапіяваць спасылку адпраўлення ў буфер абмену", + "message": "Капіяваць спасылку на Send у буфер абмену", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 3b388f1312f..5083a5060ee 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -1980,7 +1980,7 @@ "message": "API-nøgle" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Premium-abonnement kræves" }, "organizationIsDisabled": { "message": "Organisationen er deaktiveret." diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index d85be199e02..11c3390bb6f 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -587,7 +587,7 @@ "message": "Gib den 6-stelligen Verifizierungscode aus deiner Authentifizierungs-App ein." }, "enterVerificationCodeEmail": { - "message": "Gib den 6-stelligen Bestätigungscode ein, der an $EMAIL$ gesendet wurde.", + "message": "Gib den 6-stelligen Verifizierungscode ein, der an $EMAIL$ gesendet wurde.", "placeholders": { "email": { "content": "$1", @@ -988,7 +988,7 @@ "description": "Copy to clipboard" }, "checkForUpdates": { - "message": "Auf Updates prüfen" + "message": "Auf Updates prüfen …" }, "version": { "message": "Version $VERSION_NUM$", @@ -1803,7 +1803,7 @@ "message": "Minuten" }, "vaultTimeoutPolicyInEffect": { - "message": "Deine Unternehmensrichtlinien beeinflussen dein Tresor-Timeout. Das maximal zulässige Tresor-Timeout ist $HOURS$ Stunde(n) und $MINUTES$ Minute(n)", + "message": "Die Unternehmensrichtlinien beeinflussen deinen Tresor-Timeout. Das maximal zulässige Tresor-Timeout ist $HOURS$ Stunde(n) und $MINUTES$ Minute(n)", "placeholders": { "hours": { "content": "$1", @@ -1864,7 +1864,7 @@ "message": "Alle Tresore sperren" }, "accountLimitReached": { - "message": "Es dürfen nicht mehr als 5 Konten gleichzeitig angemeldet sein." + "message": "Es dürfen nicht mehr als fünf Konten gleichzeitig angemeldet sein." }, "accountPreferences": { "message": "Einstellungen" @@ -1921,7 +1921,7 @@ "message": "Passworttyp" }, "regenerateUsername": { - "message": "Benutzername neu generieren" + "message": "Benutzernamen neu generieren" }, "generateUsername": { "message": "Benutzernamen generieren" @@ -1958,7 +1958,7 @@ "message": "Alle Tresore" }, "searchOrganization": { - "message": "Organisationen durchsuchen" + "message": "Organisation durchsuchen" }, "searchMyVault": { "message": "Meinen Tresor durchsuchen" diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 4bc416e01b9..472ff720952 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -145,7 +145,7 @@ "message": "Brand" }, "expiration": { - "message": "Validoperiodo" + "message": "Eksvalidiĝo" }, "securityCode": { "message": "Kodo de sekureco" diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index bf6f57bd2de..e871c6e1c36 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -1980,7 +1980,7 @@ "message": "Clave API" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Se requiere una Suscripción Premium" }, "organizationIsDisabled": { "message": "La organización está desactivada." diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index b6c656a4ace..c3bcfa61b0d 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -560,7 +560,7 @@ "message": "Ei näytettäviä kohteita." }, "sendVerificationCode": { - "message": "Lähetä vahvistuskoodi sähköpostiisi" + "message": "Lähetä todennuskoodi sähköpostiisi" }, "sendCode": { "message": "Lähetä koodi" @@ -578,7 +578,7 @@ "message": "Todennuskoodi vaaditaan." }, "invalidVerificationCode": { - "message": "Virheellinen vahvistuskoodi" + "message": "Virheellinen todennuskoodi" }, "continue": { "message": "Jatka" diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index e18492558a1..86f16ebeb96 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -533,13 +533,13 @@ "message": "Adresse e-mail invalide." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Le mot de passe maître est requis." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Le mot de passe maître doit être entré de nouveau." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Le mot de passe maître doit au moins contenir 8 caractères." }, "masterPassDoesntMatch": { "message": "La confirmation du mot de passe maître ne correspond pas." @@ -898,10 +898,10 @@ "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { - "message": "Show website icons" + "message": "Afficher les icônes du site web" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Afficher une image reconnaissable à côté de chaque identifiant." }, "enableMinToTray": { "message": "Réduire dans la barre d'outils" @@ -1394,19 +1394,19 @@ "message": "Verrouiller avec le mot de passe maître lors du redémarrage" }, "deleteAccount": { - "message": "Delete account" + "message": "Supprimer le compte" }, "deleteAccountDesc": { - "message": "Proceed below to delete your account and all vault data." + "message": "Continuez ci-dessous pour supprimer votre compte et toutes les données associées." }, "deleteAccountWarning": { - "message": "Deleting your account is permanent. It cannot be undone." + "message": "La suppression de votre compte est définitive. Cette action ne peut pas être annulée." }, "accountDeleted": { - "message": "Account deleted" + "message": "Compte supprimé" }, "accountDeletedDesc": { - "message": "Your account has been closed and all associated data has been deleted." + "message": "Votre compte a été fermé et toutes les données associées ont été supprimées." }, "preferences": { "message": "Préférences" @@ -1562,7 +1562,7 @@ "message": "En cochant cette case, vous acceptez les éléments suivants :" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Les Conditions d'Utilisation et la Politique de Confidentialité n'ont pas été acceptées." }, "enableBrowserIntegration": { "message": "Activer l'intégration avec le navigateur" @@ -1980,16 +1980,16 @@ "message": "Clé d'IPA" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Abonnement Premium requis" }, "organizationIsDisabled": { - "message": "Organization is disabled." + "message": "L'organisation est désactivée." }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Les éléments des Organisations désactivées ne sont pas accessibles. Contactez le propriétaire de votre Organisation pour obtenir de l'aide." }, "neverLockWarning": { - "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + "message": "Êtes-vous sûr de vouloir utiliser l'option \"Jamais\" ? Définir le verrouillage sur \"Jamais\" stocke la clé de chiffrement de votre coffre sur votre appareil. Si vous utilisez cette option, vous devez vous assurer de correctement protéger votre appareil." }, "cardBrandMir": { "message": "Mir" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 6871aeea05d..5d12b9b62e2 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1403,7 +1403,7 @@ "message": "Usunięcie konta jest nieodwracalne. Ta czynność nie może zostać cofnięta." }, "accountDeleted": { - "message": "Konto usunięte" + "message": "Konto zostało usunięte" }, "accountDeletedDesc": { "message": "Konto zostało zamknięte i wszystkie powiązane z nim dane zostały usunięte." diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 4b2c1931eaf..5f15af3dc3d 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1980,7 +1980,7 @@ "message": "АПИ Кључ" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Premium претплата је потребна" }, "organizationIsDisabled": { "message": "Организација је онемогућена." diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 4c27a385e01..f439ddd8817 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -12,7 +12,7 @@ "message": "รายการโปรด" }, "types": { - "message": "Types" + "message": "ประเภท" }, "typeLogin": { "message": "เข้าสู่ระบบ" @@ -42,13 +42,13 @@ "message": "แบ่งปัน" }, "share": { - "message": "Share" + "message": "แชร์" }, "moveToOrganization": { - "message": "Move to Organization" + "message": "ย้ายไปที่องค์กร" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ ย้ายไปที่ $ORGNAME$ แล้ว", "placeholders": { "itemname": { "content": "$1", @@ -61,10 +61,10 @@ } }, "moveToOrgDesc": { - "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." + "message": "เลือกองค์กรที่คุณต้องการย้ายรายการนี้ไป การย้ายไปยังองค์กรจะเป็นการโอนความเป็นเจ้าของรายการไปยังองค์กรนั้น คุณจะไม่ได้เป็นเจ้าของโดยตรงของรายการนี้อีกต่อไปเมื่อมีการย้าย" }, "attachments": { - "message": "Attachments" + "message": "ไฟล์แนบ" }, "viewItem": { "message": "ดูรายการ" @@ -86,7 +86,7 @@ } }, "newUri": { - "message": "New URI" + "message": "URL ใหม่" }, "username": { "message": "ชื่อผู้ใช้" @@ -110,13 +110,13 @@ "message": "เว็บไซต์" }, "notes": { - "message": "Notes" + "message": "โน๊ต" }, "customFields": { "message": "Custom Fields" }, "launch": { - "message": "Launch" + "message": "เริ่ม" }, "copyValue": { "message": "คัดลอกค่า", @@ -269,7 +269,7 @@ "message": "นามสกุล" }, "fullName": { - "message": "Full Name" + "message": "ชื่อเต็ม" }, "address1": { "message": "ที่อยู่ 1" @@ -401,16 +401,16 @@ "message": "ความยาว" }, "uppercase": { - "message": "Uppercase (A-Z)" + "message": "ตัวพิมพ์ใหญ่ (A-Z)" }, "lowercase": { - "message": "Lowercase (a-z)" + "message": "ตัวพิมพ์เล็ก (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "หมายเลข (0-9)" }, "specialCharacters": { - "message": "Special Characters (!@#$%^&*)" + "message": "อักขระพิเศษ (!@#$%^&*)" }, "numWords": { "message": "Number of Words" @@ -419,7 +419,7 @@ "message": "Word Separator" }, "capitalize": { - "message": "Capitalize", + "message": "ขึ้นต้นด้วยตัวพิมพ์ใหญ่", "description": "Make the first letter of a work uppercase." }, "includeNumber": { @@ -563,19 +563,19 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send Code" + "message": "ส่งรหัส" }, "codeSent": { - "message": "Code Sent" + "message": "ส่งรหัสแล้ว" }, "verificationCode": { "message": "รหัสยืนยัน" }, "confirmIdentity": { - "message": "Confirm your identity to continue." + "message": "ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ" }, "verificationCodeRequired": { - "message": "Verification code is required." + "message": "จำเป็นต้องมีรหัสการตรวจสอบยืนยัน" }, "invalidVerificationCode": { "message": "Invalid verification code" @@ -758,16 +758,16 @@ "message": "ตัวสร้างรหัสผ่าน" }, "contactUs": { - "message": "Contact Us" + "message": "ติดต่อเรา" }, "getHelp": { - "message": "Get Help" + "message": "ขอความช่วยเหลือ" }, "fileBugReport": { "message": "File a Bug Report" }, "blog": { - "message": "Blog" + "message": "บล็อก" }, "followUs": { "message": "Follow Us" @@ -779,7 +779,7 @@ "message": "เปลี่ยนรหัสผ่านหลัก" }, "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "คุณสามารถเปลี่ยนรหัสผ่านหลักของคุณได้ที่เว็บ bitwarden.com คุณต้องการไปที่เว็บไซต์ตอนนี้ไหม?" }, "fingerprintPhrase": { "message": "Fingerprint Phrase", @@ -799,19 +799,19 @@ "message": "Get Browser Extension" }, "syncingComplete": { - "message": "Syncing complete" + "message": "การซิงก์เสร็จสมบูรณ์" }, "syncingFailed": { - "message": "Syncing failed" + "message": "การซิงก์ล้มเหลว" }, "yourVaultIsLocked": { - "message": "Your vault is locked. Verify your identity to continue." + "message": "ตู้เซฟของคุณถูกล็อก ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ" }, "unlock": { - "message": "Unlock" + "message": "ปลดล็อค" }, "loggedInAsOn": { - "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "message": "ล็อกอินด้วย $EMAIL$ บน $HOSTNAME$", "placeholders": { "email": { "content": "$1", @@ -824,10 +824,10 @@ } }, "invalidMasterPassword": { - "message": "Invalid master password" + "message": "รหัสผ่านหลักไม่ถูกต้อง" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "การเข้าสู่ระบบแบบสองขั้นตอนทำให้บัญชีของคุณมีความปลอดภัยมากขึ้นด้วยการให้คุณตรวจสอบการเข้าสู่ระบบของคุณกับอุปกรณ์อื่นเช่นคีย์ความปลอดภัย, แอพ authenticator, SMS, โทรศัพท์หรืออีเมล. เข้าสู่ระบบแบบสองขั้นตอนสามารถเปิดใช้งานบน เว็บนิรภัย bitwarden.com คุณต้องการเยี่ยมชมเว็บไซต์เดี๋ยวนี้หรือไม่" }, "twoStepLogin": { "message": "เข้าสู่ระบบแบบสองขั้นตอน" @@ -842,19 +842,19 @@ "message": "ทันที" }, "tenSeconds": { - "message": "10 seconds" + "message": "10 วินาที" }, "twentySeconds": { - "message": "20 seconds" + "message": "20 วินาที" }, "thirtySeconds": { - "message": "30 seconds" + "message": "30 วินาที" }, "oneMinute": { "message": "1 นาที" }, "twoMinutes": { - "message": "2 minutes" + "message": "2 นาที" }, "fiveMinutes": { "message": "5 นาที" @@ -884,13 +884,13 @@ "message": "On Restart" }, "never": { - "message": "Never" + "message": "ไม่อีกเลย" }, "security": { - "message": "Security" + "message": "ความปลอดภัย" }, "clearClipboard": { - "message": "Clear clipboard", + "message": "ล้างคลิปบอร์ด", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { @@ -898,7 +898,7 @@ "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { - "message": "Show website icons" + "message": "โชว์ไอคอนเว็บไซต์" }, "faviconDesc": { "message": "Show a recognizable image next to each login." @@ -964,23 +964,23 @@ "message": "Disabling this setting will also disable all other tray related settings." }, "language": { - "message": "Language" + "message": "ภาษา" }, "languageDesc": { - "message": "Change the language used by the application. Restart is required." + "message": "การเปลี่ยนภาษาของแอพพลิเคชั่น จำเป็นต้องรีสตาร์ท" }, "theme": { - "message": "Theme" + "message": "ธีม" }, "themeDesc": { "message": "Change the application's color theme." }, "dark": { - "message": "Dark", + "message": "มืด", "description": "Dark color" }, "light": { - "message": "Light", + "message": "สว่าง", "description": "Light color" }, "copy": { @@ -1015,7 +1015,7 @@ "message": "Update Available" }, "updateAvailableDesc": { - "message": "An update was found. Do you want to download it now?" + "message": "ตรวจพบการอัปเดต คุณต้องการดาวโหลดตอนนี้เลยไหม?" }, "restart": { "message": "เริ่มต้นใหม่" @@ -1030,7 +1030,7 @@ "message": "เกิดข้อผิดพลาดในการอัปเดต" }, "unknown": { - "message": "Unknown" + "message": "ไม่ทราบ" }, "copyUsername": { "message": "คัดลอกชื่อผู้ใช้" @@ -1056,10 +1056,10 @@ "message": "Refresh Membership" }, "premiumNotCurrentMember": { - "message": "You are not currently a premium member." + "message": "คุณยังไม่ได้เป็นสมาชิกระดับพรีเมียม" }, "premiumSignUpAndGet": { - "message": "Sign up for a premium membership and get:" + "message": "สมัครสมาชิกพรีเมี่ยมและรับ:" }, "premiumSignUpStorage": { "message": "1 GB of encrypted file storage." @@ -1259,11 +1259,11 @@ "message": "Hide to Tray" }, "alwaysOnTop": { - "message": "Always on Top", + "message": "อยู่ด้านบนเสมอ", "description": "Application window should always stay on top of other windows" }, "dateUpdated": { - "message": "Updated", + "message": "อัปเดตแล้ว", "description": "ex. Date this item was updated" }, "datePasswordUpdated": { @@ -1299,10 +1299,10 @@ "description": "hCaptcha is the name of a website, should not be translated" }, "invalidUrl": { - "message": "Invalid Url" + "message": "Url ไม่ถูกต้อง" }, "done": { - "message": "Done" + "message": "เสร็จสิ้น" }, "accessibilityCookieSaved": { "message": "Accessibility cookie saved!" @@ -1311,7 +1311,7 @@ "message": "No accessibility cookie saved" }, "warning": { - "message": "WARNING", + "message": "คำเตือน", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { @@ -1333,21 +1333,21 @@ "message": "There are no collections to list." }, "ownership": { - "message": "Ownership" + "message": "ความเป็นเจ้าของ" }, "whoOwnsThisItem": { - "message": "Who owns this item?" + "message": "ใครเป็นเจ้าของรายการนี้?" }, "strong": { - "message": "Strong", + "message": "แข็งแรง", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Good", + "message": "ไม่เลว", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "Weak", + "message": "ง่ายเกินไป", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { @@ -1361,19 +1361,19 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "ปลดล็อกด้วย PIN" }, "setYourPinCode": { "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." }, "pinRequired": { - "message": "PIN code is required." + "message": "กรุณาใส่รหัส PIN" }, "invalidPin": { - "message": "Invalid PIN code." + "message": "PIN ไม่ถูกต้อง" }, "unlockWithWindowsHello": { - "message": "Unlock with Windows Hello" + "message": "ปลดล็อก ด้วย Windows Hello" }, "windowsHelloConsentMessage": { "message": "Verify for Bitwarden." @@ -1394,7 +1394,7 @@ "message": "Lock with master password on restart" }, "deleteAccount": { - "message": "Delete account" + "message": "ลบบัญชีผู้ใช้" }, "deleteAccountDesc": { "message": "Proceed below to delete your account and all vault data." @@ -1403,13 +1403,13 @@ "message": "Deleting your account is permanent. It cannot be undone." }, "accountDeleted": { - "message": "Account deleted" + "message": "ลบบัญชีผู้ใช้แล้ว" }, "accountDeletedDesc": { "message": "Your account has been closed and all associated data has been deleted." }, "preferences": { - "message": "Preferences" + "message": "การตั้งค่า" }, "enableMenuBar": { "message": "Show menu bar icon" @@ -1437,7 +1437,7 @@ "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." }, "termsOfService": { - "message": "Terms of Service" + "message": "เงื่อนไขการใช้บริการ" }, "privacyPolicy": { "message": "Privacy Policy" @@ -1505,7 +1505,7 @@ "message": "Enterprise Single Sign-On" }, "setMasterPassword": { - "message": "Set Master Password" + "message": "ตั้งรหัสผ่านหลัก" }, "ssoCompleteRegistration": { "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." @@ -1514,7 +1514,7 @@ "message": "New Master Password" }, "confirmNewMasterPass": { - "message": "Confirm New Master Password" + "message": "ยืนยันรหัสผ่านใหม่" }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" @@ -1589,7 +1589,7 @@ "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." }, "approve": { - "message": "Approve" + "message": "อนุมัติ" }, "verifyBrowserTitle": { "message": "Verify browser connection" @@ -1613,14 +1613,14 @@ "message": "An organization policy is affecting your ownership options." }, "allSends": { - "message": "All Sends", + "message": "ส่งทั้งหมด", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeFile": { - "message": "File" + "message": "ไฟล์" }, "sendTypeText": { - "message": "Text" + "message": "ข้อความ" }, "searchSends": { "message": "Search Sends", @@ -1631,13 +1631,13 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "myVault": { - "message": "My Vault" + "message": "ตู้เซฟของฉัน" }, "text": { - "message": "Text" + "message": "ข้อความ" }, "deletionDate": { - "message": "Deletion Date" + "message": "ถูกลบเมื่อวันที่" }, "deletionDateDesc": { "message": "The Send will be permanently deleted on the specified date and time.", @@ -1674,7 +1674,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Send link", + "message": "ส่งลิงก์", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { @@ -1715,7 +1715,7 @@ "message": "The file you want to send." }, "days": { - "message": "$DAYS$ days", + "message": "$DAYS$ วัน", "placeholders": { "days": { "content": "$1", diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index eef6c908477..a840594b06e 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -61,7 +61,7 @@ } }, "moveToOrgDesc": { - "message": "Виберіть організацію, до якої ви бажаєте перемістити цей запис. При переміщенні до організації власність запису передається тій організації. Ви більше не будете єдиним власником цього запису після переміщення." + "message": "Виберіть організацію, до якої ви бажаєте перемістити цей запис. Переміщуючи до організації, власність запису передається тій організації. Ви більше не будете єдиним власником цього запису після переміщення." }, "attachments": { "message": "Вкладення" diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 55c7fb688c5..4d1ac739ef6 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -104,7 +104,7 @@ export class Main { this.updaterMain = new UpdaterMain( this.i18nService, this.windowMain, - "desktop", + "clients", null, null, null, diff --git a/apps/desktop/src/services/i18n.service.ts b/apps/desktop/src/services/i18n.service.ts index 7f130faee3d..4a05981e338 100644 --- a/apps/desktop/src/services/i18n.service.ts +++ b/apps/desktop/src/services/i18n.service.ts @@ -34,6 +34,7 @@ export class I18nService extends BaseI18nService { "eo", "es", "et", + "eu", "fa", "fi", "fil", diff --git a/apps/web/README.md b/apps/web/README.md index 569f220fdf9..1193b139a4d 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -5,8 +5,8 @@ The Bitwarden web project is an Angular application that powers the web vault (https://vault.bitwarden.com/).

- - Github Workflow build on master + + Github Workflow build on master Crowdin diff --git a/apps/web/src/app/accounts/accept-organization.component.ts b/apps/web/src/app/accounts/accept-organization.component.ts index 1e9e8e32aa0..ae24498ddeb 100644 --- a/apps/web/src/app/accounts/accept-organization.component.ts +++ b/apps/web/src/app/accounts/accept-organization.component.ts @@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; @@ -34,7 +35,8 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { private cryptoService: CryptoService, private policyApiService: PolicyApiServiceAbstraction, private policyService: PolicyService, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) { super(router, platformUtilsService, i18nService, route, stateService); } @@ -74,7 +76,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { request.token = qParams.token; if (await this.performResetPasswordAutoEnroll(qParams)) { - const response = await this.apiService.getOrganizationKeys(qParams.organizationId); + const response = await this.organizationApiService.getKeys(qParams.organizationId); if (response == null) { throw new Error(this.i18nService.t("resetPasswordOrgKeysError")); @@ -92,7 +94,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { return request; } - private async performResetPasswordAutoEnroll(qParams: any): Promise { + private async performResetPasswordAutoEnroll(qParams: Params): Promise { let policyList: Policy[] = null; try { const policies = await this.policyApiService.getPoliciesByToken( diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login.component.ts index 30b09246b06..6a13ff32337 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login.component.ts @@ -26,6 +26,7 @@ import { RouterService, StateService } from "../core"; selector: "app-login", templateUrl: "login.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class LoginComponent extends BaseLoginComponent { showResetPasswordAutoEnrollWarning = false; enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions; @@ -68,6 +69,7 @@ export class LoginComponent extends BaseLoginComponent { } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.email != null && qParams.email.indexOf("@") > -1) { this.email = qParams.email; diff --git a/apps/web/src/app/accounts/register.component.ts b/apps/web/src/app/accounts/register.component.ts index 3f5ce3bf7d6..4e700ca4d40 100644 --- a/apps/web/src/app/accounts/register.component.ts +++ b/apps/web/src/app/accounts/register.component.ts @@ -27,6 +27,7 @@ import { RouterService } from "../core"; selector: "app-register", templateUrl: "register.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class RegisterComponent extends BaseRegisterComponent { email = ""; showCreateOrgMessage = false; @@ -70,6 +71,7 @@ export class RegisterComponent extends BaseRegisterComponent { } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.route.queryParams.pipe(first()).subscribe((qParams) => { this.referenceData = new ReferenceEventRequest(); if (qParams.email != null && qParams.email.indexOf("@") > -1) { diff --git a/apps/web/src/app/accounts/set-password.component.ts b/apps/web/src/app/accounts/set-password.component.ts index 5b7bc2ec61f..771a71affa4 100644 --- a/apps/web/src/app/accounts/set-password.component.ts +++ b/apps/web/src/app/accounts/set-password.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; @@ -30,7 +31,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { router: Router, syncService: SyncService, route: ActivatedRoute, - stateService: StateService + stateService: StateService, + organizationApiService: OrganizationApiServiceAbstraction ) { super( i18nService, @@ -44,7 +46,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { apiService, syncService, route, - stateService + stateService, + organizationApiService ); } } diff --git a/apps/web/src/app/accounts/sso.component.ts b/apps/web/src/app/accounts/sso.component.ts index 35a9458cd4a..111d5f2a366 100644 --- a/apps/web/src/app/accounts/sso.component.ts +++ b/apps/web/src/app/accounts/sso.component.ts @@ -17,6 +17,7 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; selector: "app-sso", templateUrl: "sso.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class SsoComponent extends BaseSsoComponent { constructor( authService: AuthService, @@ -50,6 +51,7 @@ export class SsoComponent extends BaseSsoComponent { async ngOnInit() { super.ngOnInit(); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.identifier != null) { this.identifier = qParams.identifier; diff --git a/apps/web/src/app/accounts/trial-initiation/billing.component.ts b/apps/web/src/app/accounts/trial-initiation/billing.component.ts index 6f02000b958..3cff0c82d29 100644 --- a/apps/web/src/app/accounts/trial-initiation/billing.component.ts +++ b/apps/web/src/app/accounts/trial-initiation/billing.component.ts @@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { SyncService } from "@bitwarden/common/abstractions/sync.service"; @@ -34,7 +35,8 @@ export class BillingComponent extends OrganizationPlansComponent { organizationService: OrganizationService, logService: LogService, messagingService: MessagingService, - formBuilder: UntypedFormBuilder + formBuilder: UntypedFormBuilder, + organizationApiService: OrganizationApiServiceAbstraction ) { super( apiService, @@ -47,7 +49,8 @@ export class BillingComponent extends OrganizationPlansComponent { organizationService, logService, messagingService, - formBuilder + formBuilder, + organizationApiService ); } diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.html new file mode 100644 index 00000000000..b5c16911ab0 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.html @@ -0,0 +1,17 @@ +

Start Your Enterprise Free Trial Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Collaborate and share securely
  • +
  • Deploy and manage quickly and easily
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.ts new file mode 100644 index 00000000000..4a6de8d3003 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-enterprise-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-cnet-enterprise-content", + templateUrl: "cnet-enterprise-content.component.html", +}) +export class CnetEnterpriseContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.html new file mode 100644 index 00000000000..6e6f545c170 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.html @@ -0,0 +1,17 @@ +

Start Your Premium Account Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Store logins, secure notes, and more
  • +
  • Secure your account with advanced two-step login
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.ts new file mode 100644 index 00000000000..56d8b37af90 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-individual-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-cnet-individual-content", + templateUrl: "cnet-individual-content.component.html", +}) +export class CnetIndividualContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.html new file mode 100644 index 00000000000..c719c5ac7ce --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.html @@ -0,0 +1,17 @@ +

Start Your Teams Free Trial Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Collaborate and share securely
  • +
  • Deploy and manage quickly and easily
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.ts new file mode 100644 index 00000000000..ff79a0d37cd --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/cnet-teams-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-cnet-teams-content", + templateUrl: "cnet-teams-content.component.html", +}) +export class CnetTeamsContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/default-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/default-content.component.html new file mode 100644 index 00000000000..46e1fae80df --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/default-content.component.html @@ -0,0 +1,17 @@ +

The Bitwarden Password Manager

+
+

+ Trusted by millions of individuals, teams, and organizations worldwide for secure password + storage and sharing. +

+
+
    +
  • Store logins, secure notes, and more
  • +
  • Collaborate and share securely
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/default-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/default-content.component.ts new file mode 100644 index 00000000000..7ad40b089d1 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/default-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-default-content", + templateUrl: "default-content.component.html", +}) +export class DefaultContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/enterprise-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/enterprise-content.component.html new file mode 100644 index 00000000000..46e1fae80df --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/enterprise-content.component.html @@ -0,0 +1,17 @@ +

The Bitwarden Password Manager

+
+

+ Trusted by millions of individuals, teams, and organizations worldwide for secure password + storage and sharing. +

+
+
    +
  • Store logins, secure notes, and more
  • +
  • Collaborate and share securely
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/enterprise-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/enterprise-content.component.ts similarity index 100% rename from apps/web/src/app/accounts/trial-initiation/enterprise-content.component.ts rename to apps/web/src/app/accounts/trial-initiation/content/enterprise-content.component.ts diff --git a/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.html new file mode 100644 index 00000000000..46e1fae80df --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.html @@ -0,0 +1,17 @@ +

The Bitwarden Password Manager

+
+

+ Trusted by millions of individuals, teams, and organizations worldwide for secure password + storage and sharing. +

+
+
    +
  • Store logins, secure notes, and more
  • +
  • Collaborate and share securely
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.ts new file mode 100644 index 00000000000..7b1199eb421 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/enterprise1-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-enterprise1-content", + templateUrl: "enterprise1-content.component.html", +}) +export class Enterprise1ContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.html new file mode 100644 index 00000000000..e0739f58a2d --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.html @@ -0,0 +1,17 @@ +

Start Your Enterprise Free Trial Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Collaborate and share securely
  • +
  • Deploy and manage quickly and easily
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.ts new file mode 100644 index 00000000000..08dec6190c7 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/enterprise2-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-enterprise2-content", + templateUrl: "enterprise2-content.component.html", +}) +export class Enterprise2ContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.html b/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.html new file mode 100644 index 00000000000..4e04cec6da4 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.html @@ -0,0 +1,15 @@ +
+
+ + CNET Logo + +
+
+ "No more excuses; start using Bitwarden today. The identity you save could be your own. The + money definitely will be." +
+
diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.ts b/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.ts new file mode 100644 index 00000000000..4f755f66a86 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-cnet.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-logo-cnet", + templateUrl: "logo-cnet.component.html", +}) +export class LogoCnetComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.html b/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.html new file mode 100644 index 00000000000..34426168324 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.html @@ -0,0 +1,15 @@ +
+
+ + Forbes Logo + +
+
+ “Bitwarden boasts the backing of some of the world's best security experts and an attractive, + easy-to-use interface” +
+
diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.ts b/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.ts new file mode 100644 index 00000000000..818721fd1e9 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-forbes.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-logo-forbes", + templateUrl: "logo-forbes.component.html", +}) +export class LogoForbesComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.html b/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.html new file mode 100644 index 00000000000..bd44b56f090 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.html @@ -0,0 +1,5 @@ +US News 360 Reviews Best Password Manager diff --git a/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.ts b/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.ts new file mode 100644 index 00000000000..fb0b1e0c71b --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/logo-us-news.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-logo-us-news", + templateUrl: "logo-us-news.component.html", +}) +export class LogoUSNewsComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/teams-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/teams-content.component.html new file mode 100644 index 00000000000..46e1fae80df --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/teams-content.component.html @@ -0,0 +1,17 @@ +

The Bitwarden Password Manager

+
+

+ Trusted by millions of individuals, teams, and organizations worldwide for secure password + storage and sharing. +

+
+
    +
  • Store logins, secure notes, and more
  • +
  • Collaborate and share securely
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/teams-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/teams-content.component.ts similarity index 100% rename from apps/web/src/app/accounts/trial-initiation/teams-content.component.ts rename to apps/web/src/app/accounts/trial-initiation/content/teams-content.component.ts diff --git a/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.html new file mode 100644 index 00000000000..7819ead3ce5 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.html @@ -0,0 +1,17 @@ +

Start Your Teams Free Trial Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Collaborate and share securely
  • +
  • Deploy and manage quickly and easily
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.ts new file mode 100644 index 00000000000..055ec7fda10 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/teams1-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-teams1-content", + templateUrl: "teams1-content.component.html", +}) +export class Teams1ContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.html b/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.html new file mode 100644 index 00000000000..3145e20d4f8 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.html @@ -0,0 +1,17 @@ +

Start Your Free Trial Now

+
+

+ Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password + storage and sharing. +

+
+
    +
  • Collaborate and share securely
  • +
  • Deploy and manage quickly and easily
  • +
  • Access anywhere on any device
  • +
  • Create your account to get started
  • +
+
+ + +
diff --git a/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.ts b/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.ts new file mode 100644 index 00000000000..394ba90b491 --- /dev/null +++ b/apps/web/src/app/accounts/trial-initiation/content/teams2-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-teams2-content", + templateUrl: "teams2-content.component.html", +}) +export class Teams2ContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/enterprise-content.component.html b/apps/web/src/app/accounts/trial-initiation/enterprise-content.component.html deleted file mode 100644 index 239c25fdc91..00000000000 --- a/apps/web/src/app/accounts/trial-initiation/enterprise-content.component.html +++ /dev/null @@ -1,11 +0,0 @@ -

You've chosen Bitwarden for Enterprise

-
-

What you can do with Bitwarden for Enterprise

-
- -
-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
diff --git a/apps/web/src/app/accounts/trial-initiation/families-content.component.html b/apps/web/src/app/accounts/trial-initiation/families-content.component.html deleted file mode 100644 index c0c6af0be00..00000000000 --- a/apps/web/src/app/accounts/trial-initiation/families-content.component.html +++ /dev/null @@ -1,13 +0,0 @@ -

You've chosen Bitwarden for Families

-
-

- Trusted by millions of individuals, teams, and organizations worldwide for secure password - storage and sharing. -

-
-
-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
diff --git a/apps/web/src/app/accounts/trial-initiation/families-content.component.ts b/apps/web/src/app/accounts/trial-initiation/families-content.component.ts deleted file mode 100644 index 1a13be80e67..00000000000 --- a/apps/web/src/app/accounts/trial-initiation/families-content.component.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component } from "@angular/core"; - -@Component({ - selector: "app-families-content", - templateUrl: "families-content.component.html", -}) -export class FamiliesContentComponent {} diff --git a/apps/web/src/app/accounts/trial-initiation/teams-content.component.html b/apps/web/src/app/accounts/trial-initiation/teams-content.component.html deleted file mode 100644 index 20e547fef13..00000000000 --- a/apps/web/src/app/accounts/trial-initiation/teams-content.component.html +++ /dev/null @@ -1,10 +0,0 @@ -

You've chosen Bitwarden for Teams

-
-

What you can do with Btiwarden for Teams

-
-
-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html index c51a1b0277b..b78468133d5 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html @@ -1,19 +1,18 @@
-

{{ "createAccount" | i18n }}

+

{{ "createAccount" | i18n }}

-
-
+
+
Bitwarden - +
- - - - - - + + + + + + + + + + +
@@ -44,6 +52,7 @@ diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts index 9feda5af50e..f20b407e6bf 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts @@ -18,6 +18,8 @@ import { StateService as BaseStateService } from "@bitwarden/common/abstractions import { PlanType } from "@bitwarden/common/enums/planType"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions"; +import { RouterService } from "../../core"; + import { TrialInitiationComponent } from "./trial-initiation.component"; import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component"; @@ -84,6 +86,10 @@ describe("TrialInitiationComponent", () => { provide: VerticalStepperComponent, useClass: VerticalStepperStubComponent, }, + { + provide: RouterService, + useClass: Substitute.for(), + }, ], schemas: [NO_ERRORS_SCHEMA], // Allows child components to be ignored (such as register component) }).compileComponents(); diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts index 1c452cdf5ea..00bbc6e236f 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts @@ -15,13 +15,16 @@ import { ProductType } from "@bitwarden/common/enums/productType"; import { PolicyData } from "@bitwarden/common/models/data/policyData"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions"; import { Policy } from "@bitwarden/common/models/domain/policy"; +import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest"; +import { RouterService } from "./../../core/router.service"; import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component"; @Component({ selector: "app-trial", templateUrl: "trial-initiation.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class TrialInitiationComponent implements OnInit { email = ""; org = ""; @@ -29,12 +32,14 @@ export class TrialInitiationComponent implements OnInit { orgId = ""; orgLabel = ""; billingSubLabel = ""; + layout = "default"; plan: PlanType; product: ProductType; accountCreateOnly = true; policies: Policy[]; enforcedPolicyOptions: MasterPasswordPolicyOptions; validOrgs: string[] = ["teams", "enterprise", "families"]; + referenceData: ReferenceEventRequest; @ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent; orgInfoFormGroup = this.formBuilder.group({ @@ -42,6 +47,22 @@ export class TrialInitiationComponent implements OnInit { email: [""], }); + private set referenceDataId(referenceId: string) { + if (referenceId != null) { + this.referenceData.id = referenceId; + } else { + this.referenceData.id = ("; " + document.cookie) + .split("; reference=") + .pop() + .split(";") + .shift(); + } + + if (this.referenceData.id === "") { + this.referenceData.id = null; + } + } + constructor( private route: ActivatedRoute, protected router: Router, @@ -51,25 +72,40 @@ export class TrialInitiationComponent implements OnInit { private logService: LogService, private policyApiService: PolicyApiServiceAbstraction, private policyService: PolicyService, - private i18nService: I18nService + private i18nService: I18nService, + private routerService: RouterService ) {} async ngOnInit(): Promise { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.route.queryParams.pipe(first()).subscribe((qParams) => { + this.referenceData = new ReferenceEventRequest(); if (qParams.email != null && qParams.email.indexOf("@") > -1) { this.email = qParams.email; } + this.referenceDataId = qParams.reference; + if (!qParams.org) { return; } + if (qParams.layout) { + this.layout = qParams.layout; + } + if (this.validOrgs.includes(qParams.org)) { this.org = qParams.org; } else { this.org = "families"; } + this.referenceData.flow = qParams.org; + + // Are they coming from an email for sponsoring a families organization + // After logging in redirect them to setup the families sponsorship + this.setupFamilySponsorship(qParams.sponsorshipToken); + this.orgLabel = this.titleCasePipe.transform(this.org); this.accountCreateOnly = false; @@ -148,4 +184,13 @@ export class TrialInitiationComponent implements OnInit { previousStep() { this.verticalStepper.previous(); } + + private setupFamilySponsorship(sponsorshipToken: string) { + if (sponsorshipToken != null) { + const route = this.router.createUrlTree(["setup/families-for-enterprise"], { + queryParams: { plan: sponsorshipToken }, + }); + this.routerService.setPreviousUrl(route.toString()); + } + } } diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.module.ts b/apps/web/src/app/accounts/trial-initiation/trial-initiation.module.ts index 8d0ebbb1a78..fb02824222f 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.module.ts +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.module.ts @@ -10,9 +10,19 @@ import { RegisterFormModule } from "../register-form/register-form.module"; import { BillingComponent } from "./billing.component"; import { ConfirmationDetailsComponent } from "./confirmation-details.component"; -import { EnterpriseContentComponent } from "./enterprise-content.component"; -import { FamiliesContentComponent } from "./families-content.component"; -import { TeamsContentComponent } from "./teams-content.component"; +import { CnetEnterpriseContentComponent } from "./content/cnet-enterprise-content.component"; +import { CnetIndividualContentComponent } from "./content/cnet-individual-content.component"; +import { CnetTeamsContentComponent } from "./content/cnet-teams-content.component"; +import { DefaultContentComponent } from "./content/default-content.component"; +import { EnterpriseContentComponent } from "./content/enterprise-content.component"; +import { Enterprise1ContentComponent } from "./content/enterprise1-content.component"; +import { Enterprise2ContentComponent } from "./content/enterprise2-content.component"; +import { LogoCnetComponent } from "./content/logo-cnet.component"; +import { LogoForbesComponent } from "./content/logo-forbes.component"; +import { LogoUSNewsComponent } from "./content/logo-us-news.component"; +import { TeamsContentComponent } from "./content/teams-content.component"; +import { Teams1ContentComponent } from "./content/teams1-content.component"; +import { Teams2ContentComponent } from "./content/teams2-content.component"; import { TrialInitiationComponent } from "./trial-initiation.component"; import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.module"; @@ -29,10 +39,22 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul declarations: [ TrialInitiationComponent, EnterpriseContentComponent, - FamiliesContentComponent, TeamsContentComponent, ConfirmationDetailsComponent, BillingComponent, + DefaultContentComponent, + EnterpriseContentComponent, + Enterprise1ContentComponent, + Enterprise2ContentComponent, + TeamsContentComponent, + Teams1ContentComponent, + Teams2ContentComponent, + CnetEnterpriseContentComponent, + CnetIndividualContentComponent, + CnetTeamsContentComponent, + LogoCnetComponent, + LogoForbesComponent, + LogoUSNewsComponent, ], exports: [TrialInitiationComponent], providers: [TitleCasePipe], diff --git a/apps/web/src/app/accounts/two-factor.component.ts b/apps/web/src/app/accounts/two-factor.component.ts index 1d9b19af213..5ebf3ea02ed 100644 --- a/apps/web/src/app/accounts/two-factor.component.ts +++ b/apps/web/src/app/accounts/two-factor.component.ts @@ -22,6 +22,7 @@ import { TwoFactorOptionsComponent } from "./two-factor-options.component"; selector: "app-two-factor", templateUrl: "two-factor.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class TwoFactorComponent extends BaseTwoFactorComponent { @ViewChild("twoFactorOptions", { read: ViewContainerRef, static: true }) twoFactorOptionsModal: ViewContainerRef; @@ -63,11 +64,13 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { TwoFactorOptionsComponent, this.twoFactorOptionsModal, (comp) => { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => { modal.close(); this.selectedProviderType = provider; await this.init(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onRecoverSelected.subscribe(() => { modal.close(); }); diff --git a/apps/web/src/app/accounts/verify-email-token.component.ts b/apps/web/src/app/accounts/verify-email-token.component.ts index 90983f7cf00..c0ebfa78d7f 100644 --- a/apps/web/src/app/accounts/verify-email-token.component.ts +++ b/apps/web/src/app/accounts/verify-email-token.component.ts @@ -13,6 +13,7 @@ import { VerifyEmailRequest } from "@bitwarden/common/models/request/verifyEmail selector: "app-verify-email-token", templateUrl: "verify-email-token.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class VerifyEmailTokenComponent implements OnInit { constructor( private router: Router, @@ -25,6 +26,7 @@ export class VerifyEmailTokenComponent implements OnInit { ) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.userId != null && qParams.token != null) { try { diff --git a/apps/web/src/app/accounts/verify-recover-delete.component.ts b/apps/web/src/app/accounts/verify-recover-delete.component.ts index 3deadbd4d47..7139e4d10e7 100644 --- a/apps/web/src/app/accounts/verify-recover-delete.component.ts +++ b/apps/web/src/app/accounts/verify-recover-delete.component.ts @@ -12,6 +12,7 @@ import { VerifyDeleteRecoverRequest } from "@bitwarden/common/models/request/ver selector: "app-verify-recover-delete", templateUrl: "verify-recover-delete.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class VerifyRecoverDeleteComponent implements OnInit { email: string; formPromise: Promise; @@ -29,6 +30,7 @@ export class VerifyRecoverDeleteComponent implements OnInit { ) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.userId != null && qParams.token != null && qParams.email != null) { this.userId = qParams.userId; diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index ef80dc5e736..ae9107ea0ec 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -215,7 +215,7 @@ export class AppComponent implements OnDestroy, OnInit { ngOnDestroy() { this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); this.destroy$.next(); - this.destroy$.unsubscribe(); + this.destroy$.complete(); } private async logOut(expired: boolean) { diff --git a/apps/web/src/app/common/base.accept.component.ts b/apps/web/src/app/common/base.accept.component.ts index 907d7410f8b..dcca9e061c1 100644 --- a/apps/web/src/app/common/base.accept.component.ts +++ b/apps/web/src/app/common/base.accept.component.ts @@ -29,6 +29,7 @@ export abstract class BaseAcceptComponent implements OnInit { abstract unauthedHandler(qParams: Params): Promise; ngOnInit() { + // eslint-disable-next-line rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { let error = this.requiredParameters.some((e) => qParams?.[e] == null || qParams[e] === ""); let errorMessage: string = null; diff --git a/apps/web/src/app/common/base.people.component.ts b/apps/web/src/app/common/base.people.component.ts index b1aab35b322..6b9370ae814 100644 --- a/apps/web/src/app/common/base.people.component.ts +++ b/apps/web/src/app/common/base.people.component.ts @@ -85,7 +85,7 @@ export abstract class BasePeopleComponent< users: UserType[] = []; pagedUsers: UserType[] = []; searchText: string; - actionPromise: Promise; + actionPromise: Promise; protected allUsers: UserType[] = []; protected activeUsers: UserType[] = []; @@ -111,11 +111,11 @@ export abstract class BasePeopleComponent< abstract edit(user: UserType): void; abstract getUsers(): Promise>; - abstract deleteUser(id: string): Promise; - abstract revokeUser(id: string): Promise; - abstract restoreUser(id: string): Promise; - abstract reinviteUser(id: string): Promise; - abstract confirmUser(user: UserType, publicKey: Uint8Array): Promise; + abstract deleteUser(id: string): Promise; + abstract revokeUser(id: string): Promise; + abstract restoreUser(id: string): Promise; + abstract reinviteUser(id: string): Promise; + abstract confirmUser(user: UserType, publicKey: Uint8Array): Promise; async load() { const response = await this.getUsers(); @@ -343,6 +343,7 @@ export abstract class BasePeopleComponent< comp.name = this.userNamePipe.transform(user); comp.userId = user != null ? user.userId : null; comp.publicKey = publicKey; + // eslint-disable-next-line rxjs/no-async-subscribe comp.onConfirmedUser.subscribe(async () => { try { comp.formPromise = confirmUser(publicKey); diff --git a/apps/web/src/app/components/user-verification-prompt.component.html b/apps/web/src/app/components/user-verification-prompt.component.html new file mode 100644 index 00000000000..37e2ce87cf5 --- /dev/null +++ b/apps/web/src/app/components/user-verification-prompt.component.html @@ -0,0 +1,26 @@ + diff --git a/apps/web/src/app/components/user-verification-prompt.component.ts b/apps/web/src/app/components/user-verification-prompt.component.ts new file mode 100644 index 00000000000..e057193affc --- /dev/null +++ b/apps/web/src/app/components/user-verification-prompt.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/components/user-verification-prompt.component"; + +@Component({ + templateUrl: "user-verification-prompt.component.html", +}) +export class UserVerificationPromptComponent extends BaseUserVerificationPrompt {} diff --git a/apps/web/src/app/core/i18n.service.ts b/apps/web/src/app/core/i18n.service.ts index 5d6d40191b8..b60efef36ba 100644 --- a/apps/web/src/app/core/i18n.service.ts +++ b/apps/web/src/app/core/i18n.service.ts @@ -33,6 +33,7 @@ export class I18nService extends BaseI18nService { "eo", "es", "et", + "eu", "fi", "fil", "fr", diff --git a/apps/web/src/app/layouts/navbar.component.ts b/apps/web/src/app/layouts/navbar.component.ts index c5a6b03cad5..bbd4644bdb3 100644 --- a/apps/web/src/app/layouts/navbar.component.ts +++ b/apps/web/src/app/layouts/navbar.component.ts @@ -18,6 +18,7 @@ import { canAccessOrgAdmin } from "../organizations/navigation-permissions"; selector: "app-navbar", templateUrl: "navbar.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class NavbarComponent implements OnInit { selfHosted = false; name: string; diff --git a/apps/web/src/app/organizations/billing/billing-sync-api-key.component.ts b/apps/web/src/app/organizations/billing/billing-sync-api-key.component.ts index 5b36a69e172..d93e1654d5c 100644 --- a/apps/web/src/app/organizations/billing/billing-sync-api-key.component.ts +++ b/apps/web/src/app/organizations/billing/billing-sync-api-key.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; import { OrganizationApiKeyType } from "@bitwarden/common/enums/organizationApiKeyType"; @@ -28,7 +29,8 @@ export class BillingSyncApiKeyComponent { private userVerificationService: UserVerificationService, private apiService: ApiService, private platformUtilsService: PlatformUtilsService, - private i18nService: I18nService + private i18nService: I18nService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} copy() { @@ -41,7 +43,7 @@ export class BillingSyncApiKeyComponent { .buildRequest(this.masterPassword, OrganizationApiKeyRequest) .then((request) => { request.type = OrganizationApiKeyType.BillingSync; - return this.apiService.postOrganizationRotateApiKey(this.organizationId, request); + return this.organizationApiService.rotateApiKey(this.organizationId, request); }); const response = await this.formPromise; await this.load(response); @@ -56,7 +58,7 @@ export class BillingSyncApiKeyComponent { .buildRequest(this.masterPassword, OrganizationApiKeyRequest) .then((request) => { request.type = OrganizationApiKeyType.BillingSync; - return this.apiService.postOrganizationApiKey(this.organizationId, request); + return this.organizationApiService.getOrCreateApiKey(this.organizationId, request); }); const response = await this.formPromise; await this.load(response); diff --git a/apps/web/src/app/organizations/billing/organization-subscription.component.ts b/apps/web/src/app/organizations/billing/organization-subscription.component.ts index dc1dd0a1a4e..cf80ff2af1c 100644 --- a/apps/web/src/app/organizations/billing/organization-subscription.component.ts +++ b/apps/web/src/app/organizations/billing/organization-subscription.component.ts @@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { OrganizationApiKeyType } from "@bitwarden/common/enums/organizationApiKeyType"; import { OrganizationConnectionType } from "@bitwarden/common/enums/organizationConnectionType"; @@ -25,6 +26,7 @@ import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component"; selector: "app-org-subscription", templateUrl: "organization-subscription.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class OrganizationSubscriptionComponent implements OnInit { @ViewChild("setupBillingSyncTemplate", { read: ViewContainerRef, static: true }) setupBillingSyncModalRef: ViewContainerRef; @@ -48,9 +50,9 @@ export class OrganizationSubscriptionComponent implements OnInit { userOrg: Organization; existingBillingSyncConnection: OrganizationConnectionResponse; - removeSponsorshipPromise: Promise; - cancelPromise: Promise; - reinstatePromise: Promise; + removeSponsorshipPromise: Promise; + cancelPromise: Promise; + reinstatePromise: Promise; @ViewChild("rotateBillingSyncKeyTemplate", { read: ViewContainerRef, static: true }) billingSyncKeyViewContainerRef: ViewContainerRef; @@ -64,12 +66,14 @@ export class OrganizationSubscriptionComponent implements OnInit { private route: ActivatedRoute, private organizationService: OrganizationService, private logService: LogService, - private modalService: ModalService + private modalService: ModalService, + private organizationApiService: OrganizationApiServiceAbstraction ) { this.selfHosted = platformUtilsService.isSelfHost(); } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; await this.load(); @@ -85,9 +89,9 @@ export class OrganizationSubscriptionComponent implements OnInit { this.loading = true; this.userOrg = await this.organizationService.get(this.organizationId); if (this.userOrg.canManageBilling) { - this.sub = await this.apiService.getOrganizationSubscription(this.organizationId); + this.sub = await this.organizationApiService.getSubscription(this.organizationId); } - const apiKeyResponse = await this.apiService.getOrganizationApiKeyInformation( + const apiKeyResponse = await this.organizationApiService.getApiKeyInformation( this.organizationId ); this.hasBillingSyncToken = apiKeyResponse.data.some( @@ -125,7 +129,7 @@ export class OrganizationSubscriptionComponent implements OnInit { } try { - this.reinstatePromise = this.apiService.postOrganizationReinstate(this.organizationId); + this.reinstatePromise = this.organizationApiService.reinstate(this.organizationId); await this.reinstatePromise; this.platformUtilsService.showToast("success", null, this.i18nService.t("reinstated")); this.load(); @@ -151,7 +155,7 @@ export class OrganizationSubscriptionComponent implements OnInit { } try { - this.cancelPromise = this.apiService.postOrganizationCancel(this.organizationId); + this.cancelPromise = this.organizationApiService.cancel(this.organizationId); await this.cancelPromise; this.platformUtilsService.showToast( "success", @@ -185,6 +189,7 @@ export class OrganizationSubscriptionComponent implements OnInit { comp.hasBillingToken = this.hasBillingSyncToken; } ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe ref.onClosed.subscribe(async () => { await this.load(); }); diff --git a/apps/web/src/app/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/organizations/layouts/organization-layout.component.ts index 107fef459a9..48fde0d2b9d 100644 --- a/apps/web/src/app/organizations/layouts/organization-layout.component.ts +++ b/apps/web/src/app/organizations/layouts/organization-layout.component.ts @@ -33,6 +33,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { ngOnInit() { document.body.classList.remove("layout_frontend"); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.params.subscribe(async (params: any) => { this.organizationId = params.organizationId; await this.load(); diff --git a/apps/web/src/app/organizations/manage/collections.component.ts b/apps/web/src/app/organizations/manage/collections.component.ts index e935e7b10b0..04867a4eabe 100644 --- a/apps/web/src/app/organizations/manage/collections.component.ts +++ b/apps/web/src/app/organizations/manage/collections.component.ts @@ -27,6 +27,7 @@ import { EntityUsersComponent } from "./entity-users.component"; selector: "app-org-manage-collections", templateUrl: "collections.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class CollectionsComponent implements OnInit { @ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef; @ViewChild("usersTemplate", { read: ViewContainerRef, static: true }) @@ -59,9 +60,11 @@ export class CollectionsComponent implements OnInit { ) {} async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; await this.load(); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { this.searchText = qParams.search; }); @@ -131,10 +134,12 @@ export class CollectionsComponent implements OnInit { comp.collectionId = collection != null ? collection.id : null; comp.canSave = canCreate || canEdit; comp.canDelete = canDelete; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSavedCollection.subscribe(() => { modal.close(); this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDeletedCollection.subscribe(() => { modal.close(); this.removeCollection(collection); @@ -183,6 +188,7 @@ export class CollectionsComponent implements OnInit { comp.entityId = collection.id; comp.entityName = collection.name; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onEditedUsers.subscribe(() => { this.load(); modal.close(); diff --git a/apps/web/src/app/organizations/manage/events.component.ts b/apps/web/src/app/organizations/manage/events.component.ts index e16d7c4b6dd..6573543d0d0 100644 --- a/apps/web/src/app/organizations/manage/events.component.ts +++ b/apps/web/src/app/organizations/manage/events.component.ts @@ -20,6 +20,7 @@ import { EventService } from "../../core"; selector: "app-org-events", templateUrl: "events.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class EventsComponent extends BaseEventsComponent implements OnInit { exportFileName = "org-events"; organizationId: string; @@ -52,6 +53,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; this.organization = await this.organizationService.get(this.organizationId); diff --git a/apps/web/src/app/organizations/manage/groups.component.ts b/apps/web/src/app/organizations/manage/groups.component.ts index 0f95da5d811..419e3ab6410 100644 --- a/apps/web/src/app/organizations/manage/groups.component.ts +++ b/apps/web/src/app/organizations/manage/groups.component.ts @@ -18,6 +18,7 @@ import { GroupAddEditComponent } from "./group-add-edit.component"; selector: "app-org-groups", templateUrl: "groups.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class GroupsComponent implements OnInit { @ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef; @ViewChild("usersTemplate", { read: ViewContainerRef, static: true }) @@ -45,9 +46,11 @@ export class GroupsComponent implements OnInit { ) {} async ngOnInit() { - this.route.parent.params.subscribe(async (params) => { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe + this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; await this.load(); + /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ this.route.queryParams.pipe(first()).subscribe(async (qParams) => { this.searchText = qParams.search; }); @@ -88,10 +91,12 @@ export class GroupsComponent implements OnInit { (comp) => { comp.organizationId = this.organizationId; comp.groupId = group != null ? group.id : null; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSavedGroup.subscribe(() => { modal.close(); this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDeletedGroup.subscribe(() => { modal.close(); this.removeGroup(group); @@ -139,6 +144,7 @@ export class GroupsComponent implements OnInit { comp.entityId = group.id; comp.entityName = group.name; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onEditedUsers.subscribe(() => { modal.close(); }); diff --git a/apps/web/src/app/organizations/manage/manage.component.ts b/apps/web/src/app/organizations/manage/manage.component.ts index f9026f55707..f1e25092596 100644 --- a/apps/web/src/app/organizations/manage/manage.component.ts +++ b/apps/web/src/app/organizations/manage/manage.component.ts @@ -8,12 +8,14 @@ import { Organization } from "@bitwarden/common/models/domain/organization"; selector: "app-org-manage", templateUrl: "manage.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ManageComponent implements OnInit { organization: Organization; constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); }); diff --git a/apps/web/src/app/organizations/manage/people.component.ts b/apps/web/src/app/organizations/manage/people.component.ts index 48a2790cff2..84877602493 100644 --- a/apps/web/src/app/organizations/manage/people.component.ts +++ b/apps/web/src/app/organizations/manage/people.component.ts @@ -11,6 +11,7 @@ import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; @@ -42,6 +43,7 @@ import { UserGroupsComponent } from "./user-groups.component"; selector: "app-org-people", templateUrl: "people.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class PeopleComponent extends BasePeopleComponent implements OnInit @@ -92,7 +94,8 @@ export class PeopleComponent userNamePipe: UserNamePipe, private syncService: SyncService, stateService: StateService, - private organizationService: OrganizationService + private organizationService: OrganizationService, + private organizationApiService: OrganizationApiServiceAbstraction ) { super( apiService, @@ -110,7 +113,8 @@ export class PeopleComponent } async ngOnInit() { - this.route.parent.params.subscribe(async (params) => { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe + this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; const organization = await this.organizationService.get(this.organizationId); this.accessEvents = organization.useEvents; @@ -125,7 +129,7 @@ export class PeopleComponent const orgShareKey = await this.cryptoService.getOrgKey(this.organizationId); const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey); const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); - const response = await this.apiService.postOrganizationKeys(this.organizationId, request); + const response = await this.organizationApiService.updateKeys(this.organizationId, request); if (response != null) { this.orgHasKeys = response.publicKey != null && response.privateKey != null; await this.syncService.fullSync(true); // Replace oganizations with new data @@ -136,6 +140,7 @@ export class PeopleComponent await this.load(); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { this.searchText = qParams.search; if (qParams.viewEvents != null) { @@ -161,26 +166,26 @@ export class PeopleComponent return this.apiService.getOrganizationUsers(this.organizationId); } - deleteUser(id: string): Promise { + deleteUser(id: string): Promise { return this.apiService.deleteOrganizationUser(this.organizationId, id); } - revokeUser(id: string): Promise { + revokeUser(id: string): Promise { return this.apiService.revokeOrganizationUser(this.organizationId, id); } - restoreUser(id: string): Promise { + restoreUser(id: string): Promise { return this.apiService.restoreOrganizationUser(this.organizationId, id); } - reinviteUser(id: string): Promise { + reinviteUser(id: string): Promise { return this.apiService.postOrganizationUserReinvite(this.organizationId, id); } async confirmUser( user: OrganizationUserUserDetailsResponse, publicKey: Uint8Array - ): Promise { + ): Promise { const orgKey = await this.cryptoService.getOrgKey(this.organizationId); const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey.buffer); const request = new OrganizationUserConfirmRequest(); @@ -235,18 +240,22 @@ export class PeopleComponent comp.organizationId = this.organizationId; comp.organizationUserId = user != null ? user.id : null; comp.usesKeyConnector = user?.usesKeyConnector; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSavedUser.subscribe(() => { modal.close(); this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDeletedUser.subscribe(() => { modal.close(); this.removeUser(user); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onRevokedUser.subscribe(() => { modal.close(); this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onRestoredUser.subscribe(() => { modal.close(); this.load(); @@ -263,6 +272,7 @@ export class PeopleComponent comp.name = this.userNamePipe.transform(user); comp.organizationId = this.organizationId; comp.organizationUserId = user != null ? user.id : null; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSavedUser.subscribe(() => { modal.close(); }); @@ -387,6 +397,7 @@ export class PeopleComponent comp.organizationId = this.organizationId; comp.id = user != null ? user.id : null; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onPasswordReset.subscribe(() => { modal.close(); this.load(); @@ -425,6 +436,7 @@ export class PeopleComponent // Workaround to handle closing the modal shortly after it has been opened let close = false; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil modal.onShown.subscribe(() => { if (close) { modal.close(); diff --git a/apps/web/src/app/organizations/manage/policies.component.ts b/apps/web/src/app/organizations/manage/policies.component.ts index 17cc3b7bbe6..b9f11322de5 100644 --- a/apps/web/src/app/organizations/manage/policies.component.ts +++ b/apps/web/src/app/organizations/manage/policies.component.ts @@ -18,6 +18,7 @@ import { PolicyEditComponent } from "./policy-edit.component"; selector: "app-org-policies", templateUrl: "policies.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class PoliciesComponent implements OnInit { @ViewChild("editTemplate", { read: ViewContainerRef, static: true }) editModalRef: ViewContainerRef; @@ -40,6 +41,7 @@ export class PoliciesComponent implements OnInit { ) {} async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; this.organization = await this.organizationService.get(this.organizationId); @@ -48,6 +50,7 @@ export class PoliciesComponent implements OnInit { await this.load(); // Handle policies component launch from Event message + /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.policyId != null) { const policyIdFromEvents: string = qParams.policyId; @@ -85,6 +88,7 @@ export class PoliciesComponent implements OnInit { comp.policy = policy; comp.organizationId = this.organizationId; comp.policiesEnabledMap = this.policiesEnabledMap; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSavedPolicy.subscribe(() => { modal.close(); this.load(); diff --git a/apps/web/src/app/organizations/settings/account.component.ts b/apps/web/src/app/organizations/settings/account.component.ts index dc2d9581643..cf4a5afdf70 100644 --- a/apps/web/src/app/organizations/settings/account.component.ts +++ b/apps/web/src/app/organizations/settings/account.component.ts @@ -7,6 +7,7 @@ import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { SyncService } from "@bitwarden/common/abstractions/sync.service"; import { OrganizationKeysRequest } from "@bitwarden/common/models/request/organizationKeysRequest"; @@ -22,6 +23,7 @@ import { DeleteOrganizationComponent } from "./delete-organization.component"; selector: "app-org-account", templateUrl: "account.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class AccountComponent { @ViewChild("deleteOrganizationTemplate", { read: ViewContainerRef, static: true }) deleteModalRef: ViewContainerRef; @@ -37,7 +39,8 @@ export class AccountComponent { loading = true; canUseApi = false; org: OrganizationResponse; - formPromise: Promise; + formPromise: Promise; + taxFormPromise: Promise; private organizationId: string; @@ -51,19 +54,21 @@ export class AccountComponent { private cryptoService: CryptoService, private logService: LogService, private router: Router, - private organizationService: OrganizationService + private organizationService: OrganizationService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async ngOnInit() { this.selfHosted = this.platformUtilsService.isSelfHost(); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; this.canManageBilling = ( await this.organizationService.get(this.organizationId) ).canManageBilling; try { - this.org = await this.apiService.getOrganization(this.organizationId); + this.org = await this.organizationApiService.get(this.organizationId); this.canUseApi = this.org.useApi; } catch (e) { this.logService.error(e); @@ -87,7 +92,7 @@ export class AccountComponent { request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); } - this.formPromise = this.apiService.putOrganization(this.organizationId, request).then(() => { + this.formPromise = this.organizationApiService.save(this.organizationId, request).then(() => { return this.syncService.fullSync(true); }); await this.formPromise; @@ -107,6 +112,7 @@ export class AccountComponent { this.deleteModalRef, (comp) => { comp.organizationId = this.organizationId; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSuccess.subscribe(() => { this.router.navigate(["/"]); }); @@ -124,7 +130,9 @@ export class AccountComponent { await this.modalService.openViewRef(ApiKeyComponent, this.apiKeyModalRef, (comp) => { comp.keyType = "organization"; comp.entityId = this.organizationId; - comp.postKey = this.apiService.postOrganizationApiKey.bind(this.apiService); + comp.postKey = this.organizationApiService.getOrCreateApiKey.bind( + this.organizationApiService + ); comp.scope = "api.organization"; comp.grantType = "client_credentials"; comp.apiKeyTitle = "apiKey"; @@ -138,7 +146,7 @@ export class AccountComponent { comp.keyType = "organization"; comp.isRotation = true; comp.entityId = this.organizationId; - comp.postKey = this.apiService.postOrganizationRotateApiKey.bind(this.apiService); + comp.postKey = this.organizationApiService.rotateApiKey.bind(this.organizationApiService); comp.scope = "api.organization"; comp.grantType = "client_credentials"; comp.apiKeyTitle = "apiKey"; diff --git a/apps/web/src/app/organizations/settings/adjust-subscription.component.ts b/apps/web/src/app/organizations/settings/adjust-subscription.component.ts index 006c735aef4..6930f86068a 100644 --- a/apps/web/src/app/organizations/settings/adjust-subscription.component.ts +++ b/apps/web/src/app/organizations/settings/adjust-subscription.component.ts @@ -1,8 +1,8 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/models/request/organizationSubscriptionUpdateRequest"; @@ -18,16 +18,16 @@ export class AdjustSubscription { @Input() interval = "year"; @Output() onAdjusted = new EventEmitter(); - formPromise: Promise; + formPromise: Promise; limitSubscription: boolean; newSeatCount: number; newMaxSeats: number; constructor( - private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} ngOnInit() { @@ -40,7 +40,7 @@ export class AdjustSubscription { try { const seatAdjustment = this.newSeatCount - this.currentSeatCount; const request = new OrganizationSubscriptionUpdateRequest(seatAdjustment, this.newMaxSeats); - this.formPromise = this.apiService.postOrganizationUpdateSubscription( + this.formPromise = this.organizationApiService.updateSubscription( this.organizationId, request ); diff --git a/apps/web/src/app/organizations/settings/delete-organization.component.ts b/apps/web/src/app/organizations/settings/delete-organization.component.ts index 19b82ebcaab..2b2341a44ac 100644 --- a/apps/web/src/app/organizations/settings/delete-organization.component.ts +++ b/apps/web/src/app/organizations/settings/delete-organization.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, OnInit, Output } from "@angular/core"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; import { CipherType } from "@bitwarden/common/enums/cipherType"; @@ -53,19 +53,19 @@ export class DeleteOrganizationComponent implements OnInit { deleteOrganizationRequestType: "InvalidFamiliesForEnterprise" | "RegularDelete" = "RegularDelete"; organizationName: string; organizationContentSummary: OrganizationContentSummary = new OrganizationContentSummary(); - @Output() onSuccess: EventEmitter = new EventEmitter(); + @Output() onSuccess: EventEmitter = new EventEmitter(); masterPassword: Verification; - formPromise: Promise; + formPromise: Promise; constructor( - private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private userVerificationService: UserVerificationService, private logService: LogService, private cipherService: CipherService, - private organizationService: OrganizationService + private organizationService: OrganizationService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async ngOnInit(): Promise { @@ -76,7 +76,7 @@ export class DeleteOrganizationComponent implements OnInit { try { this.formPromise = this.userVerificationService .buildRequest(this.masterPassword) - .then((request) => this.apiService.deleteOrganization(this.organizationId, request)); + .then((request) => this.organizationApiService.delete(this.organizationId, request)); await this.formPromise; this.platformUtilsService.showToast( "success", diff --git a/apps/web/src/app/organizations/settings/download-license.component.ts b/apps/web/src/app/organizations/settings/download-license.component.ts index 05927c1ec3a..5cad1c3b22b 100644 --- a/apps/web/src/app/organizations/settings/download-license.component.ts +++ b/apps/web/src/app/organizations/settings/download-license.component.ts @@ -1,8 +1,8 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; @Component({ selector: "app-download-license", @@ -14,12 +14,12 @@ export class DownloadLicenseComponent { @Output() onCanceled = new EventEmitter(); installationId: string; - formPromise: Promise; + formPromise: Promise; constructor( - private apiService: ApiService, private fileDownloadService: FileDownloadService, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async submit() { @@ -28,7 +28,7 @@ export class DownloadLicenseComponent { } try { - this.formPromise = this.apiService.getOrganizationLicense( + this.formPromise = this.organizationApiService.getLicense( this.organizationId, this.installationId ); diff --git a/apps/web/src/app/organizations/settings/settings.component.ts b/apps/web/src/app/organizations/settings/settings.component.ts index 2a4f798c7f2..318608524b3 100644 --- a/apps/web/src/app/organizations/settings/settings.component.ts +++ b/apps/web/src/app/organizations/settings/settings.component.ts @@ -8,6 +8,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti selector: "app-org-settings", templateUrl: "settings.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class SettingsComponent { access2fa = false; showBilling: boolean; @@ -19,6 +20,7 @@ export class SettingsComponent { ) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.params.subscribe(async (params) => { const organization = await this.organizationService.get(params.organizationId); this.showBilling = !this.platformUtilsService.isSelfHost() && organization.canManageBilling; diff --git a/apps/web/src/app/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/organizations/settings/two-factor-setup.component.ts index c3684aa420e..be82b0a86b9 100644 --- a/apps/web/src/app/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/organizations/settings/two-factor-setup.component.ts @@ -18,6 +18,7 @@ import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from "../../se selector: "app-two-factor-setup", templateUrl: "../../settings/two-factor-setup.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent { constructor( apiService: ApiService, @@ -43,6 +44,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent { } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; await super.ngOnInit(); @@ -55,6 +57,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent { const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent); duoComp.type = TwoFactorProviderType.OrganizationDuo; duoComp.organizationId = this.organizationId; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil duoComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo); }); diff --git a/apps/web/src/app/organizations/sponsorships/families-for-enterprise-setup.component.ts b/apps/web/src/app/organizations/sponsorships/families-for-enterprise-setup.component.ts index ea8c90d7ef0..8420fd91223 100644 --- a/apps/web/src/app/organizations/sponsorships/families-for-enterprise-setup.component.ts +++ b/apps/web/src/app/organizations/sponsorships/families-for-enterprise-setup.component.ts @@ -22,6 +22,7 @@ import { DeleteOrganizationComponent } from "../settings/delete-organization.com selector: "families-for-enterprise-setup", templateUrl: "families-for-enterprise-setup.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class FamiliesForEnterpriseSetupComponent implements OnInit { @ViewChild(OrganizationPlansComponent, { static: false }) set organizationPlansComponent(value: OrganizationPlansComponent) { @@ -32,6 +33,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit { value.plan = PlanType.FamiliesAnnually; value.product = ProductType.Families; value.acceptingSponsorship = true; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil value.onSuccess.subscribe(this.onOrganizationCreateSuccess.bind(this)); } @@ -63,6 +65,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit { async ngOnInit() { document.body.classList.remove("layout_frontend"); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { const error = qParams.token == null; if (error) { @@ -130,6 +133,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit { (comp) => { comp.organizationId = organizationId; comp.deleteOrganizationRequestType = "InvalidFamiliesForEnterprise"; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSuccess.subscribe(() => { this.router.navigate(["/"]); }); diff --git a/apps/web/src/app/organizations/tools/exposed-passwords-report.component.ts b/apps/web/src/app/organizations/tools/exposed-passwords-report.component.ts index d34e757d744..00c2ce2e3f4 100644 --- a/apps/web/src/app/organizations/tools/exposed-passwords-report.component.ts +++ b/apps/web/src/app/organizations/tools/exposed-passwords-report.component.ts @@ -18,6 +18,7 @@ import { ExposedPasswordsReportComponent as BaseExposedPasswordsReportComponent selector: "app-org-exposed-passwords-report", templateUrl: "../../reports/pages/exposed-passwords-report.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportComponent { manageableCiphers: Cipher[]; @@ -42,6 +43,7 @@ export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportC } ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); this.manageableCiphers = await this.cipherService.getAll(); diff --git a/apps/web/src/app/organizations/tools/import-export/org-export.component.ts b/apps/web/src/app/organizations/tools/import-export/org-export.component.ts index 79f92542317..20b6690acd9 100644 --- a/apps/web/src/app/organizations/tools/import-export/org-export.component.ts +++ b/apps/web/src/app/organizations/tools/import-export/org-export.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import { UntypedFormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; @@ -19,6 +20,7 @@ import { ExportComponent } from "../../../tools/import-export/export.component"; selector: "app-org-export", templateUrl: "../../../tools/import-export/export.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class OrganizationExportComponent extends ExportComponent { constructor( cryptoService: CryptoService, @@ -31,7 +33,8 @@ export class OrganizationExportComponent extends ExportComponent { logService: LogService, userVerificationService: UserVerificationService, formBuilder: UntypedFormBuilder, - fileDownloadService: FileDownloadService + fileDownloadService: FileDownloadService, + modalService: ModalService ) { super( cryptoService, @@ -43,11 +46,13 @@ export class OrganizationExportComponent extends ExportComponent { logService, userVerificationService, formBuilder, - fileDownloadService + fileDownloadService, + modalService ); } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; }); diff --git a/apps/web/src/app/organizations/tools/import-export/org-import-export.module.ts b/apps/web/src/app/organizations/tools/import-export/org-import-export.module.ts index 527ac69bddd..650363300b3 100644 --- a/apps/web/src/app/organizations/tools/import-export/org-import-export.module.ts +++ b/apps/web/src/app/organizations/tools/import-export/org-import-export.module.ts @@ -7,7 +7,6 @@ import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { ImportService as ImportServiceAbstraction } from "@bitwarden/common/abstractions/import.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { ImportService } from "@bitwarden/common/services/import.service"; import { LooseComponentsModule, SharedModule } from "../../../shared"; @@ -29,7 +28,6 @@ import { OrganizationImportComponent } from "./org-import.component"; ApiService, I18nService, CollectionService, - PlatformUtilsService, CryptoService, ], }, diff --git a/apps/web/src/app/organizations/tools/import-export/org-import.component.ts b/apps/web/src/app/organizations/tools/import-export/org-import.component.ts index 504163a76fc..27d1c6cd39e 100644 --- a/apps/web/src/app/organizations/tools/import-export/org-import.component.ts +++ b/apps/web/src/app/organizations/tools/import-export/org-import.component.ts @@ -1,6 +1,7 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { ImportService } from "@bitwarden/common/abstractions/import.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; @@ -14,6 +15,7 @@ import { ImportComponent } from "../../../tools/import-export/import.component"; selector: "app-org-import", templateUrl: "../../../tools/import-export/import.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class OrganizationImportComponent extends ImportComponent { organizationName: string; @@ -25,12 +27,22 @@ export class OrganizationImportComponent extends ImportComponent { platformUtilsService: PlatformUtilsService, policyService: PolicyService, private organizationService: OrganizationService, - logService: LogService + logService: LogService, + modalService: ModalService ) { - super(i18nService, importService, router, platformUtilsService, policyService, logService); + super( + i18nService, + importService, + router, + platformUtilsService, + policyService, + logService, + modalService + ); } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; this.successNavigate = ["organizations", this.organizationId, "vault"]; diff --git a/apps/web/src/app/organizations/tools/inactive-two-factor-report.component.ts b/apps/web/src/app/organizations/tools/inactive-two-factor-report.component.ts index 56e9625511f..80fe51d32d3 100644 --- a/apps/web/src/app/organizations/tools/inactive-two-factor-report.component.ts +++ b/apps/web/src/app/organizations/tools/inactive-two-factor-report.component.ts @@ -17,6 +17,7 @@ import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponen selector: "app-inactive-two-factor-report", templateUrl: "../../reports/pages/inactive-two-factor-report.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class InactiveTwoFactorReportComponent extends BaseInactiveTwoFactorReportComponent { constructor( cipherService: CipherService, @@ -39,6 +40,7 @@ export class InactiveTwoFactorReportComponent extends BaseInactiveTwoFactorRepor } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); await super.ngOnInit(); diff --git a/apps/web/src/app/organizations/tools/reused-passwords-report.component.ts b/apps/web/src/app/organizations/tools/reused-passwords-report.component.ts index 1f97546cf36..98a62cd83ab 100644 --- a/apps/web/src/app/organizations/tools/reused-passwords-report.component.ts +++ b/apps/web/src/app/organizations/tools/reused-passwords-report.component.ts @@ -17,6 +17,7 @@ import { ReusedPasswordsReportComponent as BaseReusedPasswordsReportComponent } selector: "app-reused-passwords-report", templateUrl: "../../reports/pages/reused-passwords-report.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportComponent { manageableCiphers: Cipher[]; @@ -33,6 +34,7 @@ export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportCom } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); this.manageableCiphers = await this.cipherService.getAll(); diff --git a/apps/web/src/app/organizations/tools/tools.component.ts b/apps/web/src/app/organizations/tools/tools.component.ts index cfff58a682f..249e5cd2ca8 100644 --- a/apps/web/src/app/organizations/tools/tools.component.ts +++ b/apps/web/src/app/organizations/tools/tools.component.ts @@ -9,6 +9,7 @@ import { Organization } from "@bitwarden/common/models/domain/organization"; selector: "app-org-tools", templateUrl: "tools.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ToolsComponent { organization: Organization; accessReports = false; @@ -21,6 +22,7 @@ export class ToolsComponent { ) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); // TODO: Maybe we want to just make sure they are not on a free plan? Just compare useTotp for now diff --git a/apps/web/src/app/organizations/tools/unsecured-websites-report.component.ts b/apps/web/src/app/organizations/tools/unsecured-websites-report.component.ts index f636303bb0e..b1e99c50fc5 100644 --- a/apps/web/src/app/organizations/tools/unsecured-websites-report.component.ts +++ b/apps/web/src/app/organizations/tools/unsecured-websites-report.component.ts @@ -16,6 +16,7 @@ import { UnsecuredWebsitesReportComponent as BaseUnsecuredWebsitesReportComponen selector: "app-unsecured-websites-report", templateUrl: "../../reports/pages/unsecured-websites-report.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class UnsecuredWebsitesReportComponent extends BaseUnsecuredWebsitesReportComponent { constructor( cipherService: CipherService, @@ -30,6 +31,7 @@ export class UnsecuredWebsitesReportComponent extends BaseUnsecuredWebsitesRepor } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); await super.ngOnInit(); diff --git a/apps/web/src/app/organizations/tools/weak-passwords-report.component.ts b/apps/web/src/app/organizations/tools/weak-passwords-report.component.ts index 306b861f369..9b5d95b6372 100644 --- a/apps/web/src/app/organizations/tools/weak-passwords-report.component.ts +++ b/apps/web/src/app/organizations/tools/weak-passwords-report.component.ts @@ -18,6 +18,7 @@ import { WeakPasswordsReportComponent as BaseWeakPasswordsReportComponent } from selector: "app-weak-passwords-report", templateUrl: "../../reports/pages/weak-passwords-report.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportComponent { manageableCiphers: Cipher[]; @@ -42,6 +43,7 @@ export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportCompone } async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organization = await this.organizationService.get(params.organizationId); this.manageableCiphers = await this.cipherService.getAll(); diff --git a/apps/web/src/app/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/organizations/users/enroll-master-password-reset.component.ts index 2a8e5f21880..c2977180d4b 100644 --- a/apps/web/src/app/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/organizations/users/enroll-master-password-reset.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { SyncService } from "@bitwarden/common/abstractions/sync.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; @@ -33,7 +34,8 @@ export class EnrollMasterPasswordReset { private syncService: SyncService, private logService: LogService, private modalRef: ModalRef, - config: ModalConfig + config: ModalConfig, + private organizationApiService: OrganizationApiServiceAbstraction ) { this.organization = config.data.organization; } @@ -48,7 +50,7 @@ export class EnrollMasterPasswordReset { let keyString: string = null; // Retrieve Public Key - const orgKeys = await this.apiService.getOrganizationKeys(this.organization.id); + const orgKeys = await this.organizationApiService.getKeys(this.organization.id); if (orgKeys == null) { throw new Error(this.i18nService.t("resetPasswordOrgKeysError")); } diff --git a/apps/web/src/app/organizations/vault/vault.component.ts b/apps/web/src/app/organizations/vault/vault.component.ts index 953a05f3d80..c25e565e78c 100644 --- a/apps/web/src/app/organizations/vault/vault.component.ts +++ b/apps/web/src/app/organizations/vault/vault.component.ts @@ -86,11 +86,13 @@ export class VaultComponent implements OnInit, OnDestroy { ? "trashCleanupWarningSelfHosted" : "trashCleanupWarning" ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.params.subscribe(async (params: any) => { this.organization = await this.organizationService.get(params.organizationId); this.vaultFilterComponent.organization = this.organization; this.ciphersComponent.organization = this.organization; + /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ this.route.queryParams.pipe(first()).subscribe(async (qParams) => { this.ciphersComponent.searchText = this.vaultFilterComponent.searchText = qParams.search; if (!this.organization.canViewAllCollections) { @@ -126,6 +128,7 @@ export class VaultComponent implements OnInit, OnDestroy { } } + /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ this.route.queryParams.subscribe(async (params) => { const cipherId = getCipherIdFromParams(params); if (cipherId) { @@ -191,11 +194,14 @@ export class VaultComponent implements OnInit, OnDestroy { (comp) => { comp.organization = this.organization; comp.cipherId = cipher.id; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onUploadedAttachment.subscribe(() => (madeAttachmentChanges = true)); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDeletedAttachment.subscribe(() => (madeAttachmentChanges = true)); } ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe modal.onClosed.subscribe(async () => { if (madeAttachmentChanges) { await this.ciphersComponent.refresh(); @@ -217,6 +223,7 @@ export class VaultComponent implements OnInit, OnDestroy { } comp.organization = this.organization; comp.cipherId = cipher.id; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onSavedCollections.subscribe(async () => { modal.close(); await this.ciphersComponent.refresh(); @@ -258,14 +265,17 @@ export class VaultComponent implements OnInit, OnDestroy { (comp) => { comp.organization = this.organization; comp.cipherId = cipherId; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onSavedCipher.subscribe(async () => { modal.close(); await this.ciphersComponent.refresh(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onDeletedCipher.subscribe(async () => { modal.close(); await this.ciphersComponent.refresh(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onRestoredCipher.subscribe(async () => { modal.close(); await this.ciphersComponent.refresh(); diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 6fb664facc8..61daf7c82cb 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -5,7 +5,7 @@ import { AuthGuard } from "@bitwarden/angular/guards/auth.guard"; import { LockGuard } from "@bitwarden/angular/guards/lock.guard"; import { UnauthGuard } from "@bitwarden/angular/guards/unauth.guard"; -import { flagEnabled, FlagName } from "../utils/flags"; +import { flagEnabled, Flags } from "../utils/flags"; import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component"; import { AcceptOrganizationComponent } from "./accounts/accept-organization.component"; @@ -261,7 +261,7 @@ const routes: Routes = [ }) export class OssRoutingModule {} -export function buildFlaggedRoute(flagName: FlagName, route: Route): Route { +export function buildFlaggedRoute(flagName: keyof Flags, route: Route): Route { return flagEnabled(flagName) ? route : { diff --git a/apps/web/src/app/reports/pages/cipher-report.component.ts b/apps/web/src/app/reports/pages/cipher-report.component.ts index 9179c04371a..44d7cae619a 100644 --- a/apps/web/src/app/reports/pages/cipher-report.component.ts +++ b/apps/web/src/app/reports/pages/cipher-report.component.ts @@ -53,14 +53,17 @@ export class CipherReportComponent { } comp.cipherId = cipher == null ? null : cipher.id; + // eslint-disable-next-line rxjs/no-async-subscribe comp.onSavedCipher.subscribe(async () => { modal.close(); await this.load(); }); + // eslint-disable-next-line rxjs/no-async-subscribe comp.onDeletedCipher.subscribe(async () => { modal.close(); await this.load(); }); + // eslint-disable-next-line rxjs/no-async-subscribe comp.onRestoredCipher.subscribe(async () => { modal.close(); await this.load(); diff --git a/apps/web/src/app/reports/reports-layout.component.ts b/apps/web/src/app/reports/reports-layout.component.ts index 3d3fee2904b..7bfe912c1ad 100644 --- a/apps/web/src/app/reports/reports-layout.component.ts +++ b/apps/web/src/app/reports/reports-layout.component.ts @@ -14,6 +14,7 @@ export class ReportsLayoutComponent implements OnDestroy { constructor(router: Router) { this.subscription = router.events .pipe(filter((event) => event instanceof NavigationEnd)) + // eslint-disable-next-line rxjs-angular/prefer-takeuntil .subscribe((event) => { this.homepage = (event as NavigationEnd).url == "/reports"; }); diff --git a/apps/web/src/app/send/access.component.ts b/apps/web/src/app/send/access.component.ts index 707a5bef63d..7f4c6bbab8e 100644 --- a/apps/web/src/app/send/access.component.ts +++ b/apps/web/src/app/send/access.component.ts @@ -22,6 +22,7 @@ import { SendAccessView } from "@bitwarden/common/models/view/sendAccessView"; selector: "app-send-access", templateUrl: "access.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class AccessComponent implements OnInit { send: SendAccessView; sendType = SendType; @@ -72,6 +73,7 @@ export class AccessComponent implements OnInit { } ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.params.subscribe(async (params) => { this.id = params.sendId; this.key = params.key; diff --git a/apps/web/src/app/send/send.component.ts b/apps/web/src/app/send/send.component.ts index 4024f7c18f7..2719d904564 100644 --- a/apps/web/src/app/send/send.component.ts +++ b/apps/web/src/app/send/send.component.ts @@ -85,10 +85,12 @@ export class SendComponent extends BaseSendComponent { this.sendAddEditModalRef, (comp) => { comp.sendId = send == null ? null : send.id; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onSavedSend.subscribe(async () => { modal.close(); await this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onDeletedSend.subscribe(async () => { modal.close(); await this.load(); diff --git a/apps/web/src/app/settings/adjust-payment.component.ts b/apps/web/src/app/settings/adjust-payment.component.ts index e88329fd7b8..4e9338081fa 100644 --- a/apps/web/src/app/settings/adjust-payment.component.ts +++ b/apps/web/src/app/settings/adjust-payment.component.ts @@ -3,6 +3,7 @@ import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PaymentMethodType } from "@bitwarden/common/enums/paymentMethodType"; import { PaymentRequest } from "@bitwarden/common/models/request/paymentRequest"; @@ -24,13 +25,14 @@ export class AdjustPaymentComponent { @Output() onCanceled = new EventEmitter(); paymentMethodType = PaymentMethodType; - formPromise: Promise; + formPromise: Promise; constructor( private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async submit() { @@ -50,7 +52,7 @@ export class AdjustPaymentComponent { request.line2 = this.taxInfoComponent.taxInfo.line2; request.city = this.taxInfoComponent.taxInfo.city; request.state = this.taxInfoComponent.taxInfo.state; - return this.apiService.postOrganizationPayment(this.organizationId, request); + return this.organizationApiService.updatePayment(this.organizationId, request); } }); await this.formPromise; diff --git a/apps/web/src/app/settings/adjust-storage.component.ts b/apps/web/src/app/settings/adjust-storage.component.ts index eea26c2a2df..6931ad9d10b 100644 --- a/apps/web/src/app/settings/adjust-storage.component.ts +++ b/apps/web/src/app/settings/adjust-storage.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StorageRequest } from "@bitwarden/common/models/request/storageRequest"; import { PaymentResponse } from "@bitwarden/common/models/response/paymentResponse"; @@ -25,7 +26,7 @@ export class AdjustStorageComponent { @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; storageAdjustment = 0; - formPromise: Promise; + formPromise: Promise; constructor( private apiService: ApiService, @@ -33,7 +34,8 @@ export class AdjustStorageComponent { private platformUtilsService: PlatformUtilsService, private router: Router, private activatedRoute: ActivatedRoute, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async submit() { @@ -50,7 +52,7 @@ export class AdjustStorageComponent { if (this.organizationId == null) { response = this.formPromise = this.apiService.postAccountStorage(request); } else { - response = this.formPromise = this.apiService.postOrganizationStorage( + response = this.formPromise = this.organizationApiService.updateStorage( this.organizationId, request ); diff --git a/apps/web/src/app/settings/change-password.component.ts b/apps/web/src/app/settings/change-password.component.ts index 86c3987fd93..abd8daaf4a0 100644 --- a/apps/web/src/app/settings/change-password.component.ts +++ b/apps/web/src/app/settings/change-password.component.ts @@ -11,6 +11,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; @@ -53,7 +54,8 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { private sendService: SendService, private organizationService: OrganizationService, private keyConnectorService: KeyConnectorService, - private router: Router + private router: Router, + private organizationApiService: OrganizationApiServiceAbstraction ) { super( i18nService, @@ -267,7 +269,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { } // Retrieve public key - const response = await this.apiService.getOrganizationKeys(org.id); + const response = await this.organizationApiService.getKeys(org.id); const publicKey = Utils.fromB64ToArray(response?.publicKey); // Re-enroll - encrpyt user's encKey.key with organization public key diff --git a/apps/web/src/app/settings/create-organization.component.ts b/apps/web/src/app/settings/create-organization.component.ts index fc6caf815a8..6cae85c940e 100644 --- a/apps/web/src/app/settings/create-organization.component.ts +++ b/apps/web/src/app/settings/create-organization.component.ts @@ -11,6 +11,7 @@ import { OrganizationPlansComponent } from "./organization-plans.component"; selector: "app-create-organization", templateUrl: "create-organization.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class CreateOrganizationComponent implements OnInit { @ViewChild(OrganizationPlansComponent, { static: true }) orgPlansComponent: OrganizationPlansComponent; @@ -18,6 +19,7 @@ export class CreateOrganizationComponent implements OnInit { constructor(private route: ActivatedRoute) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.plan === "families") { this.orgPlansComponent.plan = PlanType.FamiliesAnnually; diff --git a/apps/web/src/app/settings/emergency-access-view.component.ts b/apps/web/src/app/settings/emergency-access-view.component.ts index acdf6e43e27..f40113a5ad6 100644 --- a/apps/web/src/app/settings/emergency-access-view.component.ts +++ b/apps/web/src/app/settings/emergency-access-view.component.ts @@ -18,6 +18,7 @@ import { EmergencyAddEditComponent } from "./emergency-add-edit.component"; selector: "emergency-access-view", templateUrl: "emergency-access-view.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class EmergencyAccessViewComponent implements OnInit { @ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true }) cipherAddEditModalRef: ViewContainerRef; @@ -38,6 +39,7 @@ export class EmergencyAccessViewComponent implements OnInit { ) {} ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.route.params.subscribe((qParams) => { if (qParams.id == null) { return this.router.navigate(["settings/emergency-access"]); diff --git a/apps/web/src/app/settings/emergency-access.component.ts b/apps/web/src/app/settings/emergency-access.component.ts index fec4ea59dfc..a5fe4099b04 100644 --- a/apps/web/src/app/settings/emergency-access.component.ts +++ b/apps/web/src/app/settings/emergency-access.component.ts @@ -27,6 +27,7 @@ import { EmergencyAccessTakeoverComponent } from "./emergency-access-takeover.co selector: "emergency-access", templateUrl: "emergency-access.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class EmergencyAccessComponent implements OnInit { @ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef; @ViewChild("takeoverTemplate", { read: ViewContainerRef, static: true }) @@ -84,10 +85,12 @@ export class EmergencyAccessComponent implements OnInit { comp.name = this.userNamePipe.transform(details); comp.emergencyAccessId = details?.id; comp.readOnly = !this.canAccessPremium; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onSaved.subscribe(() => { modal.close(); this.load(); }); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDeleted.subscribe(() => { modal.close(); this.remove(details); @@ -132,6 +135,7 @@ export class EmergencyAccessComponent implements OnInit { comp.name = this.userNamePipe.transform(contact); comp.emergencyAccessId = contact.id; comp.userId = contact?.granteeId; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe comp.onConfirmed.subscribe(async () => { modal.close(); @@ -264,6 +268,7 @@ export class EmergencyAccessComponent implements OnInit { comp.email = details.email; comp.emergencyAccessId = details != null ? details.id : null; + // eslint-disable-next-line rxjs-angular/prefer-takeuntil comp.onDone.subscribe(() => { modal.close(); this.platformUtilsService.showToast( diff --git a/apps/web/src/app/settings/organization-plans.component.ts b/apps/web/src/app/settings/organization-plans.component.ts index b7598fc9790..12fcb4ad40d 100644 --- a/apps/web/src/app/settings/organization-plans.component.ts +++ b/apps/web/src/app/settings/organization-plans.component.ts @@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { OrganizationService } from "@bitwarden/common/abstractions/organization.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { SyncService } from "@bitwarden/common/abstractions/sync.service"; @@ -26,6 +27,10 @@ import { PlanResponse } from "@bitwarden/common/models/response/planResponse"; import { PaymentComponent } from "./payment.component"; import { TaxInfoComponent } from "./tax-info.component"; +interface OnSuccessArgs { + organizationId: string; +} + @Component({ selector: "app-organization-plans", templateUrl: "organization-plans.component.html", @@ -41,14 +46,14 @@ export class OrganizationPlansComponent implements OnInit { @Input() product: ProductType = ProductType.Free; @Input() plan: PlanType = PlanType.Free; @Input() providerId: string; - @Output() onSuccess = new EventEmitter(); - @Output() onCanceled = new EventEmitter(); + @Output() onSuccess = new EventEmitter(); + @Output() onCanceled = new EventEmitter(); @Output() onTrialBillingSuccess = new EventEmitter(); loading = true; selfHosted = false; productTypes = ProductType; - formPromise: Promise; + formPromise: Promise; singleOrgPolicyBlock = false; isInTrialFlow = false; discount = 0; @@ -79,7 +84,8 @@ export class OrganizationPlansComponent implements OnInit { private organizationService: OrganizationService, private logService: LogService, private messagingService: MessagingService, - private formBuilder: UntypedFormBuilder + private formBuilder: UntypedFormBuilder, + private organizationApiService: OrganizationApiServiceAbstraction ) { this.selfHosted = platformUtilsService.isSelfHost(); } @@ -372,7 +378,7 @@ export class OrganizationPlansComponent implements OnInit { request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); } - const result = await this.apiService.postOrganizationUpgrade(this.organizationId, request); + const result = await this.organizationApiService.upgrade(this.organizationId, request); if (!result.success && result.paymentIntentClientSecret != null) { await this.paymentComponent.handleStripeCardPayment(result.paymentIntentClientSecret, null); } @@ -434,7 +440,7 @@ export class OrganizationPlansComponent implements OnInit { return orgId; } else { - return (await this.apiService.postOrganization(request)).id; + return (await this.organizationApiService.create(request)).id; } } @@ -449,14 +455,14 @@ export class OrganizationPlansComponent implements OnInit { fd.append("license", files[0]); fd.append("key", key); fd.append("collectionName", collectionCt); - const response = await this.apiService.postOrganizationLicense(fd); + const response = await this.organizationApiService.createLicense(fd); const orgId = response.id; await this.apiService.refreshIdentityToken(); // Org Keys live outside of the OrganizationLicense - add the keys to the org here const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); - await this.apiService.postOrganizationKeys(orgId, request); + await this.organizationApiService.updateKeys(orgId, request); return orgId; } diff --git a/apps/web/src/app/settings/payment.component.ts b/apps/web/src/app/settings/payment.component.ts index a4de1521c38..d0f4407b355 100644 --- a/apps/web/src/app/settings/payment.component.ts +++ b/apps/web/src/app/settings/payment.component.ts @@ -27,7 +27,7 @@ export class PaymentComponent implements OnInit, OnDestroy { @Input() hideCredit = false; @Input() trialFlow = false; - private destroy$: Subject = new Subject(); + private destroy$ = new Subject(); bank: any = { routing_number: null, diff --git a/apps/web/src/app/settings/tax-info.component.ts b/apps/web/src/app/settings/tax-info.component.ts index 91d9ca3ad3f..216a0d030a1 100644 --- a/apps/web/src/app/settings/tax-info.component.ts +++ b/apps/web/src/app/settings/tax-info.component.ts @@ -3,21 +3,29 @@ import { ActivatedRoute } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { OrganizationTaxInfoUpdateRequest } from "@bitwarden/common/models/request/organizationTaxInfoUpdateRequest"; import { TaxInfoUpdateRequest } from "@bitwarden/common/models/request/taxInfoUpdateRequest"; +import { TaxInfoResponse } from "@bitwarden/common/models/response/taxInfoResponse"; import { TaxRateResponse } from "@bitwarden/common/models/response/taxRateResponse"; +type TaxInfoView = Omit & { + includeTaxId: boolean; + [key: string]: unknown; +}; + @Component({ selector: "app-tax-info", templateUrl: "tax-info.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class TaxInfoComponent { @Input() trialFlow = false; @Output() onCountryChanged = new EventEmitter(); loading = true; organizationId: string; - taxInfo: any = { + taxInfo: TaxInfoView = { taxId: null, line1: null, line2: null, @@ -30,7 +38,7 @@ export class TaxInfoComponent { taxRates: TaxRateResponse[]; - private pristine: any = { + private pristine: TaxInfoView = { taxId: null, line1: null, line2: null, @@ -44,15 +52,17 @@ export class TaxInfoComponent { constructor( private apiService: ApiService, private route: ActivatedRoute, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async ngOnInit() { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; if (this.organizationId) { try { - const taxInfo = await this.apiService.getOrganizationTaxInfo(this.organizationId); + const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); if (taxInfo) { this.taxInfo.taxId = taxInfo.taxId; this.taxInfo.state = taxInfo.state; @@ -140,7 +150,7 @@ export class TaxInfoComponent { } const request = this.getTaxInfoRequest(); return this.organizationId - ? this.apiService.putOrganizationTaxInfo( + ? this.organizationApiService.updateTaxInfo( this.organizationId, request as OrganizationTaxInfoUpdateRequest ) diff --git a/apps/web/src/app/settings/two-factor-setup.component.ts b/apps/web/src/app/settings/two-factor-setup.component.ts index 35c8a0f16db..13e3cbecea8 100644 --- a/apps/web/src/app/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/settings/two-factor-setup.component.ts @@ -25,6 +25,7 @@ import { TwoFactorYubiKeyComponent } from "./two-factor-yubikey.component"; selector: "app-two-factor-setup", templateUrl: "two-factor-setup.component.html", }) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class TwoFactorSetupComponent implements OnInit { @ViewChild("recoveryTemplate", { read: ViewContainerRef, static: true }) recoveryModalRef: ViewContainerRef; @@ -116,6 +117,7 @@ export class TwoFactorSetupComponent implements OnInit { this.authenticatorModalRef, TwoFactorAuthenticatorComponent ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil authComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Authenticator); }); @@ -123,6 +125,7 @@ export class TwoFactorSetupComponent implements OnInit { } case TwoFactorProviderType.Yubikey: { const yubiComp = await this.openModal(this.yubikeyModalRef, TwoFactorYubiKeyComponent); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil yubiComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Yubikey); }); @@ -130,6 +133,7 @@ export class TwoFactorSetupComponent implements OnInit { } case TwoFactorProviderType.Duo: { const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil duoComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Duo); }); @@ -137,6 +141,7 @@ export class TwoFactorSetupComponent implements OnInit { } case TwoFactorProviderType.Email: { const emailComp = await this.openModal(this.emailModalRef, TwoFactorEmailComponent); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil emailComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Email); }); @@ -147,6 +152,7 @@ export class TwoFactorSetupComponent implements OnInit { this.webAuthnModalRef, TwoFactorWebAuthnComponent ); + // eslint-disable-next-line rxjs-angular/prefer-takeuntil webAuthnComp.onUpdated.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.WebAuthn); }); diff --git a/apps/web/src/app/settings/update-license.component.ts b/apps/web/src/app/settings/update-license.component.ts index e843a5d6009..de23be5f777 100644 --- a/apps/web/src/app/settings/update-license.component.ts +++ b/apps/web/src/app/settings/update-license.component.ts @@ -3,6 +3,7 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @Component({ @@ -14,13 +15,14 @@ export class UpdateLicenseComponent { @Output() onUpdated = new EventEmitter(); @Output() onCanceled = new EventEmitter(); - formPromise: Promise; + formPromise: Promise; constructor( private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private logService: LogService + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction ) {} async submit() { @@ -39,11 +41,11 @@ export class UpdateLicenseComponent { const fd = new FormData(); fd.append("license", files[0]); - let updatePromise: Promise = null; + let updatePromise: Promise = null; if (this.organizationId == null) { updatePromise = this.apiService.postAccountLicense(fd); } else { - updatePromise = this.apiService.postOrganizationLicenseUpdate(this.organizationId, fd); + updatePromise = this.organizationApiService.updateLicense(this.organizationId, fd); } this.formPromise = updatePromise.then(() => { diff --git a/apps/web/src/app/shared/locales.ts b/apps/web/src/app/shared/locales.ts index 706db45bd5e..ab551e93ac8 100644 --- a/apps/web/src/app/shared/locales.ts +++ b/apps/web/src/app/shared/locales.ts @@ -15,6 +15,7 @@ import localeEnIn from "@angular/common/locales/en-IN"; import localeEo from "@angular/common/locales/eo"; import localeEs from "@angular/common/locales/es"; import localeEt from "@angular/common/locales/et"; +import localeEu from "@angular/common/locales/eu"; import localeFi from "@angular/common/locales/fi"; import localeFil from "@angular/common/locales/fil"; import localeFr from "@angular/common/locales/fr"; @@ -66,6 +67,7 @@ registerLocaleData(localeEnIn, "en-IN"); registerLocaleData(localeEo, "eo"); registerLocaleData(localeEs, "es"); registerLocaleData(localeEt, "et"); +registerLocaleData(localeEu, "eu"); registerLocaleData(localeFi, "fi"); registerLocaleData(localeFil, "fil"); registerLocaleData(localeFr, "fr"); diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index f253df0d0a1..128b0585ee1 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -24,6 +24,7 @@ import { NestedCheckboxComponent } from "../components/nested-checkbox.component import { OrganizationSwitcherComponent } from "../components/organization-switcher.component"; import { PasswordRepromptComponent } from "../components/password-reprompt.component"; import { PremiumBadgeComponent } from "../components/premium-badge.component"; +import { UserVerificationPromptComponent } from "../components/user-verification-prompt.component"; import { FooterComponent } from "../layouts/footer.component"; import { FrontendLayoutComponent } from "../layouts/frontend-layout.component"; import { NavbarComponent } from "../layouts/navbar.component"; @@ -248,6 +249,7 @@ import { SharedModule } from "."; PasswordGeneratorHistoryComponent, PasswordGeneratorPolicyComponent, PasswordRepromptComponent, + UserVerificationPromptComponent, PaymentComponent, PaymentMethodComponent, PersonalOwnershipPolicyComponent, diff --git a/apps/web/src/app/tools/generator.component.html b/apps/web/src/app/tools/generator.component.html index 30f2a304014..542c0c3c5e3 100644 --- a/apps/web/src/app/tools/generator.component.html +++ b/apps/web/src/app/tools/generator.component.html @@ -296,6 +296,19 @@ />
+
+
+ + +
+
@@ -330,6 +343,18 @@ />
+
+
+ + +
+
diff --git a/apps/web/src/app/tools/import-export/export.component.html b/apps/web/src/app/tools/import-export/export.component.html index da667f3e44a..4e27fc70f75 100644 --- a/apps/web/src/app/tools/import-export/export.component.html +++ b/apps/web/src/app/tools/import-export/export.component.html @@ -1,9 +1,9 @@