diff --git a/.eslintrc.json b/.eslintrc.json index 6a5fc5b83f3..6bcea9dd045 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -86,7 +86,7 @@ "error", { "zones": [ - // Do not allow angular/node/electron code to be imported into common + // Do not allow angular/node code to be imported into common { "target": "./libs/common/**/*", "from": "./libs/angular/**/*" @@ -94,10 +94,6 @@ { "target": "./libs/common/**/*", "from": "./libs/node/**/*" - }, - { - "target": "./libs/common/**/*", - "from": "./libs/electron/**/*" } ] } @@ -131,12 +127,6 @@ "rules": { "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] } - }, - { - "files": ["libs/electron/src/**/*.ts"], - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/electron/*", "src/**/*"] }] - } } ] } diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 5dd8cd621ba..af82f6b811a 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -4,14 +4,10 @@ ./apps/browser/src/safari/desktop/Base.lproj ./apps/browser/src/services/vaultTimeout ./apps/browser/store/windows/Assets -./apps/desktop/src/models/nativeMessaging -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses ./libs/common/spec/misc/logInStrategies ./libs/common/src/abstractions/fileDownload ./libs/common/src/abstractions/userVerification ./libs/common/src/abstractions/vaultTimeout -./libs/common/src/emailForwarders ./libs/common/src/misc/logInStrategies ./libs/common/src/services/userVerification ./libs/common/src/services/vaultTimeout @@ -112,12 +108,6 @@ ./libs/common/src/enums/nativeMessagingVersion.ts ./libs/common/src/enums/cipherRepromptType.ts ./libs/common/src/enums/organizationUserType.ts -./libs/common/src/emailForwarders/fastmailForwarder.ts -./libs/common/src/emailForwarders/duckDuckGoForwarder.ts -./libs/common/src/emailForwarders/firefoxRelayForwarder.ts -./libs/common/src/emailForwarders/anonAddyForwarder.ts -./libs/common/src/emailForwarders/simpleLoginForwarder.ts -./libs/common/src/emailForwarders/forwarderOptions.ts ./libs/common/src/factories/accountFactory.ts ./libs/common/src/factories/globalStateFactory.ts ./libs/common/src/factories/stateFactory.ts @@ -162,16 +152,6 @@ ./libs/common/src/services/bitwardenFileUpload.service.ts ./libs/common/src/services/webCryptoFunction.service.ts ./libs/common/src/interfaces/IEncrypted.ts -./libs/electron/spec/services/electronLog.service.spec.ts -./libs/electron/src/baseMenu.ts -./libs/electron/src/services/electronLog.service.ts -./libs/electron/src/services/electronStorage.service.ts -./libs/electron/src/services/electronRendererMessaging.service.ts -./libs/electron/src/services/electronMainMessaging.service.ts -./libs/electron/src/services/electronPlatformUtils.service.ts -./libs/electron/src/services/electronRendererStorage.service.ts -./libs/electron/src/services/electronCrypto.service.ts -./libs/electron/src/services/electronRendererSecureStorage.service.ts ./README.md ./LICENSE_BITWARDEN.txt ./CONTRIBUTING.md @@ -186,42 +166,9 @@ ./apps/desktop/resources/appx/StoreLogo.png ./apps/desktop/resources/appx/Wide310x150Logo.png ./apps/desktop/resources/appx/Square44x44Logo.png -./apps/desktop/native-messaging-test-runner/src/ipcService.ts -./apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts -./apps/desktop/native-messaging-test-runner/src/logUtils.ts ./apps/desktop/README.md ./apps/desktop/desktop_native/Cargo.toml ./apps/desktop/desktop_native/Cargo.lock -./apps/desktop/src/app/services/desktopFileDownloadService.ts -./apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts -./apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts -./apps/desktop/src/models/nativeMessaging/messageCommon.ts -./apps/desktop/src/models/nativeMessaging/legacyMessage.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts -./apps/desktop/src/main/desktopCredentialStorageListener.ts -./apps/desktop/src/main/powerMonitor.main.ts -./apps/desktop/src/main/nativeMessaging.main.ts -./apps/desktop/src/services/passwordReprompt.service.ts -./apps/desktop/src/services/encryptedMessageHandlerService.ts -./apps/desktop/src/services/nativeMessaging.service.ts -./apps/desktop/src/services/nativeMessageHandler.service.ts ./apps/cli/stores/chocolatey/tools/VERIFICATION.txt ./apps/cli/README.md ./apps/browser/README.md diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 0a5746ce682..e54dc936eb4 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -63,8 +63,8 @@ jobs: repo_url=https://github.com/$GITHUB_REPOSITORY.git adj_build_num=${GITHUB_SHA:0:7} - echo "::set-output name=repo_url::$repo_url" - echo "::set-output name=adj_build_number::$adj_build_num" + echo "repo_url=$repo_url" >> $GITHUB_OUTPUT + echo "adj_build_number=$adj_build_num" >> $GITHUB_OUTPUT locales-test: @@ -183,7 +183,7 @@ jobs: name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-opera-mv3.zip if-no-files-found: error - + - name: Upload Chrome 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 e32dbedb24c..41b2a188fab 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -62,7 +62,7 @@ jobs: id: retrieve-version run: | PKG_VERSION=$(jq -r .version package.json) - echo "::set-output name=package_version::$PKG_VERSION" + echo "package_version=$PKG_VERSION" >> $GITHUB_OUTPUT cli: diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index be69b22a8a5..f92ae8a71e4 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -87,29 +87,29 @@ jobs: id: retrieve-version run: | PKG_VERSION=$(jq -r .version src/package.json) - echo "::set-output name=package_version::$PKG_VERSION" + echo "package_version=$PKG_VERSION" >> $GITHUB_OUTPUT - name: Increment Version id: increment-version run: | BUILD_NUMBER=$(expr 3000 + $GITHUB_RUN_NUMBER) echo "Setting build number to $BUILD_NUMBER" - echo "::set-output name=build_number::$BUILD_NUMBER" + echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT - name: Get Version Channel id: release-channel run: | case "${{ steps.retrieve-version.outputs.package_version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -117,15 +117,15 @@ jobs: id: branch-check run: | if [[ $(git ls-remote --heads origin rc) ]]; then - echo "::set-output name=rc_branch_exists::1" + echo "rc_branch_exists=1" >> $GITHUB_OUTPUT else - echo "::set-output name=rc_branch_exists::0" + echo "rc_branch_exists=0" >> $GITHUB_OUTPUT fi if [[ $(git ls-remote --heads origin hotfix-rc-desktop) ]]; then - echo "::set-output name=hotfix_branch_exists::1" + echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT else - echo "::set-output name=hotfix_branch_exists::0" + echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index eea60ccfbb0..b0bd282de6f 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -60,7 +60,7 @@ jobs: - name: Get GitHub sha as version id: version - run: echo "::set-output name=value::${GITHUB_SHA:0:7}" + run: echo "value=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT build-artifacts: name: Build artifacts @@ -303,7 +303,7 @@ jobs: IMAGE_TAG=$IMAGE_TAG-$TAG_EXTENSION fi - echo "::set-output name=value::$IMAGE_TAG" + echo "value=$IMAGE_TAG" >> $GITHUB_OUTPUT - name: Tag image env: diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index a57b600d0ec..fb456de1951 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -60,22 +60,22 @@ jobs: run: | BUILD_NUMBER=$(expr 3000 + $GITHUB_RUN_NUMBER) echo "Setting build number to $BUILD_NUMBER" - echo "::set-output name=build_number::$BUILD_NUMBER" + echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT - name: Get Version Channel id: release-channel run: | case "${{ steps.version.outputs.version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -102,7 +102,7 @@ jobs: git push -u origin $branch_name - echo "::set-output name=branch-name::$branch_name" + echo "branch-name=$branch_name" >> $GITHUB_OUTPUT linux: name: Linux Build diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 80c3b71c15d..a4aceac962b 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -29,6 +29,16 @@ on: required: true default: true type: boolean + electron_publish: + description: 'Publish electron to S3 bucket' + required: true + default: true + type: boolean + github_release: + description: 'Publish github release' + required: true + default: true + type: boolean defaults: run: @@ -70,15 +80,15 @@ jobs: run: | case "${{ steps.version.outputs.version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -136,6 +146,7 @@ jobs: run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive - name: Set staged rollout percentage + if: ${{ github.event.inputs.electron_publish }} env: RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} ROLLOUT_PCT: ${{ github.event.inputs.rollout_percentage }} @@ -145,7 +156,7 @@ jobs: echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml - name: Publish artifacts to S3 - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }} @@ -159,7 +170,7 @@ jobs: --quiet - name: Publish artifacts to R2 - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }} @@ -175,7 +186,7 @@ jobs: - name: Create Release uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 - if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }} + if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && github.event.inputs.github_release }} env: PKG_VERSION: ${{ steps.version.outputs.version }} RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} diff --git a/.github/workflows/staged-rollout-desktop.yml b/.github/workflows/staged-rollout-desktop.yml index d60fa700617..363fe134eee 100644 --- a/.github/workflows/staged-rollout-desktop.yml +++ b/.github/workflows/staged-rollout-desktop.yml @@ -54,20 +54,6 @@ jobs: r2-electron-bucket-name, cf-prod-account" - - name: Download channel update info files from S3 - env: - AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }} - AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }} - AWS_DEFAULT_REGION: 'us-west-2' - AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }} - run: | - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . \ - --quiet - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-linux.yml . \ - --quiet - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-mac.yml . \ - --quiet - - name: Download channel update info files from R2 env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} @@ -99,7 +85,7 @@ jobs: echo echo "If you want to pull a staged release because it hasn’t gone well, you must increment the version \ number higher than your broken release. Because some of your users will be on the broken 1.0.1, \ - releasing a new 1.0.1 would result in them staying on a broken version.” + releasing a new 1.0.1 would result in them staying on a broken version." exit 1 fi @@ -118,10 +104,14 @@ jobs: AWS_DEFAULT_REGION: 'us-west-2' AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }} run: | - aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \ - --include "latest*.yml" \ - --acl "public-read" \ - --quiet + aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" + + aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" + + aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" - name: Publish channel update info files to R2 env: @@ -131,7 +121,11 @@ jobs: AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }} CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }} run: | - aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \ - --include "latest*.yml" \ - --quiet \ + aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com + + aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com + + aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \ --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 3e5b5fc3c2b..7dee24e9202 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -36,7 +36,7 @@ jobs: NEW_VER=$CURR_MAJOR.$NEW_PATCH echo "New Version: $NEW_VER" - echo "::set-output name=new-version::$NEW_VER" + echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT trigger_version_bump: name: "Trigger desktop version bump workflow" diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 80d54eef145..f7ba508c6c3 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -56,7 +56,7 @@ jobs: VERSION: ${{ github.event.inputs.version_number }} run: | CLIENT=$(python -c "print('$CLIENT_NAME'.lower())") - echo "::set-output name=client::$CLIENT" + echo "client=$CLIENT" >> $GITHUB_OUTPUT git switch -c ${CLIENT}_version_bump_${VERSION} @@ -131,9 +131,9 @@ jobs: id: version-changed run: | if [ -n "$(git status --porcelain)" ]; then - echo "::set-output name=changes_to_commit::TRUE" + echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT else - echo "::set-output name=changes_to_commit::FALSE" + echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT echo "No changes to commit!"; fi @@ -142,8 +142,7 @@ jobs: env: CLIENT: ${{ steps.branch.outputs.client }} VERSION: ${{ github.event.inputs.version_number }} - run: | - git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a + run: git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a - name: Push changes if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} diff --git a/README.md b/README.md index c426251905b..a80918cf251 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository houses all Bitwarden client applications except the [Mobile application](https://github.com/bitwarden/mobile). -Please refer to the [Clients section](https://contributing.bitwarden.com/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Clients section](https://contributing.bitwarden.com/docs/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. ## Related projects: diff --git a/apps/browser/README.md b/apps/browser/README.md index 3ec5e544547..863839fd800 100644 --- a/apps/browser/README.md +++ b/apps/browser/README.md @@ -19,4 +19,4 @@ The Bitwarden browser extension is written using the Web Extension API and Angul ## Documentation -Please refer to the [Browser section](https://contributing.bitwarden.com/clients/browser/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Browser section](https://contributing.bitwarden.com/docs/clients/browser/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 6e4208b13f0..248608de651 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -482,7 +482,7 @@ "message": "الاسم مطلوب." }, "addedFolder": { - "message": "Folder added" + "message": "أُضيف المجلد" }, "changeMasterPass": { "message": "تغيير كلمة المرور الرئيسية" @@ -494,7 +494,7 @@ "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "editedFolder": { - "message": "Folder saved" + "message": "حُفظ المجلد" }, "deleteFolderConfirmation": { "message": "هل أنت متأكد من حذف هذا المجلّد؟" @@ -521,7 +521,7 @@ "message": "عنوان الـ URI" }, "uriPosition": { - "message": "URI $POSITION$", + "message": "رابط $POSITION$", "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", "placeholders": { "position": { @@ -571,13 +571,13 @@ "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "اطلب إضافة تسجيل الدخول" }, "addLoginNotificationDesc": { - "message": "Ask to add an item if one isn't found in your vault." + "message": "اطلب إضافة عنصر إذا لم يُعثر عليه في خزنتك." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "أظهر البطاقات في صفحة التبويبات" }, "showCardsCurrentTabDesc": { "message": "List card items on the Tab page for easy auto-fill." diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index b3a8a004a29..655d9e3d58e 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -149,7 +149,7 @@ "message": "Změnit hlavní heslo" }, "fingerprintPhrase": { - "message": "Fráze otisku účtu", + "message": "Unikátní přístupová fráze", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { @@ -606,7 +606,7 @@ "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." + "message": "Dotázat se na aktualizaci hesla pro přihlášení, pokud je na webové stránce zjištěno použití jiného hesla." }, "notificationChangeDesc": { "message": "Chcete aktualizovat toto heslo v Bitwarden?" @@ -618,7 +618,7 @@ "message": "Zobrazit v kontextovém menu" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Použijte pravé tlačítko pro přístup k vytvoření hesla a odpovídajícímu přihlášení pro tuto stránku. " }, "defaultUriMatchDetection": { "message": "Výchozí zjišťování shody URI", @@ -1043,10 +1043,10 @@ "message": "Zobrazit rozeznatelný obrázek vedle každého přihlášení." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Zobrazovat počet uložených přihlašovacích údajů na stránce" }, "badgeCounterDesc": { - "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku." + "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku na ikoně rozšíření prohlížeče." }, "cardholderName": { "message": "Jméno držitele karty" @@ -1632,7 +1632,7 @@ "message": "Odstraněné heslo" }, "deletedSend": { - "message": "Smazaný Send", + "message": "Send odstraněn", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -1899,10 +1899,10 @@ "message": "Vypršel časový limit relace. Vraťte se prosím zpět a zkuste se znovu přihlásit." }, "exportingPersonalVaultTitle": { - "message": "Exporting individual vault" + "message": "Export mého trezoru" }, "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "message": "Budou exportovány pouze položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", "placeholders": { "email": { "content": "$1", @@ -1970,16 +1970,16 @@ "message": "API klíč" }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "Chyba Key Connector: ujistěte se, že je Key Connector k dispozici a funguje správně." }, "premiumSubcriptionRequired": { "message": "Vyžadováno prémiové předplatné" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "Organizace je deaktivována." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "K položkám v deaktivované organizaci nemáte přístup. Požádejte o pomoc vlastníka organizace." }, "cardBrandMir": { "message": "Mir" @@ -2000,19 +2000,19 @@ "message": "Klikněte zde" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "pro obnovení do přednastavených nastavení" }, "serverVersion": { "message": "Verze serveru" }, "selfHosted": { - "message": "Self-hosted" + "message": "Vlastní hosting" }, "thirdParty": { - "message": "Third-party" + "message": "Tretí strana" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Připojeno k serveru třetí strany $SERVERNAME$. Ověřte chyby připojením na oficiální server nebo nahlaste problém správci serveru.", "placeholders": { "servername": { "content": "$1", diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 293c6e0559c..810efccc664 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -59,7 +59,7 @@ "message": "My vault" }, "allVaults": { - "message": "All Vaults" + "message": "All vaults" }, "tools": { "message": "Tools" @@ -92,13 +92,13 @@ "message": "Auto-fill" }, "generatePasswordCopied": { - "message": "Generate Password (and Copy)" + "message": "Generate password (copied)" }, "copyElementIdentifier": { - "message": "Copy Custom Field Name" + "message": "Copy custom field name" }, "noMatchingLogins": { - "message": "No matching logins." + "message": "No matching logins" }, "unlockVaultMenu": { "message": "Unlock your vault" diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index bc362924d83..e0dace731c5 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden – Ilmainen salasananhallinta", + "message": "Bitwarden - Free Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index c2d133c853f..3f273ce1d41 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -577,16 +577,16 @@ "message": "La \"Notification de demande d'ajout d'identifiant\" apparait automatiquement pour vous demander d'enregistrer dans votre coffre les identifiants que vous utilisez pour la première fois." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Afficher les cartes sur la page de l'onglet" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Lister les éléments de la carte sur la page de l'onglet pour faciliter le remplissage automatique." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Afficher les identités sur la page Onglet courant" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Lister les éléments d'identité sur la page de l'onglet pour faciliter le remplissage automatique." }, "clearClipboard": { "message": "Effacer le presse-papiers", @@ -618,7 +618,7 @@ "message": "Afficher les options du menu contextuel" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Utilisez un clic secondaire pour accéder à la génération de mots de passe et les identifiants correspondants pour le site Web. " }, "defaultUriMatchDetection": { "message": "Détection de correspondance URI par défaut", @@ -816,7 +816,7 @@ "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." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Demander la biométrie au lancement" }, "premiumRequired": { "message": "Version Premium requise" @@ -1043,10 +1043,10 @@ "message": "Afficher une image reconnaissable à côté de chaque identifiant." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Afficher le compteur de badge" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "Indique le nombre d'identifiants que vous avez pour la page web actuelle." }, "cardholderName": { "message": "Nom du titulaire de la carte" @@ -2000,19 +2000,19 @@ "message": "Cliquez ici" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "pour réinitialiser aux paramètres par défaut" }, "serverVersion": { - "message": "Server version" + "message": "Version du serveur" }, "selfHosted": { "message": "Auto-hébergé" }, "thirdParty": { - "message": "Third-party" + "message": "Tierce partie" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Connecté à l'implémentation du serveur tiers, $SERVERNAME$. Veuillez contrôler les bugs en utilisant le serveur officiel, ou rapportez-les au serveur tiers.", "placeholders": { "servername": { "content": "$1", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "vu pour la dernière fois le : $DATE$", "placeholders": { "date": { "content": "$1", @@ -2030,10 +2030,10 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Connectez-vous avec le mot de passe maître" }, "loggingInAs": { - "message": "Logging in as" + "message": "Connexion en tant que" }, "notYou": { "message": "Ce n'est pas vous ?" @@ -2042,6 +2042,6 @@ "message": "Êtes-vous nouveau ici ?" }, "rememberEmail": { - "message": "Remember email" + "message": "Se souvenir de l'e-mail" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 5682bb08e91..8b263c097c5 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -2042,6 +2042,6 @@ "message": "Jesteś tu nowy(a)?" }, "rememberEmail": { - "message": "Zapamiętaj email" + "message": "Zapamiętaj adres e-mail" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 746ec5540a9..35a1aa7a8fe 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -53,13 +53,13 @@ "message": "Zavihek" }, "vault": { - "message": "Sef" + "message": "Trezor" }, "myVault": { "message": "Moj trezor" }, "allVaults": { - "message": "All vaults" + "message": "Vsi trezorji" }, "tools": { "message": "Orodja" @@ -101,7 +101,7 @@ "message": "Nobenih ujemajočih prijav." }, "unlockVaultMenu": { - "message": "Unlock your vault" + "message": "Odkleni svoj trezor" }, "loginToVaultMenu": { "message": "Log in to your vault" @@ -131,10 +131,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send code" + "message": "Pošlji kodo" }, "codeSent": { - "message": "Code sent" + "message": "Koda poslana" }, "verificationCode": { "message": "Verifikacijska koda" @@ -242,10 +242,10 @@ "message": "Lowercase (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "Številke (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Posebni znaki (!@#$%^&*)" }, "numWords": { "message": "Število besed" @@ -864,7 +864,7 @@ "message": "To start the WebAuthn 2FA verification. Click the button below to open a new tab and follow the instructions provided in the new tab." }, "webAuthnNewTabOpen": { - "message": "Open new tab" + "message": "Odpri nov zavihek" }, "webAuthnAuthenticate": { "message": "Authenticate WebAuthn" @@ -879,10 +879,10 @@ "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "Možnosti dvostopenjske prijave" }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." + "message": "Ste izgubili dostop do vseh vaših ponudnikov dvostopenjske prijave? Uporabite svojo kodo za obnovitev in tako onemogočite dvostopenjsko prijavo v svoj račun." }, "recoveryCodeTitle": { "message": "Koda za obnovitev" @@ -927,7 +927,7 @@ "message": "Specify the base URL of your on-premises hosted Bitwarden installation." }, "customEnvironment": { - "message": "Custom environment" + "message": "Okolje po meri" }, "customEnvironmentFooter": { "message": "For advanced users. You can specify the base URL of each service independently." @@ -993,7 +993,7 @@ "message": "Generate and copy a new random password to the clipboard" }, "commandLockVaultDesc": { - "message": "Lock the vault" + "message": "Zakleni trezor" }, "privateModeWarning": { "message": "Private mode support is experimental and some features are limited." @@ -1002,10 +1002,10 @@ "message": "Custom fields" }, "copyValue": { - "message": "Copy value" + "message": "Kopiraj vrednost" }, "value": { - "message": "Value" + "message": "Vrednost" }, "newCustomField": { "message": "New custom field" @@ -1020,7 +1020,7 @@ "message": "Skrito" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "Logična vrednost" }, "cfTypeLinked": { "message": "Linked", @@ -1133,7 +1133,7 @@ "message": "Priimek" }, "fullName": { - "message": "Full name" + "message": "Polno ime" }, "identityName": { "message": "Ime identitete" @@ -1190,22 +1190,22 @@ "message": "Prijave" }, "typeSecureNote": { - "message": "Secure note" + "message": "Varni zapisek" }, "typeCard": { - "message": "Card" + "message": "Kartica" }, "typeIdentity": { - "message": "Identity" + "message": "Identiteta" }, "passwordHistory": { - "message": "Password history" + "message": "Zgodovina gesel" }, "back": { - "message": "Back" + "message": "Nazaj" }, "collections": { - "message": "Collections" + "message": "Zbirke" }, "favorites": { "message": "Priljubljeno" @@ -1226,7 +1226,7 @@ "message": "Prijave" }, "secureNotes": { - "message": "Secure notes" + "message": "Varni zapiski" }, "clear": { "message": "Počisti", @@ -1285,15 +1285,15 @@ "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { - "message": "Current URI", + "message": "Trenutni URI", "description": "The URI of one of the current open tabs in the browser." }, "organization": { - "message": "Organization", + "message": "Organizacija", "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Types" + "message": "Tipi" }, "allItems": { "message": "All items" @@ -1302,13 +1302,13 @@ "message": "There are no passwords to list." }, "remove": { - "message": "Remove" + "message": "Odstrani" }, "default": { - "message": "Default" + "message": "Privzeto" }, "dateUpdated": { - "message": "Updated", + "message": "Posodobljeno", "description": "ex. Date this item was updated" }, "dateCreated": { @@ -1329,25 +1329,25 @@ "message": "There are no collections to list." }, "ownership": { - "message": "Ownership" + "message": "Lastništvo" }, "whoOwnsThisItem": { "message": "Who owns this item?" }, "strong": { - "message": "Strong", + "message": "Močno", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Good", + "message": "Dobro", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "Weak", + "message": "Šibko", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Weak master password" + "message": "Šibko glavno geslo" }, "weakMasterPasswordDesc": { "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" @@ -1357,19 +1357,19 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Odkleni s PIN kodo" }, "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": "Potrebna je PIN koda." }, "invalidPin": { - "message": "Invalid PIN code." + "message": "Nepravilna PIN koda." }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Prijava z biometriko" }, "awaitDesktop": { "message": "Awaiting confirmation from desktop" @@ -1387,7 +1387,7 @@ "message": "Clone item" }, "clone": { - "message": "Clone" + "message": "Kloniraj" }, "passwordGeneratorPolicyInEffect": { "message": "One or more organization policies are affecting your generator settings." @@ -1396,15 +1396,15 @@ "message": "Vault timeout action" }, "lock": { - "message": "Lock", + "message": "Zakleni", "description": "Verb form: to make secure or inaccesible by" }, "trash": { - "message": "Trash", + "message": "Koš", "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search trash" + "message": "Preišči koš" }, "permanentlyDeleteItem": { "message": "Permanently delete item" @@ -1440,7 +1440,7 @@ "message": "Item auto-filled " }, "setMasterPassword": { - "message": "Set master password" + "message": "Nastavi glavno geslo" }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" @@ -1494,13 +1494,13 @@ "message": "Terms of Service" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "Pravilnik o zasebnosti" }, "hintEqualsPassword": { "message": "Your password hint cannot be the same as your password." }, "ok": { - "message": "Ok" + "message": "V redu" }, "desktopSyncVerificationTitle": { "message": "Desktop sync verification" @@ -1584,7 +1584,7 @@ } }, "send": { - "message": "Send", + "message": "Pošlji", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { @@ -1596,10 +1596,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { - "message": "Text" + "message": "Besedilo" }, "sendTypeFile": { - "message": "File" + "message": "Datoteka" }, "allSends": { "message": "All Sends", @@ -1610,7 +1610,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "expired": { - "message": "Expired" + "message": "Poteklo" }, "pendingDeletion": { "message": "Pending deletion" @@ -1626,7 +1626,7 @@ "message": "Remove Password" }, "delete": { - "message": "Delete" + "message": "Izbriši" }, "removedPassword": { "message": "Password removed" @@ -1636,11 +1636,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Send link", + "message": "Pošlji povezavo", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { - "message": "Disabled" + "message": "Onemogočeno" }, "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" @@ -1683,10 +1683,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { - "message": "1 day" + "message": "1 dan" }, "days": { - "message": "$DAYS$ days", + "message": "$DAYS$ dni", "placeholders": { "days": { "content": "$1", @@ -1835,10 +1835,10 @@ "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." }, "hours": { - "message": "Hours" + "message": "Ur" }, "minutes": { - "message": "Minutes" + "message": "Minut" }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", @@ -1911,7 +1911,7 @@ } }, "error": { - "message": "Error" + "message": "Napaka" }, "regenerateUsername": { "message": "Regenerate username" @@ -1936,13 +1936,13 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Naključno" }, "randomWord": { - "message": "Random word" + "message": "Naključna beseda" }, "websiteName": { - "message": "Website name" + "message": "Ime spletne strani" }, "whatWouldYouLikeToGenerate": { "message": "What would you like to generate?" @@ -1997,13 +1997,13 @@ "message": "Settings have been edited" }, "environmentEditedClick": { - "message": "Click here" + "message": "Kliknite tukaj" }, "environmentEditedReset": { "message": "to reset to pre-configured settings" }, "serverVersion": { - "message": "Server version" + "message": "Verzija strežnika" }, "selfHosted": { "message": "Self-hosted" @@ -2036,12 +2036,12 @@ "message": "Logging in as" }, "notYou": { - "message": "Not you?" + "message": "Niste vi?" }, "newAroundHere": { "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "Zapomni si e-pošto" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 37020aa2a7e..fc595643ba0 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -2012,7 +2012,7 @@ "message": "Трећа страна" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Повезан са имплементацијом сервера треће стране, $SERVERNAME$. Проверите грешке користећи званични сервер или их пријавите серверу треће стране.", "placeholders": { "servername": { "content": "$1", diff --git a/apps/browser/src/alarms/alarm-state.ts b/apps/browser/src/alarms/alarm-state.ts new file mode 100644 index 00000000000..774bcfe1c03 --- /dev/null +++ b/apps/browser/src/alarms/alarm-state.ts @@ -0,0 +1,66 @@ +import { BrowserApi } from "../browser/browserApi"; +import { clearClipboardAlarmName } from "../clipboard"; + +export const alarmKeys = [clearClipboardAlarmName] as const; +export type AlarmKeys = typeof alarmKeys[number]; + +type AlarmState = { [T in AlarmKeys]: number | undefined }; + +const alarmState: AlarmState = { + clearClipboard: null, + //TODO once implemented vaultTimeout: null; + //TODO once implemented checkNotifications: null; + //TODO once implemented (if necessary) processReload: null; +}; + +/** + * Retrieves the set alarm time (planned execution) for a give an commandName {@link AlarmState} + * @param commandName A command that has been previously registered with {@link AlarmState} + * @returns {Promise} null or Unix epoch timestamp when the alarm action is supposed to execute + * @example + * // getAlarmTime(clearClipboard) + */ +export async function getAlarmTime(commandName: AlarmKeys): Promise { + let alarmTime: number; + if (BrowserApi.manifestVersion == 3) { + const fromSessionStore = await chrome.storage.session.get(commandName); + alarmTime = fromSessionStore[commandName]; + } else { + alarmTime = alarmState[commandName]; + } + + return alarmTime; +} + +/** + * Registers an action that should execute after the given time has passed + * @param commandName A command that has been previously registered with {@link AlarmState} + * @param delay_ms The number of ms from now in which the command should execute from + * @example + * // setAlarmTime(clearClipboard, 5000) register the clearClipboard action which will execute when at least 5 seconds from now have passed + */ +export async function setAlarmTime(commandName: AlarmKeys, delay_ms: number): Promise { + if (!delay_ms || delay_ms === 0) { + await this.clearAlarmTime(commandName); + return; + } + + const time = Date.now() + delay_ms; + await setAlarmTimeInternal(commandName, time); +} + +/** + * Clears the time currently set for a given command + * @param commandName A command that has been previously registered with {@link AlarmState} + */ +export async function clearAlarmTime(commandName: AlarmKeys): Promise { + await setAlarmTimeInternal(commandName, null); +} + +async function setAlarmTimeInternal(commandName: AlarmKeys, time: number): Promise { + if (BrowserApi.manifestVersion == 3) { + await chrome.storage.session.set({ [commandName]: time }); + } else { + alarmState[commandName] = time; + } +} diff --git a/apps/browser/src/alarms/on-alarm-listener.ts b/apps/browser/src/alarms/on-alarm-listener.ts new file mode 100644 index 00000000000..4b246908778 --- /dev/null +++ b/apps/browser/src/alarms/on-alarm-listener.ts @@ -0,0 +1,26 @@ +import { ClearClipboard, clearClipboardAlarmName } from "../clipboard"; + +import { alarmKeys, clearAlarmTime, getAlarmTime } from "./alarm-state"; + +export const onAlarmListener = async (alarm: chrome.alarms.Alarm) => { + alarmKeys.forEach(async (key) => { + const executionTime = await getAlarmTime(key); + if (!executionTime) { + return; + } + + const currentDate = Date.now(); + if (executionTime > currentDate) { + return; + } + + await clearAlarmTime(key); + + switch (key) { + case clearClipboardAlarmName: + ClearClipboard.run(); + break; + default: + } + }); +}; diff --git a/apps/browser/src/alarms/register-alarms.ts b/apps/browser/src/alarms/register-alarms.ts new file mode 100644 index 00000000000..c4da2fc27bc --- /dev/null +++ b/apps/browser/src/alarms/register-alarms.ts @@ -0,0 +1,29 @@ +const NUMBER_OF_ALARMS = 6; + +export function registerAlarms() { + alarmsToBeCreated(NUMBER_OF_ALARMS); +} + +/** + * Creates staggered alarms that periodically (1min) raise OnAlarm events. The staggering is calculated based on the numnber of alarms passed in. + * @param numberOfAlarms Number of named alarms, that shall be registered + * @example + * // alarmsToBeCreated(2) results in 2 alarms separated by 30 seconds + * @example + * // alarmsToBeCreated(4) results in 4 alarms separated by 15 seconds + * @example + * // alarmsToBeCreated(6) results in 6 alarms separated by 10 seconds + * @example + * // alarmsToBeCreated(60) results in 60 alarms separated by 1 second + */ +function alarmsToBeCreated(numberOfAlarms: number): void { + const oneMinuteInMs = 60 * 1000; + const offset = oneMinuteInMs / numberOfAlarms; + + let calculatedWhen: number = Date.now() + offset; + + for (let index = 0; index < numberOfAlarms; index++) { + chrome.alarms.create(`bw_alarm${index}`, { periodInMinutes: 1, when: calculatedWhen }); + calculatedWhen += offset; + } +} diff --git a/apps/browser/src/background.ts b/apps/browser/src/background.ts index 504b5afb5c0..fc020b19aa3 100644 --- a/apps/browser/src/background.ts +++ b/apps/browser/src/background.ts @@ -1,6 +1,7 @@ +import { onAlarmListener } from "./alarms/on-alarm-listener"; +import { registerAlarms } from "./alarms/register-alarms"; import MainBackground from "./background/main.background"; import { BrowserApi } from "./browser/browserApi"; -import { ClearClipboard } from "./clipboard"; import { onCommandListener } from "./listeners/onCommandListener"; import { onInstallListener } from "./listeners/onInstallListener"; import { UpdateBadge } from "./listeners/update-badge"; @@ -9,13 +10,12 @@ const manifestV3MessageListeners: (( serviceCache: Record, message: { command: string } ) => void | Promise)[] = [UpdateBadge.messageListener]; -type AlarmAction = (executionTime: Date, serviceCache: Record) => void; - -const AlarmActions: AlarmAction[] = [ClearClipboard.run]; if (BrowserApi.manifestVersion === 3) { chrome.commands.onCommand.addListener(onCommandListener); chrome.runtime.onInstalled.addListener(onInstallListener); + chrome.alarms.onAlarm.addListener(onAlarmListener); + registerAlarms(); chrome.tabs.onActivated.addListener(UpdateBadge.tabsOnActivatedListener); chrome.tabs.onReplaced.addListener(UpdateBadge.tabsOnReplacedListener); chrome.tabs.onUpdated.addListener(UpdateBadge.tabsOnUpdatedListener); @@ -26,14 +26,6 @@ if (BrowserApi.manifestVersion === 3) { listener(serviceCache, message); }); }); - chrome.alarms.onAlarm.addListener((_alarm) => { - const executionTime = new Date(); - const serviceCache = {}; - - for (const alarmAction of AlarmActions) { - alarmAction(executionTime, serviceCache); - } - }); } else { const bitwardenMain = ((window as any).bitwardenMain = new MainBackground()); bitwardenMain.bootstrap().then(() => { diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 56e2e2dd7e1..1a8c5ae5c50 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -1,7 +1,7 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; const IdleInterval = 60 * 5; // 5 minutes @@ -12,7 +12,7 @@ export default class IdleBackground { constructor( private vaultTimeoutService: VaultTimeoutService, - private stateService: StateService, + private stateService: BrowserStateService, private notificationsService: NotificationsService ) { this.idle = chrome.idle || (browser != null ? browser.idle : null); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 581da587e45..2d370c91bb7 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -61,14 +61,11 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service"; import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; import { ProviderService } from "@bitwarden/common/services/provider.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { SendService } from "@bitwarden/common/services/send.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service"; import { SyncService } from "@bitwarden/common/services/sync/sync.service"; import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service"; @@ -89,19 +86,22 @@ import { UpdateBadge } from "../listeners/update-badge"; import { Account } from "../models/account"; import { PopupUtilsService } from "../popup/services/popup-utils.service"; import { AutofillService as AutofillServiceAbstraction } from "../services/abstractions/autofill.service"; -import { StateService as StateServiceAbstraction } from "../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../services/abstractions/browser-state.service"; import AutofillService from "../services/autofill.service"; import { BrowserEnvironmentService } from "../services/browser-environment.service"; +import { BrowserFolderService } from "../services/browser-folder.service"; +import { BrowserOrganizationService } from "../services/browser-organization.service"; +import { BrowserPolicyService } from "../services/browser-policy.service"; +import { BrowserSettingsService } from "../services/browser-settings.service"; +import { BrowserStateService } from "../services/browser-state.service"; import { BrowserCryptoService } from "../services/browserCrypto.service"; import BrowserLocalStorageService from "../services/browserLocalStorage.service"; import BrowserMessagingService from "../services/browserMessaging.service"; import BrowserMessagingPrivateModeBackgroundService from "../services/browserMessagingPrivateModeBackground.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; -import { FolderService } from "../services/folders/folder.service"; import I18nService from "../services/i18n.service"; import { KeyGenerationService } from "../services/keyGeneration.service"; import { LocalBackedSessionStorageService } from "../services/localBackedSessionStorage.service"; -import { StateService } from "../services/state.service"; import { VaultFilterService } from "../services/vaultFilter.service"; import VaultTimeoutService from "../services/vaultTimeout/vaultTimeout.service"; @@ -227,7 +227,7 @@ export default class MainBackground { this.secureStorageService, new StateFactory(GlobalState, Account) ); - this.stateService = new StateService( + this.stateService = new BrowserStateService( this.storageService, this.secureStorageService, this.memoryStorageService, @@ -282,7 +282,7 @@ export default class MainBackground { this.appIdService, (expired: boolean) => this.logout(expired) ); - this.settingsService = new SettingsService(this.stateService); + this.settingsService = new BrowserSettingsService(this.stateService); this.fileUploadService = new FileUploadService(this.logService, this.apiService); this.cipherService = new CipherService( this.cryptoService, @@ -295,7 +295,7 @@ export default class MainBackground { this.stateService, this.encryptService ); - this.folderService = new FolderService( + this.folderService = new BrowserFolderService( this.cryptoService, this.i18nService, this.cipherService, @@ -317,8 +317,8 @@ export default class MainBackground { this.stateService ); this.syncNotifierService = new SyncNotifierService(); - this.organizationService = new OrganizationService(this.stateService); - this.policyService = new PolicyService(this.stateService, this.organizationService); + this.organizationService = new BrowserOrganizationService(this.stateService); + this.policyService = new BrowserPolicyService(this.stateService, this.organizationService); this.policyApiService = new PolicyApiService( this.policyService, this.apiService, diff --git a/apps/browser/src/background/notification.background.ts b/apps/browser/src/background/notification.background.ts index 127fc5203d7..2da4791857d 100644 --- a/apps/browser/src/background/notification.background.ts +++ b/apps/browser/src/background/notification.background.ts @@ -15,7 +15,7 @@ import { LoginView } from "@bitwarden/common/models/view/login.view"; import { BrowserApi } from "../browser/browserApi"; import { AutofillService } from "../services/abstractions/autofill.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import AddChangePasswordQueueMessage from "./models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "./models/addLoginQueueMessage"; @@ -33,7 +33,7 @@ export default class NotificationBackground { private authService: AuthService, private policyService: PolicyService, private folderService: FolderService, - private stateService: StateService + private stateService: BrowserStateService ) {} async init() { diff --git a/apps/browser/src/background/service_factories/folder-service.factory.ts b/apps/browser/src/background/service_factories/folder-service.factory.ts index a7c90d234b1..bb35970325f 100644 --- a/apps/browser/src/background/service_factories/folder-service.factory.ts +++ b/apps/browser/src/background/service_factories/folder-service.factory.ts @@ -1,6 +1,6 @@ import { FolderService as AbstractFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; -import { FolderService } from "../../services/folders/folder.service"; +import { BrowserFolderService } from "../../services/browser-folder.service"; import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; import { cryptoServiceFactory, CryptoServiceInitOptions } from "./crypto-service.factory"; @@ -28,7 +28,7 @@ export function folderServiceFactory( "folderService", opts, async () => - new FolderService( + new BrowserFolderService( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cipherServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service_factories/organization-service.factory.ts b/apps/browser/src/background/service_factories/organization-service.factory.ts index ea11d32e26a..4f2eaee8058 100644 --- a/apps/browser/src/background/service_factories/organization-service.factory.ts +++ b/apps/browser/src/background/service_factories/organization-service.factory.ts @@ -1,5 +1,6 @@ import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; + +import { BrowserOrganizationService } from "../../services/browser-organization.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -17,6 +18,6 @@ export function organizationServiceFactory( cache, "organizationService", opts, - async () => new OrganizationService(await stateServiceFactory(cache, opts)) + async () => new BrowserOrganizationService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/policy-service.factory.ts b/apps/browser/src/background/service_factories/policy-service.factory.ts index d4940bef259..d20bca3c62e 100644 --- a/apps/browser/src/background/service_factories/policy-service.factory.ts +++ b/apps/browser/src/background/service_factories/policy-service.factory.ts @@ -1,5 +1,6 @@ import { PolicyService as AbstractPolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; + +import { BrowserPolicyService } from "../../services/browser-policy.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { @@ -26,7 +27,7 @@ export function policyServiceFactory( "policyService", opts, async () => - new PolicyService( + new BrowserPolicyService( await stateServiceFactory(cache, opts), await organizationServiceFactory(cache, opts) ) diff --git a/apps/browser/src/background/service_factories/settings-service.factory.ts b/apps/browser/src/background/service_factories/settings-service.factory.ts index 745a6d08d60..73e0ae52032 100644 --- a/apps/browser/src/background/service_factories/settings-service.factory.ts +++ b/apps/browser/src/background/service_factories/settings-service.factory.ts @@ -1,5 +1,6 @@ import { SettingsService as AbstractSettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; + +import { BrowserSettingsService } from "../../services/browser-settings.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -16,6 +17,6 @@ export function settingsServiceFactory( cache, "settingsService", opts, - async () => new SettingsService(await stateServiceFactory(cache, opts)) + async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/state-service.factory.ts b/apps/browser/src/background/service_factories/state-service.factory.ts index 1b81567ac52..6d2c5cb4fa7 100644 --- a/apps/browser/src/background/service_factories/state-service.factory.ts +++ b/apps/browser/src/background/service_factories/state-service.factory.ts @@ -2,7 +2,7 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { Account } from "../../models/account"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; @@ -34,15 +34,15 @@ export type StateServiceInitOptions = StateServiceFactoryOptions & StateMigrationServiceInitOptions; export async function stateServiceFactory( - cache: { stateService?: StateService } & CachedServices, + cache: { stateService?: BrowserStateService } & CachedServices, opts: StateServiceInitOptions -): Promise { +): Promise { const service = await factory( cache, "stateService", opts, async () => - await new StateService( + await new BrowserStateService( await diskStorageServiceFactory(cache, opts), await secureStorageServiceFactory(cache, opts), await memoryStorageServiceFactory(cache, opts), diff --git a/apps/browser/src/clipboard/clear-clipboard.spec.ts b/apps/browser/src/clipboard/clear-clipboard.spec.ts new file mode 100644 index 00000000000..0166bdb47af --- /dev/null +++ b/apps/browser/src/clipboard/clear-clipboard.spec.ts @@ -0,0 +1,39 @@ +import { BrowserApi } from "../browser/browserApi"; + +import { ClearClipboard } from "./clear-clipboard"; + +describe("clearClipboard", () => { + describe("run", () => { + it("Does not clear clipboard when no active tabs are retrieved", async () => { + jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([] as any); + + jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); + + await ClearClipboard.run(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).not.toHaveBeenCalled(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).not.toHaveBeenCalledWith(1, { + command: "clearClipboard", + }); + }); + + it("Sends a message to the content script to clear the clipboard", async () => { + jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([ + { + id: 1, + }, + ] as any); + + jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); + + await ClearClipboard.run(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledTimes(1); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledWith(1, { + command: "clearClipboard", + }); + }); + }); +}); diff --git a/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled b/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled deleted file mode 100644 index 6f7314d91ac..00000000000 --- a/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled +++ /dev/null @@ -1,79 +0,0 @@ -import { mock, MockProxy } from "jest-mock-extended"; - -import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; - -import { ClearClipboard } from "./clear-clipboard"; -import { getClearClipboardTime, setClearClipboardTime } from "./clipboard-state"; - -jest.mock("./clipboard-state", () => { - return { - getClearClipboardTime: jest.fn(), - setClearClipboardTime: jest.fn(), - }; -}); - -const getClearClipboardTimeMock = getClearClipboardTime as jest.Mock; -const setClearClipboardTimeMock = setClearClipboardTime as jest.Mock; - -describe("clearClipboard", () => { - describe("run", () => { - let stateService: MockProxy; - let serviceCache: Record; - - beforeEach(() => { - stateService = mock(); - serviceCache = { - stateService: stateService, - }; - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it("has a clear time that is past execution time", async () => { - const executionTime = new Date(2022, 1, 1, 12); - const clearTime = new Date(2022, 1, 1, 12, 1); - - jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([ - { - id: 1, - }, - ] as any); - - jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); - - getClearClipboardTimeMock.mockResolvedValue(clearTime.getTime()); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledTimes(1); - - expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledWith(1, { - command: "clearClipboard", - }); - }); - - it("has a clear time before execution time", async () => { - const executionTime = new Date(2022, 1, 1, 12); - const clearTime = new Date(2022, 1, 1, 11); - - setClearClipboardTimeMock.mockResolvedValue(clearTime.getTime()); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "getActiveTabs")).not.toHaveBeenCalled(); - }); - - it("has an undefined clearTime", async () => { - const executionTime = new Date(2022, 1, 1); - - getClearClipboardTimeMock.mockResolvedValue(undefined); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "getActiveTabs")).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/browser/src/clipboard/clear-clipboard.ts b/apps/browser/src/clipboard/clear-clipboard.ts index 00bf329f8d3..149992555b9 100644 --- a/apps/browser/src/clipboard/clear-clipboard.ts +++ b/apps/browser/src/clipboard/clear-clipboard.ts @@ -1,43 +1,15 @@ -import { StateFactory } from "@bitwarden/common/factories/stateFactory"; -import { GlobalState } from "@bitwarden/common/models/domain/global-state"; - -import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; -import { Account } from "../models/account"; -import { getClearClipboardTime } from "./clipboard-state"; +export const clearClipboardAlarmName = "clearClipboard"; export class ClearClipboard { - static async run(executionTime: Date, serviceCache: Record) { - const stateFactory = new StateFactory(GlobalState, Account); - const stateService = await stateServiceFactory(serviceCache, { - cryptoFunctionServiceOptions: { - win: self, - }, - encryptServiceOptions: { - logMacFailures: false, - }, - logServiceOptions: { - isDev: false, - }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, - stateServiceOptions: { - stateFactory: stateFactory, - }, - }); - - const clearClipboardTime = await getClearClipboardTime(stateService); - - if (!clearClipboardTime) { - return; - } - - if (clearClipboardTime < executionTime.getTime()) { - return; - } - + /** + We currently rely on an active tab with an injected content script (`../content/misc-utils.ts`) to clear the clipboard via `window.navigator.clipboard.writeText(text)` + + With https://bugs.chromium.org/p/chromium/issues/detail?id=1160302 it was said that service workers, + would have access to the clipboard api and then we could migrate to a simpler solution + */ + static async run() { const activeTabs = await BrowserApi.getActiveTabs(); if (!activeTabs || activeTabs.length === 0) { return; diff --git a/apps/browser/src/clipboard/clipboard-state.ts b/apps/browser/src/clipboard/clipboard-state.ts index a1c15addc0a..cfa2f9459f8 100644 --- a/apps/browser/src/clipboard/clipboard-state.ts +++ b/apps/browser/src/clipboard/clipboard-state.ts @@ -1,10 +1,10 @@ -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; const clearClipboardStorageKey = "clearClipboardTime"; -export const getClearClipboardTime = async (stateService: StateService) => { +export const getClearClipboardTime = async (stateService: BrowserStateService) => { return await stateService.getFromSessionMemory(clearClipboardStorageKey); }; -export const setClearClipboardTime = async (stateService: StateService, time: number) => { +export const setClearClipboardTime = async (stateService: BrowserStateService, time: number) => { await stateService.setInSessionMemory(clearClipboardStorageKey, time); }; diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts index e9c2141211f..5d119513f43 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts @@ -2,30 +2,30 @@ import { mock, MockProxy } from "jest-mock-extended"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; +import { setAlarmTime } from "../alarms/alarm-state"; import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; -import { setClearClipboardTime } from "./clipboard-state"; +import { clearClipboardAlarmName } from "./clear-clipboard"; import { GeneratePasswordToClipboardCommand } from "./generate-password-to-clipboard-command"; -jest.mock("./clipboard-state", () => { +jest.mock("../alarms/alarm-state", () => { return { - getClearClipboardTime: jest.fn(), - setClearClipboardTime: jest.fn(), + setAlarmTime: jest.fn(), }; }); -const setClearClipboardTimeMock = setClearClipboardTime as jest.Mock; +const setAlarmTimeMock = setAlarmTime as jest.Mock; describe("GeneratePasswordToClipboardCommand", () => { let passwordGenerationService: MockProxy; - let stateService: MockProxy; + let stateService: MockProxy; let sut: GeneratePasswordToClipboardCommand; beforeEach(() => { passwordGenerationService = mock(); - stateService = mock(); + stateService = mock(); passwordGenerationService.getOptions.mockResolvedValue([{ length: 8 }, {} as any]); @@ -53,9 +53,9 @@ describe("GeneratePasswordToClipboardCommand", () => { text: "PASSWORD", }); - expect(setClearClipboardTimeMock).toHaveBeenCalledTimes(1); + expect(setAlarmTimeMock).toHaveBeenCalledTimes(1); - expect(setClearClipboardTimeMock).toHaveBeenCalledWith(stateService, expect.any(Number)); + expect(setAlarmTimeMock).toHaveBeenCalledWith(clearClipboardAlarmName, expect.any(Number)); }); it("does not have clear clipboard value", async () => { @@ -70,7 +70,7 @@ describe("GeneratePasswordToClipboardCommand", () => { text: "PASSWORD", }); - expect(setClearClipboardTimeMock).not.toHaveBeenCalled(); + expect(setAlarmTimeMock).not.toHaveBeenCalled(); }); }); }); diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts index ca92d2c686f..0cd8eec2c93 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts @@ -1,14 +1,15 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { StateService } from "../services/abstractions/state.service"; +import { setAlarmTime } from "../alarms/alarm-state"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; -import { setClearClipboardTime } from "./clipboard-state"; +import { clearClipboardAlarmName } from "./clear-clipboard"; import { copyToClipboard } from "./copy-to-clipboard-command"; export class GeneratePasswordToClipboardCommand { constructor( private passwordGenerationService: PasswordGenerationService, - private stateService: StateService + private stateService: BrowserStateService ) {} async generatePasswordToClipboard(tab: chrome.tabs.Tab) { @@ -20,7 +21,7 @@ export class GeneratePasswordToClipboardCommand { const clearClipboard = await this.stateService.getClearClipboard(); if (clearClipboard != null) { - await setClearClipboardTime(this.stateService, Date.now() + clearClipboard * 1000); + await setAlarmTime(clearClipboardAlarmName, clearClipboard * 1000); } } } diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts index cc8a5618760..92c5dfb0170 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from "rxjs"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { browserSession } from "./browser-session.decorator"; import { SessionStorable } from "./session-storable"; @@ -22,25 +22,25 @@ describe("browserSession decorator", () => { }); it("should create if StateService is a constructor argument", () => { - const stateService = Object.create(StateService.prototype, {}); + const stateService = Object.create(BrowserStateService.prototype, {}); @browserSession class TestClass { - constructor(private stateService: StateService) {} + constructor(private stateService: BrowserStateService) {} } expect(new TestClass(stateService)).toBeDefined(); }); describe("interaction with @sessionSync decorator", () => { - let stateService: StateService; + let stateService: BrowserStateService; @browserSession class TestClass { @sessionSync({ initializer: (s: string) => s }) private behaviorSubject = new BehaviorSubject(""); - constructor(private stateService: StateService) {} + constructor(private stateService: BrowserStateService) {} fromJSON(json: any) { this.behaviorSubject.next(json); @@ -48,7 +48,7 @@ describe("browserSession decorator", () => { } beforeEach(() => { - stateService = Object.create(StateService.prototype, {}) as StateService; + stateService = Object.create(BrowserStateService.prototype, {}) as BrowserStateService; }); it("should create a session syncer", () => { diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts index 73cdf767357..5d9d56c1d71 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts @@ -1,6 +1,6 @@ import { Constructor } from "type-fest"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { SessionStorable } from "./session-storable"; import { SessionSyncer } from "./session-syncer"; @@ -22,7 +22,13 @@ export function browserSession>(constructor: TCto super(...args); // Require state service to be injected - const stateService = args.find((arg) => arg instanceof StateService); + const stateService: BrowserStateService = [this as any] + .concat(args) + .find( + (arg) => + typeof arg.setInSessionMemory === "function" && + typeof arg.getFromSessionMemory === "function" + ); if (!stateService) { throw new Error( `Cannot decorate ${constructor.name} with browserSession, Browser's StateService must be injected` @@ -38,7 +44,7 @@ export function browserSession>(constructor: TCto ); } - buildSyncer(metadata: SyncedItemMetadata, stateService: StateService) { + buildSyncer(metadata: SyncedItemMetadata, stateService: BrowserStateService) { const syncer = new SessionSyncer((this as any)[metadata.propertyKey], stateService, metadata); syncer.init(); return syncer; diff --git a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts index 38ec7aac9a1..c6d46b28fd1 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts @@ -8,9 +8,12 @@ describe("sessionSync decorator", () => { class TestClass { @sessionSync({ ctor: ctor, initializer: initializer }) private testProperty = new BehaviorSubject(""); + @sessionSync({ ctor: ctor, initializer: initializer, initializeAs: "array" }) + private secondTestProperty = new BehaviorSubject(""); complete() { this.testProperty.complete(); + this.secondTestProperty.complete(); } } @@ -19,11 +22,40 @@ describe("sessionSync decorator", () => { expect((testClass as any).__syncedItemMetadata).toEqual([ expect.objectContaining({ propertyKey: "testProperty", - sessionKey: "TestClass_testProperty", + sessionKey: "testProperty_0", ctor: ctor, initializer: initializer, }), - testClass.complete(), + expect.objectContaining({ + propertyKey: "secondTestProperty", + sessionKey: "secondTestProperty_1", + ctor: ctor, + initializer: initializer, + initializeAs: "array", + }), ]); + testClass.complete(); + }); + + class TestClass2 { + @sessionSync({ ctor: ctor, initializer: initializer }) + private testProperty = new BehaviorSubject(""); + + complete() { + this.testProperty.complete(); + } + } + + it("should maintain sessionKey index count for other test classes", () => { + const testClass = new TestClass2(); + expect((testClass as any).__syncedItemMetadata).toEqual([ + expect.objectContaining({ + propertyKey: "testProperty", + sessionKey: "testProperty_2", + ctor: ctor, + initializer: initializer, + }), + ]); + testClass.complete(); }); }); diff --git a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts index df0764528f7..6a6b3d33fa1 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts @@ -1,13 +1,17 @@ import { Jsonify } from "type-fest"; import { SessionStorable } from "./session-storable"; +import { InitializeOptions } from "./sync-item-metadata"; -class BuildOptions { +class BuildOptions> { ctor?: new () => T; - initializer?: (keyValuePair: Jsonify) => T; - initializeAsArray? = false; + initializer?: (keyValuePair: TJson) => T; + initializeAs?: InitializeOptions; } +// Used to ensure uniqueness for each synced observable +let index = 0; + /** * A decorator used to indicate the BehaviorSubject should be synced for this browser session across all contexts. * @@ -20,10 +24,10 @@ class BuildOptions { * @param buildOptions * Builders for the value, requires either a constructor (ctor) for your BehaviorSubject type or an * initializer function that takes a key value pair representation of the BehaviorSubject data - * and returns your instantiated BehaviorSubject value. `initializeAsArray can optionally be used to indicate + * and returns your instantiated BehaviorSubject value. `initializeAs can optionally be used to indicate * the provided initializer function should be used to build an array of values. For example, * ```ts - * \@sessionSync({ initializer: Foo.fromJSON, initializeAsArray: true }) + * \@sessionSync({ initializer: Foo.fromJSON, initializeAs: 'array' }) * ``` * is equivalent to * ``` @@ -43,10 +47,10 @@ export function sessionSync(buildOptions: BuildOptions) { p.__syncedItemMetadata.push({ propertyKey, - sessionKey: `${prototype.constructor.name}_${propertyKey}`, + sessionKey: `${propertyKey}_${index++}`, ctor: buildOptions.ctor, initializer: buildOptions.initializer, - initializeAsArray: buildOptions.initializeAsArray, + initializeAs: buildOptions.initializeAs ?? "object", }); }; } 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 5286cece1bb..00a0da433a5 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 @@ -1,8 +1,9 @@ +import { awaitAsync as flushAsyncObservables } from "@bitwarden/angular/../test-utils"; import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, ReplaySubject } from "rxjs"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SessionSyncer } from "./session-syncer"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -10,8 +11,13 @@ import { SyncedItemMetadata } from "./sync-item-metadata"; describe("session syncer", () => { const propertyKey = "behaviorSubject"; const sessionKey = "Test__" + propertyKey; - const metaData = { propertyKey, sessionKey, initializer: (s: string) => s }; - let stateService: MockProxy; + const metaData: SyncedItemMetadata = { + propertyKey, + sessionKey, + initializer: (s: string) => s, + initializeAs: "object", + }; + let stateService: MockProxy; let sut: SessionSyncer; let behaviorSubject: BehaviorSubject; @@ -23,7 +29,7 @@ describe("session syncer", () => { manifest_version: 3, }); - stateService = mock(); + stateService = mock(); sut = new SessionSyncer(behaviorSubject, stateService, metaData); }); @@ -34,53 +40,85 @@ describe("session syncer", () => { }); describe("constructor", () => { - it("should throw if behaviorSubject is not an instance of BehaviorSubject", () => { + it("should throw if subject is not an instance of Subject", () => { expect(() => { new SessionSyncer({} as any, stateService, null); - }).toThrowError("behaviorSubject must be an instance of BehaviorSubject"); + }).toThrowError("subject must inherit from Subject"); }); it("should create if either ctor or initializer is provided", () => { expect( - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, ctor: String }) + new SessionSyncer(behaviorSubject, stateService, { + propertyKey, + sessionKey, + ctor: String, + initializeAs: "object", + }) ).toBeDefined(); expect( new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, initializer: (s: any) => s, + initializeAs: "object", }) ).toBeDefined(); }); it("should throw if neither ctor or initializer is provided", () => { expect(() => { - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey }); + new SessionSyncer(behaviorSubject, stateService, { + propertyKey, + sessionKey, + initializeAs: "object", + }); }).toThrowError("ctor or initializer must be provided"); }); }); - describe("manifest v2 init", () => { - let observeSpy: jest.SpyInstance; - let listenForUpdatesSpy: jest.SpyInstance; - - beforeEach(() => { - observeSpy = jest.spyOn(behaviorSubject, "subscribe").mockReturnThis(); - listenForUpdatesSpy = jest.spyOn(BrowserApi, "messageListener").mockReturnValue(); - jest.spyOn(chrome.runtime, "getManifest").mockReturnValue({ - name: "bitwarden-test", - version: "0.0.0", - manifest_version: 2, - }); + describe("init", () => { + it("should ignore all updates currently in a ReplaySubject's buffer", () => { + const replaySubject = new ReplaySubject(Infinity); + replaySubject.next("1"); + replaySubject.next("2"); + replaySubject.next("3"); + sut = new SessionSyncer(replaySubject, stateService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(3); }); - it("should not start observing", () => { - expect(observeSpy).not.toHaveBeenCalled(); + it("should ignore BehaviorSubject's initial value", () => { + const behaviorSubject = new BehaviorSubject("initial"); + sut = new SessionSyncer(behaviorSubject, stateService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); + + sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(1); }); - it("should not start listening", () => { - expect(listenForUpdatesSpy).not.toHaveBeenCalled(); + it("should grab an initial value from storage if it exists", () => { + stateService.hasInSessionMemory.mockResolvedValue(true); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + + expect(updateSpy).toHaveBeenCalledWith(); + }); + + it("should not grab an initial value from storage if it does not exist", () => { + stateService.hasInSessionMemory.mockResolvedValue(false); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + + expect(updateSpy).toHaveBeenCalledWith(); }); }); @@ -146,6 +184,7 @@ describe("session syncer", () => { stateService.getFromSessionMemory.mockResolvedValue("test"); await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" }); + await flushAsyncObservables(); expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1); expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey, builder); 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 2acfed2954f..68294b68c3d 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts @@ -1,9 +1,9 @@ -import { BehaviorSubject, concatMap, Subscription } from "rxjs"; +import { BehaviorSubject, concatMap, ReplaySubject, Subject, Subscription } from "rxjs"; import { Utils } from "@bitwarden/common/misc/utils"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -11,16 +11,16 @@ export class SessionSyncer { subscription: Subscription; id = Utils.newGuid(); - // everyone gets the same initial values - private ignoreNextUpdate = true; + // ignore initial values + private ignoreNUpdates = 0; constructor( - private behaviorSubject: BehaviorSubject, - private stateService: StateService, + private subject: Subject, + private stateService: BrowserStateService, private metaData: SyncedItemMetadata ) { - if (!(behaviorSubject instanceof BehaviorSubject)) { - throw new Error("behaviorSubject must be an instance of BehaviorSubject"); + if (!(subject instanceof Subject)) { + throw new Error("subject must inherit from Subject"); } if (metaData.ctor == null && metaData.initializer == null) { @@ -29,11 +29,23 @@ export class SessionSyncer { } init() { - if (BrowserApi.manifestVersion !== 3) { - return; + switch (this.subject.constructor) { + case ReplaySubject: + // ignore all updates currently in the buffer + this.ignoreNUpdates = (this.subject as any)._buffer.length; + break; + case BehaviorSubject: + this.ignoreNUpdates = 1; + break; + default: + break; } this.observe(); + if (this.stateService.hasInSessionMemory(this.metaData.sessionKey)) { + this.update(); + } + this.listenForUpdates(); } @@ -41,11 +53,11 @@ 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 + this.subscription = this.subject .pipe( concatMap(async (next) => { - if (this.ignoreNextUpdate) { - this.ignoreNextUpdate = false; + if (this.ignoreNUpdates > 0) { + this.ignoreNUpdates -= 1; return; } await this.updateSession(next); @@ -66,10 +78,14 @@ export class SessionSyncer { if (message.command != this.updateMessageCommand || message.id === this.id) { return; } + this.update(); + } + + async update() { const builder = SyncedItemMetadata.builder(this.metaData); const value = await this.stateService.getFromSessionMemory(this.metaData.sessionKey, builder); - this.ignoreNextUpdate = true; - this.behaviorSubject.next(value); + this.ignoreNUpdates = 1; + this.subject.next(value); } private async updateSession(value: any) { diff --git a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts index 2b3f4715d46..facfda32fd8 100644 --- a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts +++ b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts @@ -1,17 +1,27 @@ +export type InitializeOptions = "array" | "record" | "object"; + export class SyncedItemMetadata { propertyKey: string; sessionKey: string; ctor?: new () => any; initializer?: (keyValuePair: any) => any; - initializeAsArray?: boolean; + initializeAs: InitializeOptions; static builder(metadata: SyncedItemMetadata): (o: any) => any { const itemBuilder = metadata.initializer != null ? metadata.initializer : (o: any) => Object.assign(new metadata.ctor(), o); - if (metadata.initializeAsArray) { + if (metadata.initializeAs === "array") { return (keyValuePair: any) => keyValuePair.map((o: any) => itemBuilder(o)); + } else if (metadata.initializeAs === "record") { + return (keyValuePair: any) => { + const record: Record = {}; + for (const key in keyValuePair) { + record[key] = itemBuilder(keyValuePair[key]); + } + return record; + }; } else { return (keyValuePair: any) => itemBuilder(keyValuePair); } diff --git a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts index 5cd869a5b67..12b0b57d523 100644 --- a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts @@ -8,32 +8,60 @@ describe("builder", () => { const ctor = TestClass; it("should use initializer if provided", () => { - const metadata = { propertyKey, sessionKey: key, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should use ctor if initializer is not provided", () => { - const metadata = { propertyKey, sessionKey: key, ctor }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBeInstanceOf(TestClass); }); it("should prefer initializer over ctor", () => { - const metadata = { propertyKey, sessionKey: key, ctor, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should honor initialize as array", () => { - const metadata = { + const metadata: SyncedItemMetadata = { propertyKey, sessionKey: key, initializer: initializer, - initializeAsArray: true, + initializeAs: "array", }; const builder = SyncedItemMetadata.builder(metadata); expect(builder([{}])).toBeInstanceOf(Array); expect(builder([{}])[0]).toBe("used initializer"); }); + + it("should honor initialize as record", () => { + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer: initializer, + initializeAs: "record", + }; + const builder = SyncedItemMetadata.builder(metadata); + expect(builder({ key: "" })).toBeInstanceOf(Object); + expect(builder({ key: "" })).toStrictEqual({ key: "used initializer" }); + }); }); diff --git a/apps/browser/src/listeners/update-badge.ts b/apps/browser/src/listeners/update-badge.ts index 9c7c122a45a..8762a15ab2a 100644 --- a/apps/browser/src/listeners/update-badge.ts +++ b/apps/browser/src/listeners/update-badge.ts @@ -15,7 +15,7 @@ import { searchServiceFactory } from "../background/service_factories/search-ser import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; import { Account } from "../models/account"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; export type BadgeOptions = { @@ -25,7 +25,7 @@ export type BadgeOptions = { export class UpdateBadge { private authService: AuthService; - private stateService: StateService; + private stateService: BrowserStateService; private cipherService: CipherService; private badgeAction: typeof chrome.action; private sidebarAction: OperaSidebarAction | FirefoxSidebarAction; diff --git a/apps/browser/src/models/account.ts b/apps/browser/src/models/account.ts index f49c55d2909..cfbcbecf979 100644 --- a/apps/browser/src/models/account.ts +++ b/apps/browser/src/models/account.ts @@ -1,3 +1,5 @@ +import { Jsonify } from "type-fest"; + import { Account as BaseAccount, AccountSettings as BaseAccountSettings, @@ -9,6 +11,14 @@ import { BrowserSendComponentState } from "./browserSendComponentState"; export class AccountSettings extends BaseAccountSettings { vaultTimeout = -1; // On Restart + + static fromJSON(json: Jsonify): AccountSettings { + if (json == null) { + return null; + } + + return Object.assign(new AccountSettings(), json, super.fromJSON(json)); + } } export class Account extends BaseAccount { @@ -29,4 +39,18 @@ export class Account extends BaseAccount { this.ciphers = init?.ciphers ?? new BrowserComponentState(); this.sendType = init?.sendType ?? new BrowserComponentState(); } + + static fromJSON(json: Jsonify): Account { + if (json == null) { + return null; + } + + return Object.assign(new Account({}), json, super.fromJSON(json), { + settings: AccountSettings.fromJSON(json.settings), + groupings: BrowserGroupingsComponentState.fromJSON(json.groupings), + send: BrowserSendComponentState.fromJSON(json.send), + ciphers: BrowserComponentState.fromJSON(json.ciphers), + sendType: BrowserComponentState.fromJSON(json.sendType), + }); + } } diff --git a/apps/browser/src/models/browserComponentState.ts b/apps/browser/src/models/browserComponentState.ts index d968726c413..c5540d088ff 100644 --- a/apps/browser/src/models/browserComponentState.ts +++ b/apps/browser/src/models/browserComponentState.ts @@ -1,4 +1,14 @@ +import { Jsonify } from "type-fest"; + export class BrowserComponentState { scrollY: number; searchText: string; + + static fromJSON(json: Jsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserComponentState(), json); + } } diff --git a/apps/browser/src/models/browserGroupingsComponentState.ts b/apps/browser/src/models/browserGroupingsComponentState.ts index 63eb4aaa88f..f406e3d8271 100644 --- a/apps/browser/src/models/browserGroupingsComponentState.ts +++ b/apps/browser/src/models/browserGroupingsComponentState.ts @@ -1,7 +1,9 @@ import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { FolderView } from "@bitwarden/common/models/view/folder.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; @@ -15,4 +17,28 @@ export class BrowserGroupingsComponentState extends BrowserComponentState { folders: FolderView[]; collections: CollectionView[]; deletedCount: number; + + toJSON() { + return Utils.merge(this, { + collectionCounts: Utils.mapToRecord(this.collectionCounts), + folderCounts: Utils.mapToRecord(this.folderCounts), + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserGroupingsComponentState(), json, { + favoriteCiphers: json.favoriteCiphers?.map((c) => CipherView.fromJSON(c)), + noFolderCiphers: json.noFolderCiphers?.map((c) => CipherView.fromJSON(c)), + ciphers: json.ciphers?.map((c) => CipherView.fromJSON(c)), + collectionCounts: Utils.recordToMap(json.collectionCounts), + folderCounts: Utils.recordToMap(json.folderCounts), + typeCounts: Utils.recordToMap(json.typeCounts), + folders: json.folders?.map((f) => FolderView.fromJSON(f)), + }); + } } diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts index e2bf4eaa5d6..99508737ab1 100644 --- a/apps/browser/src/models/browserSendComponentState.ts +++ b/apps/browser/src/models/browserSendComponentState.ts @@ -1,9 +1,28 @@ import { SendType } from "@bitwarden/common/enums/sendType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { SendView } from "@bitwarden/common/models/view/send.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; export class BrowserSendComponentState extends BrowserComponentState { sends: SendView[]; typeCounts: Map; + + toJSON() { + return Utils.merge(this, { + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserSendComponentState(), json, { + sends: json.sends?.map((s) => SendView.fromJSON(s)), + typeCounts: Utils.recordToMap(json.typeCounts), + }); + } } diff --git a/apps/browser/src/popup/accounts/set-password.component.html b/apps/browser/src/popup/accounts/set-password.component.html index 517374578c2..9eb551bf269 100644 --- a/apps/browser/src/popup/accounts/set-password.component.html +++ b/apps/browser/src/popup/accounts/set-password.component.html @@ -39,13 +39,10 @@
-
diff --git a/apps/desktop/src/app/vault/folder-add-edit.component.html b/apps/desktop/src/app/vault/folder-add-edit.component.html index ea7303dceb2..4e9adec8172 100644 --- a/apps/desktop/src/app/vault/folder-add-edit.component.html +++ b/apps/desktop/src/app/vault/folder-add-edit.component.html @@ -47,17 +47,17 @@ class="danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/desktop/src/app/vault/generator.component.html b/apps/desktop/src/app/vault/generator.component.html index 6111bcc3c6a..6acf7a331bf 100644 --- a/apps/desktop/src/app/vault/generator.component.html +++ b/apps/desktop/src/app/vault/generator.component.html @@ -52,11 +52,11 @@ appStopClick appA11yTitle="{{ 'regenerateUsername' | i18n }}" (click)="regenerate()" - [disabled]="form.loading" + [disabled]="$any(form).loading" > @@ -384,7 +384,7 @@ diff --git a/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html index 91a5b9ee772..eaa874d28e1 100644 --- a/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html @@ -63,7 +63,7 @@ class="edit-button" *ngIf="f.node.id" (click)="editFolder(f.node)" - appA11yTitle="{{ 'editFolder' | i18n }}" + appA11yTitle="{{ 'editFolder' | i18n }}: {{ f.node.name }}" > diff --git a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html index 3663b69fc46..cfec1cf89d0 100644 --- a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html @@ -24,7 +24,7 @@ class="filter-button" (click)="clearFilter()" [attr.aria-pressed]="!hasActiveFilter" - appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name }}" + appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name | i18n }}" > {{ organizationGrouping.name | i18n }} @@ -73,7 +73,7 @@ class="filter-button" (click)="clearFilter()" [attr.aria-pressed]="!hasActiveFilter" - appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name }}" + appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name | i18n }}" > {{ organizationGrouping.name | i18n }} @@ -102,18 +102,18 @@ - + diff --git a/apps/desktop/src/app/vault/vault.component.ts b/apps/desktop/src/app/vault/vault.component.ts index 8c3e2103cdf..5681535e246 100644 --- a/apps/desktop/src/app/vault/vault.component.ts +++ b/apps/desktop/src/app/vault/vault.component.ts @@ -27,8 +27,8 @@ import { CipherType } from "@bitwarden/common/enums/cipherType"; import { EventType } from "@bitwarden/common/enums/eventType"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/models/view/folder.view"; -import { invokeMenu, RendererMenuItem } from "@bitwarden/electron/utils"; +import { invokeMenu, RendererMenuItem } from "../../utils"; import { SearchBarService } from "../layout/search/search-bar.service"; import { AddEditComponent } from "./add-edit.component"; diff --git a/apps/desktop/src/app/vault/view.component.html b/apps/desktop/src/app/vault/view.component.html index 8b1b0445744..5b3896f68ed 100644 --- a/apps/desktop/src/app/vault/view.component.html +++ b/apps/desktop/src/app/vault/view.component.html @@ -64,16 +64,16 @@ appA11yTitle="{{ 'checkPassword' | i18n }}" (click)="checkPassword()" [appApiAction]="checkPasswordPromise" - [disabled]="checkPasswordBtn.loading" + [disabled]="$any(checkPasswordBtn).loading" > @@ -472,12 +472,12 @@ {{ attachment.sizeName }} diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index fd168510b38..1ca90e8ce2f 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 12a4f27de17..084e90d0048 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -45,7 +45,7 @@ "message": "Share" }, "moveToOrganization": { - "message": "Move to Organisation" + "message": "Move to organisation" }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index ea5a4e040f3..263a2356b3b 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1844,7 +1844,7 @@ "message": "Czas blokowania sejfu przekracza limit określony przez organizację." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatyczne rejestrowanie użytkowników do resetowania hasła" + "message": "Automatyczne rejestrowanie użytkowników" }, "resetPasswordAutoEnrollInviteWarning": { "message": "Ta organizacja posługuje się zasadą, która automatycznie rejestruje użytkowników do resetowania hasła. Rejestracja umożliwia administratorom organizacji zmianę Twojego hasła głównego." @@ -2029,7 +2029,7 @@ "message": "Logowanie jako" }, "rememberEmail": { - "message": "Zapamiętaj e-mail" + "message": "Zapamiętaj adres e-mail" }, "notYou": { "message": "To nie Ty?" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 28075bd45d5..0d7244c122d 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -48,7 +48,7 @@ "message": "Chuyển tới Tổ chức" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ đã di chuyển tới $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -805,7 +805,7 @@ "message": "Đồng bộ thất bại" }, "yourVaultIsLocked": { - "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chinhs của bạn để mở." + "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chính của bạn để mở." }, "unlock": { "message": "Mở khóa" @@ -1213,7 +1213,7 @@ "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Tên miền", "description": "Domain name. Ex. website.com" }, "host": { @@ -1662,7 +1662,7 @@ "message": "Văn bản" }, "deletionDate": { - "message": "Deletion date" + "message": "Ngày xóa" }, "deletionDateDesc": { "message": "Send sẽ được xóa vĩnh viễn vào ngày và giờ được chỉ định.", @@ -1703,7 +1703,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send link", + "message": "Gửi liên kết", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1752,14 +1752,14 @@ "message": "1 ngày" }, "custom": { - "message": "Custom" + "message": "Tùy chỉnh" }, "deleteSendConfirmation": { "message": "Bạn có chắc chắn muốn xóa Send này?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "Copy Send link to clipboard", + "message": "Sao chép liên kết tới Khay nhớ tạm", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { @@ -1777,58 +1777,58 @@ "message": "Sao chép liên kết" }, "disabled": { - "message": "Disabled" + "message": "Đã tắt" }, "maxAccessCountReached": { - "message": "Max access count reached" + "message": "Đã đạt đến số lượng truy cập tối đa" }, "expired": { - "message": "Expired" + "message": "Đã hết hạn" }, "pendingDeletion": { "message": "Đang chờ xóa" }, "webAuthnAuthenticate": { - "message": "Authenticate WebAuthn" + "message": "Xác thực WebAuthn" }, "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Ẩn địa chỉ email của tôi khỏi người nhận." }, "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." + "message": "Có một hoặc vài chính sách của tổ chức đang làm ảnh hưởng đến cài đặt tạo mật khẩu của bạn." }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "Yêu cầu xác nhận danh tính qua Email" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature." + "message": "Bạn phải xác minh email của mình để sử dụng tính năng này." }, "passwordPrompt": { - "message": "Master password re-prompt" + "message": "Nhắc lại mật khẩu chính" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "Xác nhận mật khẩu chính" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "Hành động này được bảo vệ. Để tiếp tục, vui lòng nhập lại mật khẩu chính của bạn để xác minh danh tính của bạn." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Mật khẩu chính đã được cập nhật" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Cập nhật Mật khẩu chính" }, "updateMasterPasswordWarning": { - "message": "Your master password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Mật khẩu chính của bạn gần đây đã được thay đổi bởi một quản trị viên trong tổ chức của bạn. Để truy cập Kho, bạn phải cập nhật nó ngay bây giờ. Tiếp tục sẽ đăng xuất bạn khỏi phiên hiện tại của bạn, yêu cầu bạn đăng nhập lại. Các phiên hoạt động trên các thiết bị khác có thể tiếp tục hoạt động trong tối đa một giờ." }, "hours": { - "message": "Hours" + "message": "Giờ" }, "minutes": { - "message": "Minutes" + "message": "Phút" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Chính sách tổ chức của bạn đang ảnh hưởng đến thời gian chờ Kho của bạn. Thời gian chờ kho tối đa được phép là $HOURS$ giờ và $MINUTES$ phút", "placeholders": { "hours": { "content": "$1", @@ -1841,10 +1841,10 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Thời gian chờ Kho của bạn vượt quá các hạn chế do tổ chức của bạn đặt ra." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Đăng ký tự động" }, "resetPasswordAutoEnrollInviteWarning": { "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." @@ -1856,13 +1856,13 @@ "message": "One or more organization policies prevents you from exporting your personal vault." }, "addAccount": { - "message": "Add account" + "message": "Thêm tài khoản" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Xóa mật khẩu chính" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Đã xóa mật khẩu chính" }, "convertOrganizationEncryptionDesc": { "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", @@ -1874,7 +1874,7 @@ } }, "leaveOrganization": { - "message": "Leave organization" + "message": "Rời khỏi tổ chức" }, "leaveOrganizationConfirmation": { "message": "Are you sure you want to leave this organization?" @@ -1892,7 +1892,7 @@ "message": "No more than 5 accounts may be logged in at the same time." }, "accountPreferences": { - "message": "Preferences" + "message": "Tuỳ chỉnh" }, "appPreferences": { "message": "App settings (all accounts)" @@ -1910,10 +1910,10 @@ } }, "switchAccount": { - "message": "Switch account" + "message": "Chuyển tài khoản" }, "options": { - "message": "Options" + "message": "Tùy chọn" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1937,19 +1937,19 @@ "message": "Đã mở khóa" }, "generator": { - "message": "Generator" + "message": "Tạo" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Bạn muốn tạo gì?" }, "passwordType": { - "message": "Password type" + "message": "Loại mật khẩu" }, "regenerateUsername": { "message": "Regenerate username" }, "generateUsername": { - "message": "Generate username" + "message": "Tạo tên tài khoản" }, "usernameType": { "message": "Username type" @@ -1968,22 +1968,22 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Ngẫu nhiên" }, "randomWord": { - "message": "Random word" + "message": "Từ ngẫu nhiên" }, "websiteName": { - "message": "Website name" + "message": "Tên website" }, "service": { - "message": "Service" + "message": "Dịch vụ" }, "allVaults": { - "message": "All vaults" + "message": "Tất cả kho" }, "searchOrganization": { - "message": "Search organization" + "message": "Tìm tổ chức" }, "searchMyVault": { "message": "Search my vault" diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index d54d46487a2..f5a078cdcc5 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -6,20 +6,20 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { StateService } from "@bitwarden/common/services/state.service"; -import { ElectronLogService } from "@bitwarden/electron/services/electronLog.service"; -import { ElectronMainMessagingService } from "@bitwarden/electron/services/electronMainMessaging.service"; -import { ElectronStorageService } from "@bitwarden/electron/services/electronStorage.service"; -import { TrayMain } from "@bitwarden/electron/tray.main"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { WindowMain } from "@bitwarden/electron/window.main"; import { BiometricMain } from "./main/biometric/biometric.main"; -import { DesktopCredentialStorageListener } from "./main/desktopCredentialStorageListener"; +import { DesktopCredentialStorageListener } from "./main/desktop-credential-storage-listener"; import { MenuMain } from "./main/menu/menu.main"; import { MessagingMain } from "./main/messaging.main"; -import { NativeMessagingMain } from "./main/nativeMessaging.main"; -import { PowerMonitorMain } from "./main/powerMonitor.main"; +import { NativeMessagingMain } from "./main/native-messaging.main"; +import { PowerMonitorMain } from "./main/power-monitor.main"; +import { TrayMain } from "./main/tray.main"; +import { UpdaterMain } from "./main/updater.main"; +import { WindowMain } from "./main/window.main"; import { Account } from "./models/account"; +import { ElectronLogService } from "./services/electron-log.service"; +import { ElectronMainMessagingService } from "./services/electron-main-messaging.service"; +import { ElectronStorageService } from "./services/electron-storage.service"; import { I18nService } from "./services/i18n.service"; export class Main { @@ -105,15 +105,7 @@ export class Main { (win) => this.trayMain.setupWindowListeners(win) ); this.messagingMain = new MessagingMain(this, this.stateService); - this.updaterMain = new UpdaterMain( - this.i18nService, - this.windowMain, - "clients", - null, - null, - null, - "bitwarden" - ); + this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, "bitwarden"); this.menuMain = new MenuMain(this); this.powerMonitorMain = new PowerMonitorMain(this); this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.stateService); diff --git a/apps/desktop/src/main/biometric/biometric.windows.main.ts b/apps/desktop/src/main/biometric/biometric.windows.main.ts index 01e3b563f2f..207e6c38b56 100644 --- a/apps/desktop/src/main/biometric/biometric.windows.main.ts +++ b/apps/desktop/src/main/biometric/biometric.windows.main.ts @@ -4,7 +4,8 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { biometrics } from "@bitwarden/desktop-native"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { WindowMain } from "../window.main"; import { BiometricMain } from "./biometric.main"; diff --git a/apps/desktop/src/main/desktopCredentialStorageListener.ts b/apps/desktop/src/main/desktop-credential-storage-listener.ts similarity index 100% rename from apps/desktop/src/main/desktopCredentialStorageListener.ts rename to apps/desktop/src/main/desktop-credential-storage-listener.ts diff --git a/apps/desktop/src/main/menu/menu.about.ts b/apps/desktop/src/main/menu/menu.about.ts index 64f1adad0ce..33d4f12ea55 100644 --- a/apps/desktop/src/main/menu/menu.about.ts +++ b/apps/desktop/src/main/menu/menu.about.ts @@ -1,8 +1,9 @@ import { BrowserWindow, clipboard, dialog, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMacAppStore, isSnapStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isSnapStore, isWindowsStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menu.account.ts b/apps/desktop/src/main/menu/menu.account.ts index b7c4b1f693f..31e0797ebb6 100644 --- a/apps/desktop/src/main/menu/menu.account.ts +++ b/apps/desktop/src/main/menu/menu.account.ts @@ -2,7 +2,8 @@ import { BrowserWindow, dialog, MenuItemConstructorOptions, shell } from "electr import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { isMacAppStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isWindowsStore } from "../../utils"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menu.bitwarden.ts b/apps/desktop/src/main/menu/menu.bitwarden.ts index d25454d9a49..7a20b381f58 100644 --- a/apps/desktop/src/main/menu/menu.bitwarden.ts +++ b/apps/desktop/src/main/menu/menu.bitwarden.ts @@ -2,8 +2,9 @@ import { BrowserWindow, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac } from "@bitwarden/electron/utils"; + +import { isMac } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { FirstMenu } from "./menu.first"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index 71b4a236922..6a7c3ba16dd 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -2,8 +2,9 @@ import { BrowserWindow, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac, isMacAppStore } from "@bitwarden/electron/utils"; + +import { isMac, isMacAppStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { FirstMenu } from "./menu.first"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.first.ts b/apps/desktop/src/main/menu/menu.first.ts index 5786b4e68cf..06f24b0570e 100644 --- a/apps/desktop/src/main/menu/menu.first.ts +++ b/apps/desktop/src/main/menu/menu.first.ts @@ -2,8 +2,9 @@ import { BrowserWindow, dialog, MenuItem, MenuItemConstructorOptions } from "ele import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMacAppStore, isSnapStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isSnapStore, isWindowsStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.help.ts b/apps/desktop/src/main/menu/menu.help.ts index 60fce1ce494..fde742b3737 100644 --- a/apps/desktop/src/main/menu/menu.help.ts +++ b/apps/desktop/src/main/menu/menu.help.ts @@ -1,7 +1,8 @@ import { shell, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { isMacAppStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isWindowsStore } from "../../utils"; import { AboutMenu } from "./menu.about"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menu.main.ts b/apps/desktop/src/main/menu/menu.main.ts index 66670e196cc..6a2bd3531b5 100644 --- a/apps/desktop/src/main/menu/menu.main.ts +++ b/apps/desktop/src/main/menu/menu.main.ts @@ -1,17 +1,22 @@ import { app, Menu } from "electron"; -import { BaseMenu } from "@bitwarden/electron/baseMenu"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { Main } from "../../main"; +import { WindowMain } from "../window.main"; import { MenuUpdateRequest } from "./menu.updater"; import { Menubar } from "./menubar"; const cloudWebVaultUrl = "https://vault.bitwarden.com"; -export class MenuMain extends BaseMenu { +export class MenuMain { + private i18nService: I18nService; + private windowMain: WindowMain; + constructor(private main: Main) { - super(main.i18nService, main.windowMain); + this.i18nService = main.i18nService; + this.windowMain = main.windowMain; } async init() { @@ -49,4 +54,84 @@ export class MenuMain extends BaseMenu { } return webVaultUrl; } + + private initContextMenu() { + if (this.windowMain.win == null) { + return; + } + + const selectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + enabled: false, + }, + { + label: this.i18nService.t("copy"), + role: "copy", + enabled: false, + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputSelectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + this.windowMain.win.webContents.on("context-menu", (e, props) => { + const selected = props.selectionText && props.selectionText.trim() !== ""; + if (props.isEditable && selected) { + inputSelectionMenu.popup({ window: this.windowMain.win }); + } else if (props.isEditable) { + inputMenu.popup({ window: this.windowMain.win }); + } else if (selected) { + selectionMenu.popup({ window: this.windowMain.win }); + } + }); + } } diff --git a/apps/desktop/src/main/menu/menu.window.ts b/apps/desktop/src/main/menu/menu.window.ts index 3746e22b726..8f588781a56 100644 --- a/apps/desktop/src/main/menu/menu.window.ts +++ b/apps/desktop/src/main/menu/menu.window.ts @@ -2,8 +2,9 @@ import { MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { isMac } from "@bitwarden/electron/utils"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { isMac } from "../../utils"; +import { WindowMain } from "../window.main"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menubar.ts b/apps/desktop/src/main/menu/menubar.ts index 441b9ac6550..5048c60dc09 100644 --- a/apps/desktop/src/main/menu/menubar.ts +++ b/apps/desktop/src/main/menu/menubar.ts @@ -2,9 +2,10 @@ import { Menu, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac } from "@bitwarden/electron/utils"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { isMac } from "../../utils"; +import { UpdaterMain } from "../updater.main"; +import { WindowMain } from "../window.main"; import { AboutMenu } from "./menu.about"; import { AccountMenu } from "./menu.account"; diff --git a/apps/desktop/src/main/nativeMessaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts similarity index 99% rename from apps/desktop/src/main/nativeMessaging.main.ts rename to apps/desktop/src/main/native-messaging.main.ts index 0559d2ce96c..ae549ace384 100644 --- a/apps/desktop/src/main/nativeMessaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -8,7 +8,8 @@ import { ipcMain } from "electron"; import * as ipc from "node-ipc"; import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { WindowMain } from "./window.main"; export class NativeMessagingMain { private connected: Socket[] = []; diff --git a/apps/desktop/src/main/powerMonitor.main.ts b/apps/desktop/src/main/power-monitor.main.ts similarity index 94% rename from apps/desktop/src/main/powerMonitor.main.ts rename to apps/desktop/src/main/power-monitor.main.ts index 438a0e6f9ec..56e6139ccf1 100644 --- a/apps/desktop/src/main/powerMonitor.main.ts +++ b/apps/desktop/src/main/power-monitor.main.ts @@ -1,8 +1,7 @@ import { powerMonitor } from "electron"; -import { isSnapStore } from "@bitwarden/electron/utils"; - import { Main } from "../main"; +import { isSnapStore } from "../utils"; // tslint:disable-next-line const IdleLockSeconds = 5 * 60; // 5 minutes diff --git a/libs/electron/src/tray.main.ts b/apps/desktop/src/main/tray.main.ts similarity index 100% rename from libs/electron/src/tray.main.ts rename to apps/desktop/src/main/tray.main.ts diff --git a/libs/electron/src/updater.main.ts b/apps/desktop/src/main/updater.main.ts similarity index 86% rename from libs/electron/src/updater.main.ts rename to apps/desktop/src/main/updater.main.ts index 638c5efeda4..314ea076b22 100644 --- a/libs/electron/src/updater.main.ts +++ b/apps/desktop/src/main/updater.main.ts @@ -4,10 +4,11 @@ import { autoUpdater } from "electron-updater"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "./utils"; +import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "../utils"; + import { WindowMain } from "./window.main"; -const UpdaterCheckInitalDelay = 5 * 1000; // 5 seconds +const UpdaterCheckInitialDelay = 5 * 1000; // 5 seconds const UpdaterCheckInterval = 12 * 60 * 60 * 1000; // 12 hours export class UpdaterMain { @@ -18,10 +19,6 @@ export class UpdaterMain { constructor( private i18nService: I18nService, private windowMain: WindowMain, - private gitHubProject: string, - private onCheckingForUpdate: () => void = null, - private onReset: () => void = null, - private onUpdateDownloaded: () => void = null, private projectName: string ) { autoUpdater.logger = log; @@ -36,13 +33,10 @@ export class UpdaterMain { } async init() { - global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitalDelay); + global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitialDelay); global.setInterval(async () => await this.checkForUpdate(), UpdaterCheckInterval); autoUpdater.on("checking-for-update", () => { - if (this.onCheckingForUpdate != null) { - this.onCheckingForUpdate(); - } this.doingUpdateCheck = true; }); @@ -87,10 +81,6 @@ export class UpdaterMain { }); autoUpdater.on("update-downloaded", async (info) => { - if (this.onUpdateDownloaded != null) { - this.onUpdateDownloaded(); - } - if (this.windowMain.win == null) { return; } @@ -132,7 +122,7 @@ export class UpdaterMain { if (!this.canUpdate) { if (withFeedback) { - shell.openExternal("https://github.com/bitwarden/" + this.gitHubProject + "/releases"); + shell.openExternal("https://github.com/bitwarden/clients/releases"); } return; @@ -147,9 +137,6 @@ export class UpdaterMain { } private reset() { - if (this.onReset != null) { - this.onReset(); - } autoUpdater.autoDownload = true; this.doingUpdateCheck = false; } diff --git a/libs/electron/src/window.main.ts b/apps/desktop/src/main/window.main.ts similarity index 99% rename from libs/electron/src/window.main.ts rename to apps/desktop/src/main/window.main.ts index 47f7e4e9cd3..5b58172ac1b 100644 --- a/libs/electron/src/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -6,7 +6,7 @@ import { app, BrowserWindow, screen } from "electron"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "./utils"; +import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "../utils"; const mainWindowSizeKey = "mainWindowSize"; const WindowEventHandlingDelay = 100; diff --git a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts similarity index 60% rename from apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts rename to apps/desktop/src/models/native-messaging/decrypted-command-data.ts index 0aea170f6e6..eeab234d7a4 100644 --- a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts +++ b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts @@ -1,4 +1,4 @@ -import { EncryptedCommand } from "./encryptedCommand"; +import { EncryptedCommand } from "./encrypted-command"; export type DecryptedCommandData = { command: EncryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedCommand.ts b/apps/desktop/src/models/native-messaging/encrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedCommand.ts rename to apps/desktop/src/models/native-messaging/encrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts similarity index 76% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-response.ts index bed85b65e96..5f8deb05a30 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessageResponse = MessageCommon & { encryptedPayload: EncString; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts diff --git a/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts new file mode 100644 index 00000000000..a0d35a7adbd --- /dev/null +++ b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts @@ -0,0 +1,16 @@ +import { AccountStatusResponse } from "./account-status-response"; +import { CannotDecryptErrorResponse } from "./cannot-decrypt-error-response"; +import { CipherResponse } from "./cipher-response"; +import { FailureStatusResponse } from "./failure-status-response"; +import { GenerateResponse } from "./generate-response"; +import { SuccessStatusResponse } from "./success-status-response"; +import { UserStatusErrorResponse } from "./user-status-error-response"; + +export type EncyptedMessageResponse = + | AccountStatusResponse[] + | CannotDecryptErrorResponse + | CipherResponse[] + | FailureStatusResponse + | GenerateResponse + | SuccessStatusResponse + | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts b/apps/desktop/src/models/native-messaging/encrypted-message.ts similarity index 80% rename from apps/desktop/src/models/nativeMessaging/encryptedMessage.ts rename to apps/desktop/src/models/native-messaging/encrypted-message.ts index bd0b5177a40..fc06214875e 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessage = MessageCommon & { // Will decrypt to a DecryptedCommandData object diff --git a/apps/desktop/src/models/native-messaging/index.ts b/apps/desktop/src/models/native-messaging/index.ts new file mode 100644 index 00000000000..7f6701886af --- /dev/null +++ b/apps/desktop/src/models/native-messaging/index.ts @@ -0,0 +1,25 @@ +export * from "./encrypted-message-payloads/credential-create-payload"; +export * from "./encrypted-message-payloads/credential-retrieve-payload"; +export * from "./encrypted-message-payloads/credential-update-payload"; +export * from "./encrypted-message-payloads/password-generate-payload"; + +export * from "./encrypted-message-responses/account-status-response"; +export * from "./encrypted-message-responses/cannot-decrypt-error-response"; +export * from "./encrypted-message-responses/cipher-response"; +export * from "./encrypted-message-responses/encrypted-message-response"; +export * from "./encrypted-message-responses/failure-status-response"; +export * from "./encrypted-message-responses/generate-response"; +export * from "./encrypted-message-responses/success-status-response"; +export * from "./encrypted-message-responses/user-status-error-response"; + +export * from "./decrypted-command-data"; +export * from "./encrypted-command"; +export * from "./encrypted-message"; +export * from "./encrypted-message-response"; +export * from "./legacy-message"; +export * from "./legacy-message-wrapper"; +export * from "./message"; +export * from "./message-common"; +export * from "./unencrypted-command"; +export * from "./unencrypted-message"; +export * from "./unencrypted-message-response"; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts similarity index 77% rename from apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts rename to apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts index 5ef10e73289..03068374069 100644 --- a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts +++ b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { LegacyMessage } from "./legacyMessage"; +import { LegacyMessage } from "./legacy-message"; export type LegacyMessageWrapper = { message: LegacyMessage | EncString; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessage.ts b/apps/desktop/src/models/native-messaging/legacy-message.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/legacyMessage.ts rename to apps/desktop/src/models/native-messaging/legacy-message.ts diff --git a/apps/desktop/src/models/nativeMessaging/messageCommon.ts b/apps/desktop/src/models/native-messaging/message-common.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/messageCommon.ts rename to apps/desktop/src/models/native-messaging/message-common.ts diff --git a/apps/desktop/src/models/native-messaging/message.ts b/apps/desktop/src/models/native-messaging/message.ts new file mode 100644 index 00000000000..b76666bf142 --- /dev/null +++ b/apps/desktop/src/models/native-messaging/message.ts @@ -0,0 +1,4 @@ +import { EncryptedMessage } from "./encrypted-message"; +import { UnencryptedMessage } from "./unencrypted-message"; + +export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts b/apps/desktop/src/models/native-messaging/unencrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts rename to apps/desktop/src/models/native-messaging/unencrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts similarity index 85% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message-response.ts index a564b1324e2..b4f71ec6253 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts @@ -1,4 +1,4 @@ -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type UnencryptedMessageResponse = MessageCommon & ( diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts b/apps/desktop/src/models/native-messaging/unencrypted-message.ts similarity index 58% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message.ts index a665f312a98..d4ddb83ad68 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message.ts @@ -1,5 +1,5 @@ -import { MessageCommon } from "./messageCommon"; -import { UnencryptedCommand } from "./unencryptedCommand"; +import { MessageCommon } from "./message-common"; +import { UnencryptedCommand } from "./unencrypted-command"; export type UnencryptedMessage = MessageCommon & { command: UnencryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts b/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts deleted file mode 100644 index a10da2d8571..00000000000 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AccountStatusResponse } from "./accountStatusResponse"; -import { CannotDecryptErrorResponse } from "./cannotDecryptErrorResponse"; -import { CipherResponse } from "./cipherResponse"; -import { FailureStatusResponse } from "./failureStatusResponse"; -import { GenerateResponse } from "./generateResponse"; -import { SuccessStatusResponse } from "./successStatusResponse"; -import { UserStatusErrorResponse } from "./userStatusErrorResponse"; - -export type EncyptedMessageResponse = - | AccountStatusResponse[] - | CannotDecryptErrorResponse - | CipherResponse[] - | FailureStatusResponse - | GenerateResponse - | SuccessStatusResponse - | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/index.ts b/apps/desktop/src/models/nativeMessaging/index.ts deleted file mode 100644 index 1b0148c3f90..00000000000 --- a/apps/desktop/src/models/nativeMessaging/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export * from "./encryptedMessagePayloads/credentialCreatePayload"; -export * from "./encryptedMessagePayloads/credentialRetrievePayload"; -export * from "./encryptedMessagePayloads/credentialUpdatePayload"; -export * from "./encryptedMessagePayloads/passwordGeneratePayload"; - -export * from "./encryptedMessageResponses/accountStatusResponse"; -export * from "./encryptedMessageResponses/cannotDecryptErrorResponse"; -export * from "./encryptedMessageResponses/cipherResponse"; -export * from "./encryptedMessageResponses/encryptedMessageResponse"; -export * from "./encryptedMessageResponses/failureStatusResponse"; -export * from "./encryptedMessageResponses/generateResponse"; -export * from "./encryptedMessageResponses/successStatusResponse"; -export * from "./encryptedMessageResponses/userStatusErrorResponse"; - -export * from "./decryptedCommandData"; -export * from "./encryptedCommand"; -export * from "./encryptedMessage"; -export * from "./encryptedMessageResponse"; -export * from "./legacyMessage"; -export * from "./legacyMessageWrapper"; -export * from "./message"; -export * from "./messageCommon"; -export * from "./unencryptedCommand"; -export * from "./unencryptedMessage"; -export * from "./unencryptedMessageResponse"; diff --git a/apps/desktop/src/models/nativeMessaging/message.ts b/apps/desktop/src/models/nativeMessaging/message.ts deleted file mode 100644 index 25d96aa218a..00000000000 --- a/apps/desktop/src/models/nativeMessaging/message.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { EncryptedMessage } from "./encryptedMessage"; -import { UnencryptedMessage } from "./unencryptedMessage"; - -export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index fc77537579f..7006f7ac31b 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2022.10.2", + "version": "2022.11.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2022.10.2", + "version": "2022.11.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index f7e3af532c3..2be746c7970 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2022.10.2", + "version": "2022.11.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index 5818b194710..43da0d31bdf 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -534,3 +534,9 @@ img, app-vault-view .box-footer { user-select: auto; } + +/* tweak for inconsistent line heights in cipher view */ +.box-footer button, +.box-footer a { + line-height: 1; +} diff --git a/libs/electron/src/services/electronCrypto.service.ts b/apps/desktop/src/services/electron-crypto.service.ts similarity index 100% rename from libs/electron/src/services/electronCrypto.service.ts rename to apps/desktop/src/services/electron-crypto.service.ts diff --git a/libs/electron/spec/services/electronLog.service.spec.ts b/apps/desktop/src/services/electron-log.service.spec.ts similarity index 75% rename from libs/electron/spec/services/electronLog.service.spec.ts rename to apps/desktop/src/services/electron-log.service.spec.ts index 5802478fb70..5b12edc9450 100644 --- a/libs/electron/spec/services/electronLog.service.spec.ts +++ b/apps/desktop/src/services/electron-log.service.spec.ts @@ -1,4 +1,4 @@ -import { ElectronLogService } from "@bitwarden/electron/services/electronLog.service"; +import { ElectronLogService } from "./electron-log.service"; describe("ElectronLogService", () => { it("sets dev based on electron method", () => { diff --git a/libs/electron/src/services/electronLog.service.ts b/apps/desktop/src/services/electron-log.service.ts similarity index 100% rename from libs/electron/src/services/electronLog.service.ts rename to apps/desktop/src/services/electron-log.service.ts diff --git a/libs/electron/src/services/electronMainMessaging.service.ts b/apps/desktop/src/services/electron-main-messaging.service.ts similarity index 97% rename from libs/electron/src/services/electronMainMessaging.service.ts rename to apps/desktop/src/services/electron-main-messaging.service.ts index a025e32e373..b8d754b925b 100644 --- a/libs/electron/src/services/electronMainMessaging.service.ts +++ b/apps/desktop/src/services/electron-main-messaging.service.ts @@ -3,8 +3,8 @@ import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, session } from "elec import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/enums/themeType"; +import { WindowMain } from "../main/window.main"; import { RendererMenuItem } from "../utils"; -import { WindowMain } from "../window.main"; export class ElectronMainMessagingService implements MessagingService { constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { diff --git a/libs/electron/src/services/electronPlatformUtils.service.ts b/apps/desktop/src/services/electron-platform-utils.service.ts similarity index 97% rename from libs/electron/src/services/electronPlatformUtils.service.ts rename to apps/desktop/src/services/electron-platform-utils.service.ts index 1b710b85f2f..8af865d8452 100644 --- a/libs/electron/src/services/electronPlatformUtils.service.ts +++ b/apps/desktop/src/services/electron-platform-utils.service.ts @@ -87,6 +87,10 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { return ipcRenderer.invoke("appVersion"); } + async getApplicationVersionNumber(): Promise { + return (await this.getApplicationVersion()).split(/[+|-]/)[0].trim(); + } + // Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349 // has been merged and an updated electron build is available. supportsWebAuthn(win: Window): boolean { diff --git a/libs/electron/src/services/electronRendererMessaging.service.ts b/apps/desktop/src/services/electron-renderer-messaging.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererMessaging.service.ts rename to apps/desktop/src/services/electron-renderer-messaging.service.ts diff --git a/libs/electron/src/services/electronRendererSecureStorage.service.ts b/apps/desktop/src/services/electron-renderer-secure-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererSecureStorage.service.ts rename to apps/desktop/src/services/electron-renderer-secure-storage.service.ts diff --git a/libs/electron/src/services/electronRendererStorage.service.ts b/apps/desktop/src/services/electron-renderer-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererStorage.service.ts rename to apps/desktop/src/services/electron-renderer-storage.service.ts diff --git a/libs/electron/src/services/electronStorage.service.ts b/apps/desktop/src/services/electron-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronStorage.service.ts rename to apps/desktop/src/services/electron-storage.service.ts diff --git a/apps/desktop/src/services/encryptedMessageHandlerService.ts b/apps/desktop/src/services/encrypted-message-handler.service.ts similarity index 84% rename from apps/desktop/src/services/encryptedMessageHandlerService.ts rename to apps/desktop/src/services/encrypted-message-handler.service.ts index f5ac1e5fcef..5f45118768e 100644 --- a/apps/desktop/src/services/encryptedMessageHandlerService.ts +++ b/apps/desktop/src/services/encrypted-message-handler.service.ts @@ -12,18 +12,18 @@ import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/models/view/login.view"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { CredentialCreatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload"; -import { CredentialRetrievePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload"; -import { CredentialUpdatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload"; -import { PasswordGeneratePayload } from "../models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload"; -import { AccountStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/accountStatusResponse"; -import { CipherResponse } from "../models/nativeMessaging/encryptedMessageResponses/cipherResponse"; -import { EncyptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse"; -import { FailureStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/failureStatusResponse"; -import { GenerateResponse } from "../models/nativeMessaging/encryptedMessageResponses/generateResponse"; -import { SuccessStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/successStatusResponse"; -import { UserStatusErrorResponse } from "../models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { CredentialCreatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-create-payload"; +import { CredentialRetrievePayload } from "../models/native-messaging/encrypted-message-payloads/credential-retrieve-payload"; +import { CredentialUpdatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-update-payload"; +import { PasswordGeneratePayload } from "../models/native-messaging/encrypted-message-payloads/password-generate-payload"; +import { AccountStatusResponse } from "../models/native-messaging/encrypted-message-responses/account-status-response"; +import { CipherResponse } from "../models/native-messaging/encrypted-message-responses/cipher-response"; +import { EncyptedMessageResponse } from "../models/native-messaging/encrypted-message-responses/encrypted-message-response"; +import { FailureStatusResponse } from "../models/native-messaging/encrypted-message-responses/failure-status-response"; +import { GenerateResponse } from "../models/native-messaging/encrypted-message-responses/generate-response"; +import { SuccessStatusResponse } from "../models/native-messaging/encrypted-message-responses/success-status-response"; +import { UserStatusErrorResponse } from "../models/native-messaging/encrypted-message-responses/user-status-error-response"; import { StateService } from "./state.service"; diff --git a/apps/desktop/src/services/nativeMessageHandler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts similarity index 92% rename from apps/desktop/src/services/nativeMessageHandler.service.ts rename to apps/desktop/src/services/native-message-handler.service.ts index e7c92082075..fd00bf33f8e 100644 --- a/apps/desktop/src/services/nativeMessageHandler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -12,14 +12,14 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/services/state.service"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { EncryptedMessage } from "../models/nativeMessaging/encryptedMessage"; -import { EncryptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponse"; -import { Message } from "../models/nativeMessaging/message"; -import { UnencryptedMessage } from "../models/nativeMessaging/unencryptedMessage"; -import { UnencryptedMessageResponse } from "../models/nativeMessaging/unencryptedMessageResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { EncryptedMessage } from "../models/native-messaging/encrypted-message"; +import { EncryptedMessageResponse } from "../models/native-messaging/encrypted-message-response"; +import { Message } from "../models/native-messaging/message"; +import { UnencryptedMessage } from "../models/native-messaging/unencrypted-message"; +import { UnencryptedMessageResponse } from "../models/native-messaging/unencrypted-message-response"; -import { EncryptedMessageHandlerService } from "./encryptedMessageHandlerService"; +import { EncryptedMessageHandlerService } from "./encrypted-message-handler.service"; const EncryptionAlgorithm = "sha1"; diff --git a/apps/desktop/src/services/nativeMessaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts similarity index 95% rename from apps/desktop/src/services/nativeMessaging.service.ts rename to apps/desktop/src/services/native-messaging.service.ts index 83e3705dc7d..0a2b443bfbf 100644 --- a/apps/desktop/src/services/nativeMessaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -15,11 +15,11 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { LegacyMessage } from "../models/nativeMessaging/legacyMessage"; -import { LegacyMessageWrapper } from "../models/nativeMessaging/legacyMessageWrapper"; -import { Message } from "../models/nativeMessaging/message"; +import { LegacyMessage } from "../models/native-messaging/legacy-message"; +import { LegacyMessageWrapper } from "../models/native-messaging/legacy-message-wrapper"; +import { Message } from "../models/native-messaging/message"; -import { NativeMessageHandlerService } from "./nativeMessageHandler.service"; +import { NativeMessageHandlerService } from "./native-message-handler.service"; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = "sha1"; diff --git a/apps/desktop/src/services/passwordReprompt.service.ts b/apps/desktop/src/services/password-reprompt.service.ts similarity index 100% rename from apps/desktop/src/services/passwordReprompt.service.ts rename to apps/desktop/src/services/password-reprompt.service.ts diff --git a/libs/electron/spec/utils.spec.ts b/apps/desktop/src/utils.spec.ts similarity index 96% rename from libs/electron/spec/utils.spec.ts rename to apps/desktop/src/utils.spec.ts index 96af43eb752..e9fbad3df9e 100644 --- a/libs/electron/spec/utils.spec.ts +++ b/apps/desktop/src/utils.spec.ts @@ -1,4 +1,4 @@ -import { cleanUserAgent } from "@bitwarden/electron/utils"; +import { cleanUserAgent } from "./utils"; const expectedUserAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome} Safari/537.36`; diff --git a/libs/electron/src/utils.ts b/apps/desktop/src/utils.ts similarity index 100% rename from libs/electron/src/utils.ts rename to apps/desktop/src/utils.ts diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index f2f28c73065..9ae70b05f9a 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -11,11 +11,11 @@ "baseUrl": ".", "paths": { "@bitwarden/common/*": ["../../libs/common/src/*"], - "@bitwarden/angular/*": ["../../libs/angular/src/*"], - "@bitwarden/electron/*": ["../../libs/electron/src/*"] + "@bitwarden/angular/*": ["../../libs/angular/src/*"] } }, "angularCompilerOptions": { + "strictTemplates": true, "preserveWhitespaces": true }, "include": ["src", "../../libs/common/src/services/**/*.worker.ts"] diff --git a/apps/web/README.md b/apps/web/README.md index 1193b139a4d..f75c7142389 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -21,4 +21,4 @@ ## Documentation -Please refer to the [Web vault section](https://contributing.bitwarden.com/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Web vault section](https://contributing.bitwarden.com/docs/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 5bd5e6b0608..eb13f2febb2 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -17,6 +17,6 @@ }, "flags": { "showTrial": true, - "showPasswordless": false + "showPasswordless": true } } diff --git a/apps/web/package.json b/apps/web/package.json index 7098100f60f..7e39254626e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2022.10.2", + "version": "2022.11.2", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/apps/web/src/app/accounts/login/login.component.html b/apps/web/src/app/accounts/login/login.component.html index 3b7724559fe..8202af0dfd8 100644 --- a/apps/web/src/app/accounts/login/login.component.html +++ b/apps/web/src/app/accounts/login/login.component.html @@ -78,7 +78,7 @@ diff --git a/apps/web/src/app/accounts/register-form/register-form.component.html b/apps/web/src/app/accounts/register-form/register-form.component.html index d9456d5a6e9..9ae891a4701 100644 --- a/apps/web/src/app/accounts/register-form/register-form.component.html +++ b/apps/web/src/app/accounts/register-form/register-form.component.html @@ -34,7 +34,7 @@ @@ -143,7 +143,7 @@ class="btn btn-outline-danger btn-submit" (click)="removeSponsorship()" [appApiAction]="removeSponsorshipPromise" - [disabled]="removeSponsorshipBtn.loading" + [disabled]="$any(removeSponsorshipBtn).loading" *ngIf="isSponsoredSubscription" > @@ -230,7 +230,7 @@ class="btn btn-outline-danger btn-submit ml-1" (click)="cancel()" [appApiAction]="cancelPromise" - [disabled]="cancelBtn.loading" + [disabled]="$any(cancelBtn).loading" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel" > diff --git a/apps/web/src/app/organizations/components/access-selector/access-selector.component.html b/apps/web/src/app/organizations/components/access-selector/access-selector.component.html index 10390b3e053..cc4d1d7fa78 100644 --- a/apps/web/src/app/organizations/components/access-selector/access-selector.component.html +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.component.html @@ -53,11 +53,13 @@
{{ item.labelName }} - + {{ "invited" | i18n }}
-
{{ item.email }}
+
+ {{ $any(item).email }} +
@@ -110,11 +112,11 @@ - {{ item.role | userType: "-" }} + {{ $any(item).role | userType: "-" }} - {{ item.viaGroupName ?? "-" }} + {{ $any(item).viaGroupName ?? "-" }} diff --git a/apps/web/src/app/organizations/manage/collection-add-edit.component.html b/apps/web/src/app/organizations/manage/collection-add-edit.component.html index 97c973a4195..41368d589a7 100644 --- a/apps/web/src/app/organizations/manage/collection-add-edit.component.html +++ b/apps/web/src/app/organizations/manage/collection-add-edit.component.html @@ -81,7 +81,7 @@ @@ -140,17 +140,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/entity-events.component.html b/apps/web/src/app/organizations/manage/entity-events.component.html index 122d0b81888..b41e6f3ba16 100644 --- a/apps/web/src/app/organizations/manage/entity-events.component.html +++ b/apps/web/src/app/organizations/manage/entity-events.component.html @@ -52,11 +52,11 @@ type="button" class="btn btn-sm btn-outline-primary ml-3" (click)="loadEvents(true)" - [disabled]="loaded && refreshBtn.loading" + [disabled]="loaded && $any(refreshBtn).loading" > {{ "refresh" | i18n }} @@ -101,7 +101,7 @@ type="button" class="btn btn-block btn-link btn-submit" (click)="loadEvents(false)" - [disabled]="loaded && moreBtn.loading" + [disabled]="loaded && $any(moreBtn).loading" *ngIf="continuationToken" > diff --git a/apps/web/src/app/organizations/manage/events.component.html b/apps/web/src/app/organizations/manage/events.component.html index a7468bcf378..e2c03f0dde5 100644 --- a/apps/web/src/app/organizations/manage/events.component.html +++ b/apps/web/src/app/organizations/manage/events.component.html @@ -95,7 +95,7 @@ bitButton buttonType="primary" (click)="loadEvents(false)" - [disabled]="loaded && moreBtn.loading" + [disabled]="loaded && $any(moreBtn).loading" *ngIf="continuationToken" > @@ -132,7 +132,7 @@ type="checkbox" [(ngModel)]="c.hidePasswords" name="Collection[{{ i }}].HidePasswords" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -140,7 +140,7 @@ type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{ i }}].ReadOnly" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -164,17 +164,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/user-add-edit.component.html b/apps/web/src/app/organizations/manage/user-add-edit.component.html index 7d15eaa22f3..458d1d0bab3 100644 --- a/apps/web/src/app/organizations/manage/user-add-edit.component.html +++ b/apps/web/src/app/organizations/manage/user-add-edit.component.html @@ -341,7 +341,7 @@ @@ -354,7 +354,7 @@ type="checkbox" [(ngModel)]="c.hidePasswords" name="Collection[{{ i }}].HidePasswords" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -362,7 +362,7 @@ type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{ i }}].ReadOnly" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -416,17 +416,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/user-groups.component.html b/apps/web/src/app/organizations/manage/user-groups.component.html index 347125e9825..217af6ea44b 100644 --- a/apps/web/src/app/organizations/manage/user-groups.component.html +++ b/apps/web/src/app/organizations/manage/user-groups.component.html @@ -34,7 +34,7 @@ diff --git a/apps/web/src/app/organizations/policies/policies.component.ts b/apps/web/src/app/organizations/policies/policies.component.ts index 0a316f5597d..717e35ac968 100644 --- a/apps/web/src/app/organizations/policies/policies.component.ts +++ b/apps/web/src/app/organizations/policies/policies.component.ts @@ -29,7 +29,7 @@ export class PoliciesComponent implements OnInit { organization: Organization; private orgPolicies: PolicyResponse[]; - private policiesEnabledMap: Map = new Map(); + protected policiesEnabledMap: Map = new Map(); constructor( private route: ActivatedRoute, diff --git a/apps/web/src/app/organizations/settings/organization-billing.component.ts b/apps/web/src/app/organizations/settings/organization-billing.component.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/web/src/app/organizations/vault/add-edit.component.ts b/apps/web/src/app/organizations/vault/add-edit.component.ts index e8138b1abc7..27a2b9a11dd 100644 --- a/apps/web/src/app/organizations/vault/add-edit.component.ts +++ b/apps/web/src/app/organizations/vault/add-edit.component.ts @@ -18,7 +18,6 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; import { CipherData } from "@bitwarden/common/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/models/domain/cipher"; -import { Organization } from "@bitwarden/common/models/domain/organization"; import { CipherCreateRequest } from "@bitwarden/common/models/request/cipher-create.request"; import { CipherRequest } from "@bitwarden/common/models/request/cipher.request"; @@ -29,7 +28,6 @@ import { AddEditComponent as BaseAddEditComponent } from "../../vault/add-edit.c templateUrl: "../../vault/add-edit.component.html", }) export class AddEditComponent extends BaseAddEditComponent { - organization: Organization; originalCipher: Cipher = null; constructor( diff --git a/apps/web/src/app/organizations/vault/vault-items.component.ts b/apps/web/src/app/organizations/vault/vault-items.component.ts index 670217a724a..044b3d6d752 100644 --- a/apps/web/src/app/organizations/vault/vault-items.component.ts +++ b/apps/web/src/app/organizations/vault/vault-items.component.ts @@ -12,7 +12,6 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; -import { Organization } from "@bitwarden/common/models/domain/organization"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { VaultItemsComponent as BaseVaultItemsComponent } from "../../vault/vault-items.component"; @@ -24,9 +23,6 @@ import { VaultItemsComponent as BaseVaultItemsComponent } from "../../vault/vaul export class VaultItemsComponent extends BaseVaultItemsComponent { @Output() onEventsClicked = new EventEmitter(); - organization: Organization; - accessEvents = false; - protected allCiphers: CipherView[] = []; constructor( @@ -86,6 +82,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent { async search(timeout: number = null) { await super.search(timeout, this.allCiphers); } + events(c: CipherView) { this.onEventsClicked.emit(c); } diff --git a/apps/web/src/app/organizations/vault/vault.component.html b/apps/web/src/app/organizations/vault/vault.component.html index dcc20913b9b..7a75ce01c42 100644 --- a/apps/web/src/app/organizations/vault/vault.component.html +++ b/apps/web/src/app/organizations/vault/vault.component.html @@ -19,7 +19,7 @@

{{ "vaultItems" | i18n }} - + (); + passwordStrengthMap = new Map(); private passwordStrengthCache = new Map(); @@ -110,7 +111,7 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen return true; } - private scoreKey(score: number): [string, string] { + private scoreKey(score: number): [string, BadgeTypes] { switch (score) { case 4: return ["strong", "success"]; diff --git a/apps/web/src/app/reports/shared/report-card/report-card.component.ts b/apps/web/src/app/reports/shared/report-card/report-card.component.ts index 063d52bc682..13a2a04e0d2 100644 --- a/apps/web/src/app/reports/shared/report-card/report-card.component.ts +++ b/apps/web/src/app/reports/shared/report-card/report-card.component.ts @@ -1,5 +1,7 @@ import { Component, Input } from "@angular/core"; +import { Icon } from "@bitwarden/components"; + import { ReportVariant } from "../models/report-variant"; @Component({ @@ -10,7 +12,7 @@ export class ReportCardComponent { @Input() title: string; @Input() description: string; @Input() route: string; - @Input() icon: string; + @Input() icon: Icon; @Input() variant: ReportVariant; protected get disabled() { diff --git a/apps/web/src/app/send/access.component.html b/apps/web/src/app/send/access.component.html index b5818610203..91ae288d3c2 100644 --- a/apps/web/src/app/send/access.component.html +++ b/apps/web/src/app/send/access.component.html @@ -81,7 +81,7 @@ id="text" rows="8" name="Text" - [(ngModel)]="sendText" + [ngModel]="sendText" class="form-control" readonly > diff --git a/apps/web/src/app/send/add-edit.component.html b/apps/web/src/app/send/add-edit.component.html index dcc60a3c7af..dd9f61c6c58 100644 --- a/apps/web/src/app/send/add-edit.component.html +++ b/apps/web/src/app/send/add-edit.component.html @@ -55,7 +55,7 @@ name="Type_{{ o.value }}" id="type_{{ o.value }}" [value]="o.value" - (change)="typeChanged(o)" + (change)="typeChanged()" [checked]="send.type === o.value" />