diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 94f0e939b74..cc70d3f5b4a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,12 +29,12 @@ libs/tools @bitwarden/team-tools-dev bitwarden_license/bit-web/src/app/tools @bitwarden/team-tools-dev bitwarden_license/bit-common/src/tools @bitwarden/team-tools-dev -## Localization/Crowdin (Tools team) -apps/browser/src/_locales @bitwarden/team-tools-dev -apps/browser/store/locales @bitwarden/team-tools-dev -apps/cli/src/locales @bitwarden/team-tools-dev -apps/desktop/src/locales @bitwarden/team-tools-dev -apps/web/src/locales @bitwarden/team-tools-dev +## Localization/Crowdin (Platform and Tools team) +apps/browser/src/_locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev +apps/browser/store/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev +apps/cli/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev +apps/desktop/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev +apps/web/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev ## Vault team files ## apps/browser/src/vault @bitwarden/team-vault-dev @@ -131,6 +131,7 @@ apps/web/src/app/key-management @bitwarden/team-key-management-dev apps/browser/src/key-management @bitwarden/team-key-management-dev apps/cli/src/key-management @bitwarden/team-key-management-dev libs/key-management @bitwarden/team-key-management-dev +libs/key-management-ui @bitwarden/team-key-management-dev libs/common/src/key-management @bitwarden/team-key-management-dev apps/desktop/destkop_native/core/src/biometric/ @bitwarden/team-key-management-dev diff --git a/.github/codecov.yml b/.github/codecov.yml index b4774407206..b79cdd9f413 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,2 +1,66 @@ ignore: - "**/*.spec.ts" # Tests + +component_management: + default_rules: + statuses: + - type: project + target: auto + individual_components: + - component_id: key-management-biometrics + name: Key Management - Biometrics + paths: + - apps/browser/src/key-management/biometrics/** + - apps/cli/src/key-management/cli-biometrics-service.ts + - apps/desktop/destkop_native/core/src/biometric/** + - apps/desktop/src/key-management/biometrics/** + - apps/desktop/src/services/biometric-message-handler.service.ts + - apps/web/src/app/key-management/web-biometric.service.ts + - libs/key-management/src/biometrics/** + - component_id: key-management-lock + name: Key Management - Lock + paths: + - apps/browser/src/key-management/lock/** + - apps/desktop/src/key-management/lock/** + - apps/web/src/app/key-management/lock/** + - libs/key-management-ui/src/lock/** + - component_id: key-management-ipc + name: Key Management - IPC + paths: + - apps/browser/src/background/nativeMessaging.background.ts + - apps/desktop/src/services/native-messaging.service.ts + - component_id: key-management-key-rotation + name: Key Management - Key Rotation + paths: + - apps/web/src/app/key-management/key-rotation/** + - apps/web/src/app/key-management/migrate-encryption/** + - libs/key-management/src/user-asymmetric-key-regeneration/** + - component_id: key-management-process-reload + name: Key Management - Process Reload + paths: + - apps/web/src/app/key-management/services/web-process-reload.service.ts + - libs/common/src/key-management/services/default-process-reload.service.ts + - component_id: key-management-keys + name: Key Management - Keys + paths: + - libs/key-management/src/kdf-config.service.ts + - libs/key-management/src/key.service.ts + - libs/common/src/key-management/master-password/** + - component_id: key-management-crypto + name: Key Management - Crypto + paths: + - libs/common/src/key-management/crypto/** + - component_id: key-management + name: Key Management + paths: + - apps/browser/src/key-management/** + - apps/browser/src/background/nativeMessaging.background.ts + - apps/cli/src/key-management/** + - apps/desktop/destkop_native/core/src/biometric/** + - apps/desktop/src/key-management/** + - apps/desktop/src/services/biometric-message-handler.service.ts + - apps/desktop/src/services/native-messaging.service.ts + - apps/web/src/app/key-managemen/** + - libs/common/src/key-management/** + - libs/key-management/** + - libs/key-management-ui/** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 051fb6d771c..8c214b99ed3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -103,15 +103,15 @@ jobs: matrix: os: - ubuntu-22.04 - - macos-latest - - windows-latest + - macos-14 + - windows-2022 steps: - name: Check Rust version run: rustup --version - name: Install gnome-keyring - if: ${{ matrix.os=='ubuntu-latest' }} + if: ${{ matrix.os=='ubuntu-22.04' }} run: | sudo apt-get update sudo apt-get install -y gnome-keyring dbus-x11 @@ -124,7 +124,7 @@ jobs: run: cargo build - name: Test Ubuntu - if: ${{ matrix.os=='ubuntu-latest' }} + if: ${{ matrix.os=='ubuntu-22.04' }} working-directory: ./apps/desktop/desktop_native run: | eval "$(dbus-launch --sh-syntax)" @@ -135,11 +135,41 @@ jobs: cargo test -- --test-threads=1 - name: Test macOS - if: ${{ matrix.os=='macos-latest' }} + if: ${{ matrix.os=='macos-14' }} working-directory: ./apps/desktop/desktop_native run: cargo test -- --test-threads=1 - name: Test Windows - if: ${{ matrix.os=='windows-latest'}} + if: ${{ matrix.os=='windows-2022'}} working-directory: ./apps/desktop/desktop_native/core run: cargo test -- --test-threads=1 + + rust-coverage: + name: Rust Coverage + runs-on: macos-14 + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Install rust + uses: dtolnay/rust-toolchain@a54c7afa936fefeb4456b2dd8068152669aa8203 # stable + with: + toolchain: stable + components: llvm-tools + + - name: Cache cargo registry + uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + with: + workspaces: "apps/desktop/desktop_native -> target" + + - name: Install cargo-llvm-cov + run: cargo install cargo-llvm-cov --version 0.6.16 + + - name: Generate coverage + working-directory: ./apps/desktop/desktop_native + run: cargo llvm-cov --all-features --lcov --output-path lcov.info --workspace --no-cfg-coverage + + - name: Upload to codecov.io + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 + with: + files: ./apps/desktop/desktop_native/lcov.info diff --git a/apps/browser/scripts/package-safari.ps1 b/apps/browser/scripts/package-safari.ps1 index 075ed606070..ce208478098 100755 --- a/apps/browser/scripts/package-safari.ps1 +++ b/apps/browser/scripts/package-safari.ps1 @@ -52,7 +52,7 @@ foreach ($subBuildPath in $subBuildPaths) { "--verbose", "--force", "--sign", - "E7C9978F6FBCE0553429185C405E61F5380BE8EB", + "4B9662CAB74E8E4F4ECBDD9EDEF2543659D95E3C", "--entitlements", $entitlementsPath ) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 10efba9422f..8698315b57c 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4155,15 +4155,6 @@ "itemName": { "message": "Item name" }, - "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", - "placeholders": { - "collections": { - "content": "$1", - "example": "Work, Personal" - } - } - }, "organizationIsDeactivated": { "message": "Organization is deactivated" }, @@ -4896,6 +4887,15 @@ "extraWide": { "message": "Extra wide" }, + "cannotRemoveViewOnlyCollections": { + "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "placeholders": { + "collections": { + "content": "$1", + "example": "Work, Personal" + } + } + }, "updateDesktopAppOrDisableFingerprintDialogTitle": { "message": "Please update your desktop application" }, diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index acf0dedde27..6757972e839 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -424,6 +424,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ } await this.setupSubmitListenerOnFormlessField(formFieldElement); + return; } /** @@ -439,15 +440,16 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ this.formElements.add(formElement); formElement.addEventListener(EVENTS.SUBMIT, this.handleFormFieldSubmitEvent); - const closesSubmitButton = await this.findSubmitButton(formElement); + const closestSubmitButton = await this.findSubmitButton(formElement); // If we cannot find a submit button within the form, check for a submit button outside the form. - if (!closesSubmitButton) { + if (!closestSubmitButton) { await this.setupSubmitListenerOnFormlessField(formFieldElement); return; } - this.setupSubmitButtonEventListeners(closesSubmitButton); + this.setupSubmitButtonEventListeners(closestSubmitButton); + return; } } @@ -459,9 +461,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ */ private async setupSubmitListenerOnFormlessField(formFieldElement: FillableFormFieldElement) { if (formFieldElement && !this.fieldsWithSubmitElements.has(formFieldElement)) { - const closesSubmitButton = await this.findClosestFormlessSubmitButton(formFieldElement); - this.setupSubmitButtonEventListeners(closesSubmitButton); + const closestSubmitButton = await this.findClosestFormlessSubmitButton(formFieldElement); + + this.setupSubmitButtonEventListeners(closestSubmitButton); } + return; } /** diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index 16b11b98866..378521cfc42 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -747,7 +747,7 @@ describe("AutofillService", () => { jest.spyOn(autofillService as any, "generateFillScript"); jest.spyOn(autofillService as any, "generateLoginFillScript"); jest.spyOn(logService, "info"); - jest.spyOn(cipherService, "updateLastUsedDate"); + jest.spyOn(chrome.runtime, "sendMessage"); jest.spyOn(eventCollectionService, "collect"); const autofillResult = await autofillService.doAutoFill(autofillOptions); @@ -769,7 +769,10 @@ describe("AutofillService", () => { ); expect(autofillService["generateLoginFillScript"]).toHaveBeenCalled(); expect(logService.info).not.toHaveBeenCalled(); - expect(cipherService.updateLastUsedDate).toHaveBeenCalledWith(autofillOptions.cipher.id); + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + cipherId: autofillOptions.cipher.id, + command: "updateLastUsedDate", + }); expect(chrome.tabs.sendMessage).toHaveBeenCalledWith( autofillOptions.pageDetails[0].tab.id, { @@ -890,11 +893,11 @@ describe("AutofillService", () => { it("skips updating the cipher's last used date if the passed options indicate that we should skip the last used cipher", async () => { autofillOptions.skipLastUsed = true; - jest.spyOn(cipherService, "updateLastUsedDate"); + jest.spyOn(chrome.runtime, "sendMessage"); await autofillService.doAutoFill(autofillOptions); - expect(cipherService.updateLastUsedDate).not.toHaveBeenCalled(); + expect(chrome.runtime.sendMessage).not.toHaveBeenCalled(); }); it("returns early if the fillScript cannot be generated", async () => { diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 6d0e9954ade..c35b19990cb 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -463,8 +463,13 @@ export default class AutofillService implements AutofillServiceInterface { fillScript.properties.delay_between_operations = 20; didAutofill = true; + if (!options.skipLastUsed) { - await this.cipherService.updateLastUsedDate(options.cipher.id); + // In order to prevent a UI update send message to background script to update last used date + await chrome.runtime.sendMessage({ + command: "updateLastUsedDate", + cipherId: options.cipher.id, + }); } // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts index 7471c298917..5f3bc4dfff9 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts @@ -317,6 +317,7 @@ describe("CollectAutofillContentService", () => { __form__0: { opid: "__form__0", htmlAction: formAction, + htmlClass: null, htmlName: formName, htmlID: formId, htmlMethod: formMethod, @@ -544,6 +545,7 @@ describe("CollectAutofillContentService", () => { __form__0: { opid: "__form__0", htmlAction: formAction1, + htmlClass: null, htmlName: formName1, htmlID: formId1, htmlMethod: formMethod1, @@ -551,6 +553,7 @@ describe("CollectAutofillContentService", () => { __form__1: { opid: "__form__1", htmlAction: formAction2, + htmlClass: null, htmlName: formName2, htmlID: formId2, htmlMethod: formMethod2, diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index b858af25fae..0f9c8993014 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -228,6 +228,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ opid: formElement.opid, htmlAction: this.getFormActionAttribute(formElement), htmlName: this.getPropertyOrAttribute(formElement, "name"), + htmlClass: this.getPropertyOrAttribute(formElement, "class"), htmlID: this.getPropertyOrAttribute(formElement, "id"), htmlMethod: this.getPropertyOrAttribute(formElement, "method"), }); @@ -982,8 +983,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ const queueLength = this.mutationsQueue.length; if (!this.domQueryService.pageContainsShadowDomElements()) { - // Checking if a page contains shadowDOM elements is a heavy operation and doesn't have to be done immediately, so we can call this within an idle moment on the event loop. - requestIdleCallbackPolyfill(this.checkPageContainsShadowDom, { timeout: 500 }); + this.checkPageContainsShadowDom(); } for (let queueIndex = 0; queueIndex < queueLength; queueIndex++) { diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index 43efd338c6e..dff40a3ab93 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -56,6 +56,7 @@ export class InlineMenuFieldQualificationService "neuer benutzer", "neues passwort", "neue e-mail", + "pwdcheck", ]; private updatePasswordFieldKeywords = [ "update password", diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index b75847dbbfe..7729981740d 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -75,12 +75,16 @@ import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/bill import { ClientType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/bulk-encrypt.service.implementation"; +import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; +import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service"; +import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation"; import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { RegionConfig } from "@bitwarden/common/platform/abstractions/environment.service"; import { Fido2ActiveRequestManager as Fido2ActiveRequestManagerAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-active-request-manager.abstraction"; import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction"; @@ -123,10 +127,6 @@ import { ConfigApiService } from "@bitwarden/common/platform/services/config/con import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; -import { BulkEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/bulk-encrypt.service.implementation"; -import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; -import { FallbackBulkEncryptService } from "@bitwarden/common/platform/services/cryptography/fallback-bulk-encrypt.service"; -import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { Fido2ActiveRequestManager } from "@bitwarden/common/platform/services/fido2/fido2-active-request-manager"; import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service"; import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service"; @@ -208,7 +208,7 @@ import { ImportApiServiceAbstraction, ImportService, ImportServiceAbstraction, -} from "@bitwarden/importer/core"; +} from "@bitwarden/importer-core"; import { BiometricsService, BiometricStateService, @@ -809,7 +809,7 @@ export default class MainBackground { this.apiService, ); - this.ssoLoginService = new SsoLoginService(this.stateProvider); + this.ssoLoginService = new SsoLoginService(this.stateProvider, this.logService); this.userVerificationApiService = new UserVerificationApiService(this.apiService); @@ -1142,6 +1142,7 @@ export default class MainBackground { this.accountService, lockService, this.billingAccountProfileStateService, + this.cipherService, ); this.nativeMessagingBackground = new NativeMessagingBackground( this.keyService, diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 21c8f351e8d..8c369e966c5 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -4,9 +4,9 @@ import { delay, filter, firstValueFrom, from, map, race, timer } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 2a756293070..b9622e82005 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -16,6 +16,7 @@ import { MessageListener, isExternalMessage } from "@bitwarden/common/platform/m import { devFlagEnabled } from "@bitwarden/common/platform/misc/flags"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { NotificationsService } from "@bitwarden/common/platform/notifications"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { BiometricsCommands } from "@bitwarden/key-management"; @@ -53,6 +54,7 @@ export default class RuntimeBackground { private accountService: AccountService, private readonly lockService: LockService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private cipherService: CipherService, ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -200,6 +202,9 @@ export default class RuntimeBackground { case BiometricsCommands.GetBiometricsStatusForUser: { return await this.main.biometricsService.getBiometricsStatusForUser(msg.userId); } + case "updateLastUsedDate": { + return await this.cipherService.updateLastUsedDate(msg.cipherId); + } case "getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag": { return await this.configService.getFeatureFlag( FeatureFlag.UseTreeWalkerApiForPageDetailsCollection, diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts index 4d6a403a18a..1b93e33a94e 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts @@ -1,6 +1,6 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Lazy } from "@bitwarden/common/platform/misc/lazy"; diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.ts index 900212ddefa..b030ff46270 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Subject } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts index 762380071b7..fe049c4f1db 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts @@ -147,7 +147,9 @@ describe("Browser Utils Service", () => { describe("isViewOpen", () => { it("returns false if a heartbeat response is not received", async () => { - BrowserApi.sendMessageWithResponse = jest.fn().mockResolvedValueOnce(undefined); + chrome.runtime.sendMessage = jest.fn().mockImplementation((message, callback) => { + callback(undefined); + }); const isViewOpen = await browserPlatformUtilsService.isViewOpen(); @@ -155,16 +157,29 @@ describe("Browser Utils Service", () => { }); it("returns true if a heartbeat response is received", async () => { - BrowserApi.sendMessageWithResponse = jest - .fn() - .mockImplementationOnce((subscriber) => - Promise.resolve((subscriber === "checkVaultPopupHeartbeat") as any), - ); + chrome.runtime.sendMessage = jest.fn().mockImplementation((message, callback) => { + callback(message.command === "checkVaultPopupHeartbeat"); + }); const isViewOpen = await browserPlatformUtilsService.isViewOpen(); expect(isViewOpen).toBe(true); }); + + it("returns false if special error is sent", async () => { + chrome.runtime.sendMessage = jest.fn().mockImplementation((message, callback) => { + chrome.runtime.lastError = new Error( + "Could not establish connection. Receiving end does not exist.", + ); + callback(undefined); + }); + + const isViewOpen = await browserPlatformUtilsService.isViewOpen(); + + expect(isViewOpen).toBe(false); + + chrome.runtime.lastError = null; + }); }); describe("copyToClipboard", () => { @@ -228,6 +243,7 @@ describe("Browser Utils Service", () => { }); it("copies the passed text using the offscreen document if the extension is using manifest v3", async () => { + BrowserApi.sendMessageWithResponse = jest.fn(); const text = "test"; offscreenDocumentService.offscreenApiSupported.mockReturnValue(true); getManifestVersionSpy.mockReturnValue(3); @@ -302,6 +318,7 @@ describe("Browser Utils Service", () => { }); it("reads the clipboard text using the offscreen document", async () => { + BrowserApi.sendMessageWithResponse = jest.fn(); offscreenDocumentService.offscreenApiSupported.mockReturnValue(true); getManifestVersionSpy.mockReturnValue(3); offscreenDocumentService.withDocument.mockImplementationOnce((_, __, callback) => diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts index 3679b2731e3..c9200ecc1a4 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts @@ -169,7 +169,28 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic // Query views on safari since chrome.runtime.sendMessage does not timeout and will hang. return BrowserApi.isPopupOpen(); } - return Boolean(await BrowserApi.sendMessageWithResponse("checkVaultPopupHeartbeat")); + + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ command: "checkVaultPopupHeartbeat" }, (response) => { + if (chrome.runtime.lastError != null) { + // This error means that nothing was there to listen to the message, + // meaning the view is not open. + if ( + chrome.runtime.lastError.message === + "Could not establish connection. Receiving end does not exist." + ) { + resolve(false); + return; + } + + // All unhandled errors still reject + reject(chrome.runtime.lastError); + return; + } + + resolve(Boolean(response)); + }); + }); } lockTimeout(): number { diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 1a78492d555..257497fb13d 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -55,13 +55,13 @@ import { } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AnimationControlService, DefaultAnimationControlService, } from "@bitwarden/common/platform/abstractions/animation-control.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html index eae8e2cc980..071873b40c9 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html @@ -7,4 +7,5 @@ [description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null" showAutofillButton [primaryActionAutofill]="clickItemsToAutofillVaultView" + [groupByType]="groupByType()" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts index d2dd21be6d8..03d84120785 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts @@ -1,5 +1,6 @@ import { CommonModule } from "@angular/common"; import { Component, OnInit } from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; import { combineLatest, firstValueFrom, map, Observable } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -48,6 +49,10 @@ export class AutofillVaultListItemsComponent implements OnInit { clickItemsToAutofillVaultView = false; + protected groupByType = toSignal( + this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => !hasFilter)), + ); + /** * Observable that determines whether the empty autofill tip should be shown. * The tip is shown when there are no login ciphers to autofill, no filter is applied, and autofill is allowed in diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 4c7067df53a..6e6e30b359b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -27,7 +27,7 @@ - + {{ "clone" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 8634d680052..94b4c2b855b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -97,6 +97,9 @@ export class ItemMoreOptionsComponent implements OnInit { return this.cipher.edit; } + get canViewPassword() { + return this.cipher.viewPassword; + } /** * Determines if the cipher can be autofilled. */ diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts index d57b1d2fe36..db3fff04bbb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts @@ -11,11 +11,11 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { ButtonModule, DialogService, MenuModule, NoItemsModule } from "@bitwarden/components"; +import { AddEditFolderDialogComponent } from "@bitwarden/vault"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; -import { AddEditFolderDialogComponent } from "../add-edit-folder-dialog/add-edit-folder-dialog.component"; export interface NewItemInitialValues { folderId?: string; @@ -72,6 +72,6 @@ export class NewItemDropdownV2Component implements OnInit { } openFolderDialog() { - this.dialogService.open(AddEditFolderDialogComponent); + AddEditFolderDialogComponent.open(this.dialogService); } } diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts index 3b9dc9a1647..bcea2e76190 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts @@ -6,11 +6,12 @@ import { combineLatest, map, take } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { DisclosureTriggerForDirective, IconButtonModule } from "@bitwarden/components"; +import { + DisclosureComponent, + DisclosureTriggerForDirective, + IconButtonModule, +} from "@bitwarden/components"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { DisclosureComponent } from "../../../../../../../../libs/components/src/disclosure/disclosure.component"; import { runInsideAngular } from "../../../../../platform/browser/run-inside-angular.operator"; import { VaultPopupListFiltersService } from "../../../../../vault/popup/services/vault-popup-list-filters.service"; import { VaultListFiltersComponent } from "../vault-list-filters/vault-list-filters.component"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html index 1593c747f7d..2272d3fbd6c 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html @@ -1,9 +1,13 @@ - + - - - - - - - - - - - - + + +

+ {{ group.subHeaderKey | i18n }} +

+
+ + + + + + + + + + + + + + + + + +
diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts index 725aaac4666..f95790cda5f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts @@ -9,11 +9,14 @@ import { EventEmitter, inject, Input, - OnInit, Output, Signal, signal, ViewChild, + computed, + OnInit, + ChangeDetectionStrategy, + input, } from "@angular/core"; import { Router } from "@angular/router"; import { firstValueFrom, Observable, map } from "rxjs"; @@ -23,6 +26,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { BadgeModule, @@ -73,6 +77,7 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options selector: "app-vault-list-items-container", templateUrl: "vault-list-items-container.component.html", standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, }) export class VaultListItemsContainerComponent implements OnInit, AfterViewInit { private compactModeService = inject(CompactModeService); @@ -110,11 +115,51 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit { */ private viewCipherTimeout: number | null; + ciphers = input([]); + /** - * The list of ciphers to display. + * If true, we will group ciphers by type (Login, Card, Identity) + * within subheadings in a single container, converted to a WritableSignal. */ - @Input() - ciphers: PopupCipherView[] = []; + groupByType = input(false); + + /** + * Computed signal for a grouped list of ciphers with an optional header + */ + cipherGroups$ = computed< + { + subHeaderKey?: string | null; + ciphers: PopupCipherView[]; + }[] + >(() => { + const groups: { [key: string]: CipherView[] } = {}; + + this.ciphers().forEach((cipher) => { + let groupKey; + + if (this.groupByType()) { + switch (cipher.type) { + case CipherType.Card: + groupKey = "cards"; + break; + case CipherType.Identity: + groupKey = "identities"; + break; + } + } + + if (!groups[groupKey]) { + groups[groupKey] = []; + } + + groups[groupKey].push(cipher); + }); + + return Object.keys(groups).map((key) => ({ + subHeaderKey: this.groupByType ? key : "", + ciphers: groups[key], + })); + }); /** * Title for the vault list item section. diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts index 67e069d388a..e20bb1f1bcd 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts @@ -11,10 +11,8 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { PasswordHistoryViewComponent } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { PasswordHistoryViewComponent } from "../../../../../../../../libs/vault/src/components/password-history-view/password-history-view.component"; import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index 43471e56e7b..6e017042711 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -69,13 +69,13 @@ { return hasSearchText || Object.values(filters).some((filter) => filter !== null); }), + shareReplay({ bufferSize: 1, refCount: true }), ); /** diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts index 3aab9f935e4..deddbd444fc 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts @@ -14,17 +14,15 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; -import { BadgeModule, CheckboxModule, Option } from "@bitwarden/components"; +import { + BadgeModule, + CardComponent, + CheckboxModule, + FormFieldModule, + Option, + SelectModule, +} from "@bitwarden/components"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { CardComponent } from "../../../../../../libs/components/src/card/card.component"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { FormFieldModule } from "../../../../../../libs/components/src/form-field/form-field.module"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { SelectModule } from "../../../../../../libs/components/src/select/select.module"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupCompactModeService } from "../../../platform/popup/layout/popup-compact-mode.service"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts index 9c202e26fef..6689f5a6c6d 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts @@ -14,10 +14,10 @@ import { UserId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { DialogService } from "@bitwarden/components"; +import { AddEditFolderDialogComponent } from "@bitwarden/vault"; import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; -import { AddEditFolderDialogComponent } from "../components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component"; import { FoldersV2Component } from "./folders-v2.component"; @@ -27,8 +27,8 @@ import { FoldersV2Component } from "./folders-v2.component"; template: ``, }) class MockPopupHeaderComponent { - @Input() pageTitle: string; - @Input() backAction: () => void; + @Input() pageTitle: string = ""; + @Input() backAction: () => void = () => {}; } @Component({ @@ -37,14 +37,15 @@ class MockPopupHeaderComponent { template: ``, }) class MockPopupFooterComponent { - @Input() pageTitle: string; + @Input() pageTitle: string = ""; } describe("FoldersV2Component", () => { let component: FoldersV2Component; let fixture: ComponentFixture; const folderViews$ = new BehaviorSubject([]); - const open = jest.fn(); + const open = jest.spyOn(AddEditFolderDialogComponent, "open"); + const mockDialogService = { open: jest.fn() }; beforeEach(async () => { open.mockClear(); @@ -68,7 +69,7 @@ describe("FoldersV2Component", () => { imports: [MockPopupHeaderComponent, MockPopupFooterComponent], }, }) - .overrideProvider(DialogService, { useValue: { open } }) + .overrideProvider(DialogService, { useValue: mockDialogService }) .compileComponents(); fixture = TestBed.createComponent(FoldersV2Component); @@ -101,9 +102,7 @@ describe("FoldersV2Component", () => { editButton.triggerEventHandler("click"); - expect(open).toHaveBeenCalledWith(AddEditFolderDialogComponent, { - data: { editFolderConfig: { folder } }, - }); + expect(open).toHaveBeenCalledWith(mockDialogService, { editFolderConfig: { folder } }); }); it("opens add dialog for new folder when there are no folders", () => { @@ -114,6 +113,6 @@ describe("FoldersV2Component", () => { addButton.triggerEventHandler("click"); - expect(open).toHaveBeenCalledWith(AddEditFolderDialogComponent, { data: {} }); + expect(open).toHaveBeenCalledWith(mockDialogService, {}); }); }); diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.ts index 8abc3f906c0..f71374e5305 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.ts @@ -12,25 +12,14 @@ import { ButtonModule, DialogService, IconButtonModule, + ItemModule, + NoItemsModule, } from "@bitwarden/components"; -import { VaultIcons } from "@bitwarden/vault"; +import { AddEditFolderDialogComponent, VaultIcons } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { ItemGroupComponent } from "../../../../../../libs/components/src/item/item-group.component"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { ItemModule } from "../../../../../../libs/components/src/item/item.module"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { NoItemsModule } from "../../../../../../libs/components/src/no-items/no-items.module"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; -import { - AddEditFolderDialogComponent, - AddEditFolderDialogData, -} from "../components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component"; @Component({ standalone: true, @@ -42,7 +31,6 @@ import { PopupPageComponent, PopupHeaderComponent, ItemModule, - ItemGroupComponent, NoItemsModule, IconButtonModule, ButtonModule, @@ -78,8 +66,6 @@ export class FoldersV2Component { // If a folder is provided, the edit variant should be shown const editFolderConfig = folder ? { folder } : undefined; - this.dialogService.open(AddEditFolderDialogComponent, { - data: { editFolderConfig }, - }); + AddEditFolderDialogComponent.open(this.dialogService, { editFolderConfig }); } } diff --git a/apps/browser/tsconfig.json b/apps/browser/tsconfig.json index 3eaa7e100a6..81b9bc870c4 100644 --- a/apps/browser/tsconfig.json +++ b/apps/browser/tsconfig.json @@ -24,7 +24,7 @@ "@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"], "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/importer/ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], @@ -51,8 +51,8 @@ }, "include": [ "src", - "../../libs/common/src/platform/services/**/*.worker.ts", "../../libs/common/src/autofill/constants", - "../../libs/common/custom-matchers.d.ts" + "../../libs/common/custom-matchers.d.ts", + "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts" ] } diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index bce41d64d1f..ba60a577a71 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -205,7 +205,7 @@ const mainConfig = { "./src/autofill/deprecated/overlay/pages/button/bootstrap-autofill-overlay-button.deprecated.ts", "overlay/list": "./src/autofill/deprecated/overlay/pages/list/bootstrap-autofill-overlay-list.deprecated.ts", - "encrypt-worker": "../../libs/common/src/platform/services/cryptography/encrypt.worker.ts", + "encrypt-worker": "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts", "content/send-on-installed-message": "./src/vault/content/send-on-installed-message.ts", }, optimization: { diff --git a/apps/cli/src/admin-console/commands/confirm.command.ts b/apps/cli/src/admin-console/commands/confirm.command.ts index 5f5f58163e3..0d5c7ba069c 100644 --- a/apps/cli/src/admin-console/commands/confirm.command.ts +++ b/apps/cli/src/admin-console/commands/confirm.command.ts @@ -5,7 +5,7 @@ import { OrganizationUserConfirmRequest, } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { KeyService } from "@bitwarden/key-management"; diff --git a/apps/cli/src/commands/download.command.ts b/apps/cli/src/commands/download.command.ts index 6a7cda2ac91..01ef675d2a8 100644 --- a/apps/cli/src/commands/download.command.ts +++ b/apps/cli/src/commands/download.command.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index 13152f3d29e..9af28863c09 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -6,10 +6,10 @@ import { CollectionRequest } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CipherExport } from "@bitwarden/common/models/export/cipher.export"; import { CollectionExport } from "@bitwarden/common/models/export/collection.export"; import { FolderExport } from "@bitwarden/common/models/export/folder.export"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; @@ -123,6 +123,9 @@ export class EditCommand { "Item does not belong to an organization. Consider moving it first.", ); } + if (!cipher.viewPassword) { + return Response.noEditPermission(); + } cipher.collectionIds = req; try { diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index c8a6f314240..a90bfa64894 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -13,6 +13,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType } from "@bitwarden/common/enums"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CardExport } from "@bitwarden/common/models/export/card.export"; import { CipherExport } from "@bitwarden/common/models/export/cipher.export"; import { CollectionExport } from "@bitwarden/common/models/export/collection.export"; @@ -23,7 +24,6 @@ import { LoginUriExport } from "@bitwarden/common/models/export/login-uri.export import { LoginExport } from "@bitwarden/common/models/export/login.export"; import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; diff --git a/apps/cli/src/models/response.ts b/apps/cli/src/models/response.ts index 76d9509226d..ac0977182f4 100644 --- a/apps/cli/src/models/response.ts +++ b/apps/cli/src/models/response.ts @@ -39,6 +39,10 @@ export class Response { return Response.error("Not found."); } + static noEditPermission(): Response { + return Response.error("You do not have permission to edit this item"); + } + static badRequest(message: string): Response { return Response.error(message); } diff --git a/apps/cli/src/platform/services/node-env-secure-storage.service.ts b/apps/cli/src/platform/services/node-env-secure-storage.service.ts index 2807509e428..5e31995606f 100644 --- a/apps/cli/src/platform/services/node-env-secure-storage.service.ts +++ b/apps/cli/src/platform/services/node-env-secure-storage.service.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { throwError } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 4eda8cd7abd..f7dad133f94 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -61,6 +61,8 @@ import { import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; +import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; +import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { @@ -83,8 +85,6 @@ import { AppIdService } from "@bitwarden/common/platform/services/app-id.service import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; -import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; -import { FallbackBulkEncryptService } from "@bitwarden/common/platform/services/cryptography/fallback-bulk-encrypt.service"; import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; @@ -151,7 +151,7 @@ import { ImportApiServiceAbstraction, ImportService, ImportServiceAbstraction, -} from "@bitwarden/importer/core"; +} from "@bitwarden/importer-core"; import { DefaultKdfConfigService, KdfConfigService, diff --git a/apps/cli/src/tools/import.command.ts b/apps/cli/src/tools/import.command.ts index d68ef4d043a..f414357c941 100644 --- a/apps/cli/src/tools/import.command.ts +++ b/apps/cli/src/tools/import.command.ts @@ -11,7 +11,7 @@ import { import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { ImportServiceAbstraction, ImportType } from "@bitwarden/importer/core"; +import { ImportServiceAbstraction, ImportType } from "@bitwarden/importer-core"; import { Response } from "../models/response"; import { MessageResponse } from "../models/response/message.response"; diff --git a/apps/cli/src/tools/send/commands/get.command.ts b/apps/cli/src/tools/send/commands/get.command.ts index 0e650c8503d..1d651c50bf0 100644 --- a/apps/cli/src/tools/send/commands/get.command.ts +++ b/apps/cli/src/tools/send/commands/get.command.ts @@ -5,7 +5,7 @@ import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; diff --git a/apps/cli/src/tools/send/commands/receive.command.ts b/apps/cli/src/tools/send/commands/receive.command.ts index 41a6681af55..879d03f6dae 100644 --- a/apps/cli/src/tools/send/commands/receive.command.ts +++ b/apps/cli/src/tools/send/commands/receive.command.ts @@ -5,9 +5,9 @@ import * as inquirer from "inquirer"; import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts index 73027e80112..28f58187fdb 100644 --- a/apps/cli/src/vault/create.command.ts +++ b/apps/cli/src/vault/create.command.ts @@ -11,10 +11,10 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CipherExport } from "@bitwarden/common/models/export/cipher.export"; import { CollectionExport } from "@bitwarden/common/models/export/collection.export"; import { FolderExport } from "@bitwarden/common/models/export/folder.export"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json index 3853cd93126..9d6e3066b29 100644 --- a/apps/cli/tsconfig.json +++ b/apps/cli/tsconfig.json @@ -17,7 +17,7 @@ "@bitwarden/auth/common": ["../../libs/auth/src/common"], "@bitwarden/auth/angular": ["../../libs/auth/src/angular"], "@bitwarden/common/*": ["../../libs/common/src/*"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/generator-core": ["../../libs/tools/generator/core/src"], "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"], diff --git a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts index 71c55a17d1e..c01d581afe8 100644 --- a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts +++ b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts @@ -3,11 +3,11 @@ import "module-alias/register"; import { v4 as uuidv4 } from "uuid"; +import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; -import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; // eslint-disable-next-line no-restricted-imports diff --git a/apps/desktop/resources/entitlements.mas.plist b/apps/desktop/resources/entitlements.mas.plist index 5bb95f76afb..8bca39937d6 100644 --- a/apps/desktop/resources/entitlements.mas.plist +++ b/apps/desktop/resources/entitlements.mas.plist @@ -16,6 +16,8 @@ com.apple.security.files.user-selected.read-write + com.apple.security.device.usb + com.apple.developer.authentication-services.autofill-credential-provider com.apple.security.temporary-exception.files.home-relative-path.read-write diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 9d317b8276a..1f10b551fec 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -54,7 +54,6 @@ import { InternalFolderService } from "@bitwarden/common/vault/abstractions/fold import { CipherType } from "@bitwarden/common/vault/enums"; import { DialogService, ToastOptions, ToastService } from "@bitwarden/components"; import { CredentialGeneratorHistoryDialogComponent } from "@bitwarden/generator-components"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { KeyService, BiometricStateService } from "@bitwarden/key-management"; import { DeleteAccountComponent } from "../auth/delete-account.component"; @@ -65,9 +64,7 @@ import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.compo import { SettingsComponent } from "./accounts/settings.component"; import { ExportDesktopComponent } from "./tools/export/export-desktop.component"; import { CredentialGeneratorComponent } from "./tools/generator/credential-generator.component"; -import { GeneratorComponent } from "./tools/generator.component"; import { ImportDesktopComponent } from "./tools/import/import-desktop.component"; -import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component"; const BroadcasterSubscriptionId = "AppComponent"; const IdleTimeout = 60000 * 10; // 10 minutes @@ -126,7 +123,6 @@ export class AppComponent implements OnInit, OnDestroy { private broadcasterService: BroadcasterService, private folderService: InternalFolderService, private syncService: SyncService, - private passwordGenerationService: PasswordGenerationServiceAbstraction, private cipherService: CipherService, private authService: AuthService, private router: Router, @@ -508,41 +504,13 @@ export class AppComponent implements OnInit, OnDestroy { } async openGenerator() { - const isGeneratorSwapEnabled = await this.configService.getFeatureFlag( - FeatureFlag.GeneratorToolsModernization, - ); - if (isGeneratorSwapEnabled) { - await this.dialogService.open(CredentialGeneratorComponent); - return; - } - - this.modalService.closeAll(); - - [this.modal] = await this.modalService.openViewRef( - GeneratorComponent, - this.generatorModalRef, - (comp) => (comp.comingFromAddEdit = false), - ); - - // eslint-disable-next-line rxjs-angular/prefer-takeuntil - this.modal.onClosed.subscribe(() => { - this.modal = null; - }); + await this.dialogService.open(CredentialGeneratorComponent); + return; } async openGeneratorHistory() { - const isGeneratorSwapEnabled = await this.configService.getFeatureFlag( - FeatureFlag.GeneratorToolsModernization, - ); - if (isGeneratorSwapEnabled) { - await this.dialogService.open(CredentialGeneratorHistoryDialogComponent); - return; - } - - await this.openModal( - PasswordGeneratorHistoryComponent, - this.passwordHistoryRef, - ); + await this.dialogService.open(CredentialGeneratorHistoryDialogComponent); + return; } private async updateAppMenu() { diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index 9516066ac7b..c07a41d4b63 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -47,8 +47,6 @@ import { HeaderComponent } from "./layout/header.component"; import { NavComponent } from "./layout/nav.component"; import { SearchComponent } from "./layout/search/search.component"; import { SharedModule } from "./shared/shared.module"; -import { GeneratorComponent } from "./tools/generator.component"; -import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component"; import { AddEditComponent as SendAddEditComponent } from "./tools/send/add-edit.component"; import { SendComponent } from "./tools/send/send.component"; @@ -80,8 +78,6 @@ import { SendComponent } from "./tools/send/send.component"; HeaderComponent, HintComponent, NavComponent, - GeneratorComponent, - PasswordGeneratorHistoryComponent, PasswordHistoryComponent, PremiumComponent, RegisterComponent, diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 4dca9f4cd76..6a58f36cfb9 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -7,7 +7,7 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index c00b88eb110..c23d9d62ee8 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -50,10 +50,10 @@ import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { ClientType } from "@bitwarden/common/enums"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction"; import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction"; diff --git a/apps/desktop/src/app/tools/generator.component.html b/apps/desktop/src/app/tools/generator.component.html deleted file mode 100644 index 0a8043fe594..00000000000 --- a/apps/desktop/src/app/tools/generator.component.html +++ /dev/null @@ -1,636 +0,0 @@ - diff --git a/apps/desktop/src/app/tools/generator.component.spec.ts b/apps/desktop/src/app/tools/generator.component.spec.ts deleted file mode 100644 index c259b4e52ce..00000000000 --- a/apps/desktop/src/app/tools/generator.component.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { ActivatedRoute } from "@angular/router"; -import { mock, MockProxy } from "jest-mock-extended"; - -import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { ToastService } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; - -import { GeneratorComponent } from "./generator.component"; - -describe("GeneratorComponent", () => { - let component: GeneratorComponent; - let fixture: ComponentFixture; - let platformUtilsServiceMock: MockProxy; - - beforeEach(() => { - platformUtilsServiceMock = mock(); - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.configureTestingModule({ - declarations: [GeneratorComponent, I18nPipe], - providers: [ - { - provide: PasswordGenerationServiceAbstraction, - useValue: mock(), - }, - { - provide: UsernameGenerationServiceAbstraction, - useValue: mock(), - }, - { - provide: PlatformUtilsService, - useValue: platformUtilsServiceMock, - }, - { - provide: I18nService, - useValue: mock(), - }, - { - provide: ActivatedRoute, - useValue: mock(), - }, - { - provide: LogService, - useValue: mock(), - }, - { - provide: CipherService, - useValue: mock(), - }, - { - provide: AccountService, - useValue: mock(), - }, - { - provide: ToastService, - useValue: mock(), - }, - ], - schemas: [NO_ERRORS_SCHEMA], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(GeneratorComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); - - describe("usernameTypesLearnMore()", () => { - it("should call platformUtilsService.launchUri() once", () => { - component.usernameTypesLearnMore(); - expect(platformUtilsServiceMock.launchUri).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/apps/desktop/src/app/tools/generator.component.ts b/apps/desktop/src/app/tools/generator.component.ts deleted file mode 100644 index fc9ff489a1a..00000000000 --- a/apps/desktop/src/app/tools/generator.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Component, NgZone } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; - -import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ToastService } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-generator", - templateUrl: "generator.component.html", -}) -export class GeneratorComponent extends BaseGeneratorComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - usernameGenerationService: UsernameGenerationServiceAbstraction, - accountService: AccountService, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - route: ActivatedRoute, - ngZone: NgZone, - logService: LogService, - toastService: ToastService, - ) { - super( - passwordGenerationService, - usernameGenerationService, - platformUtilsService, - accountService, - i18nService, - logService, - route, - ngZone, - window, - toastService, - ); - } - - usernameTypesLearnMore() { - this.platformUtilsService.launchUri("https://bitwarden.com/help/generator/#username-types"); - } -} diff --git a/apps/desktop/src/app/tools/password-generator-history.component.html b/apps/desktop/src/app/tools/password-generator-history.component.html deleted file mode 100644 index 3144ef7da86..00000000000 --- a/apps/desktop/src/app/tools/password-generator-history.component.html +++ /dev/null @@ -1,52 +0,0 @@ - diff --git a/apps/desktop/src/app/tools/password-generator-history.component.ts b/apps/desktop/src/app/tools/password-generator-history.component.ts deleted file mode 100644 index 0c7c9c4e221..00000000000 --- a/apps/desktop/src/app/tools/password-generator-history.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from "@angular/core"; - -import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "@bitwarden/angular/tools/generator/components/password-generator-history.component"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ToastService } from "@bitwarden/components"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-password-generator-history", - templateUrl: "password-generator-history.component.html", -}) -export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - toastService: ToastService, - ) { - super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); - } -} diff --git a/apps/desktop/src/auth/set-password.component.ts b/apps/desktop/src/auth/set-password.component.ts index a5849992864..ed4e8de5b3d 100644 --- a/apps/desktop/src/auth/set-password.component.ts +++ b/apps/desktop/src/auth/set-password.component.ts @@ -11,8 +11,8 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/desktop/src/platform/services/electron-key.service.ts b/apps/desktop/src/platform/services/electron-key.service.ts index 0db634375ef..6259c1f353f 100644 --- a/apps/desktop/src/platform/services/electron-key.service.ts +++ b/apps/desktop/src/platform/services/electron-key.service.ts @@ -3,8 +3,8 @@ import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/desktop/src/scss/base.scss b/apps/desktop/src/scss/base.scss index 3912d6cbf1d..22eb3df0d17 100644 --- a/apps/desktop/src/scss/base.scss +++ b/apps/desktop/src/scss/base.scss @@ -66,9 +66,9 @@ a { } } -input, +input:not(bit-form-field input), select, -textarea { +textarea:not(bit-form-field textarea) { @include themify($themes) { color: themed("textColor"); background-color: themed("inputBackgroundColor"); diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index 75a72640f2b..5a9befe2112 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -188,44 +188,6 @@ p.lead { } } -.generated-block { - font-size: $font-size-large; - font-family: $font-family-monospace; - padding: 8px 10px 8px 0; - display: flex; - border-radius: $border-radius; - border: 1px solid; - - @include themify($themes) { - background-color: transparent; - border-color: themed("borderColorAlt"); - } - - .modal-body & { - margin-top: 10px; - } - - .generated-wrapper { - text-align: left; - width: 100%; - min-width: 0; - white-space: pre-wrap; - word-break: break-all; - padding: 15px; - } - - .action-buttons { - display: flex; - align-self: center; - height: 100%; - margin-left: 10px; - - button { - margin-left: 10px; - } - } -} - .password-wrapper { overflow-wrap: break-word; white-space: pre-wrap; diff --git a/apps/desktop/src/services/biometric-message-handler.service.spec.ts b/apps/desktop/src/services/biometric-message-handler.service.spec.ts index a7f0f555ca2..7c16f766607 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.spec.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.spec.ts @@ -5,8 +5,8 @@ import { of } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/desktop/src/services/biometric-message-handler.service.ts b/apps/desktop/src/services/biometric-message-handler.service.ts index 0482434708e..ea31d068b4e 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.ts @@ -6,8 +6,8 @@ import { combineLatest, concatMap, firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; diff --git a/apps/desktop/src/services/duckduckgo-message-handler.service.ts b/apps/desktop/src/services/duckduckgo-message-handler.service.ts index fa5c2f4d9f7..e4474b60741 100644 --- a/apps/desktop/src/services/duckduckgo-message-handler.service.ts +++ b/apps/desktop/src/services/duckduckgo-message-handler.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { firstValueFrom } from "rxjs"; import { NativeMessagingVersion } from "@bitwarden/common/enums"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/desktop/src/vault/app/vault/attachments.component.ts b/apps/desktop/src/vault/app/vault/attachments.component.ts index 2b554ba2291..ea4f49b8431 100644 --- a/apps/desktop/src/vault/app/vault/attachments.component.ts +++ b/apps/desktop/src/vault/app/vault/attachments.component.ts @@ -4,7 +4,7 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/ang import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/desktop/src/vault/app/vault/collections.component.html b/apps/desktop/src/vault/app/vault/collections.component.html index 113ebe5ff97..13bd3178899 100644 --- a/apps/desktop/src/vault/app/vault/collections.component.html +++ b/apps/desktop/src/vault/app/vault/collections.component.html @@ -21,6 +21,7 @@ type="checkbox" [(ngModel)]="$any(c).checked" name="Collection[{{ i }}].Checked" + [disabled]="!cipher.canAssignToCollections" /> diff --git a/apps/desktop/src/vault/app/vault/vault.component.html b/apps/desktop/src/vault/app/vault/vault.component.html index 64bf8ac256f..1879573a843 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.html +++ b/apps/desktop/src/vault/app/vault/vault.component.html @@ -39,8 +39,8 @@ (onCancelled)="cancelledAddEdit($event)" (onShareCipher)="shareCipher($event)" (onEditCollections)="cipherCollections($event)" - (onGeneratePassword)="openGenerator(true, true)" - (onGenerateUsername)="openGenerator(true, false)" + (onGeneratePassword)="openGenerator(true)" + (onGenerateUsername)="openGenerator(false)" >
{ - if (this.addEditComponent != null) { - this.addEditComponent.markPasswordAsDirty(); - if (passwordType) { - this.addEditComponent.cipher.login.password = value ?? ""; - } else { - this.addEditComponent.cipher.login.username = value ?? ""; - } - } - }, - type: passwordType ? "password" : "username", - }); - return; - } - - // TODO: Legacy code below, remove once the new generator is fully implemented - // https://bitwarden.atlassian.net/browse/PM-7121 - const cipher = this.addEditComponent?.cipher; - const loginType = cipher != null && cipher.type === CipherType.Login && cipher.login != null; - - const [modal, childComponent] = await this.modalService.openViewRef( - GeneratorComponent, - this.generatorModalRef, - (comp) => { - comp.comingFromAddEdit = comingFromAddEdit; - if (comingFromAddEdit) { - comp.type = passwordType ? "password" : "username"; - if (loginType && cipher.login.hasUris && cipher.login.uris[0].hostname != null) { - comp.usernameWebsite = cipher.login.uris[0].hostname; + async openGenerator(passwordType = true) { + CredentialGeneratorDialogComponent.open(this.dialogService, { + onCredentialGenerated: (value?: string) => { + if (this.addEditComponent != null) { + this.addEditComponent.markPasswordAsDirty(); + if (passwordType) { + this.addEditComponent.cipher.login.password = value ?? ""; + } else { + this.addEditComponent.cipher.login.username = value ?? ""; } } }, - ); - this.modal = modal; - - // eslint-disable-next-line rxjs-angular/prefer-takeuntil - childComponent.onSelected.subscribe((value: string) => { - this.modal.close(); - if (loginType) { - this.addEditComponent.markPasswordAsDirty(); - if (passwordType) { - this.addEditComponent.cipher.login.password = value; - } else { - this.addEditComponent.cipher.login.username = value; - } - } - }); - - // eslint-disable-next-line rxjs-angular/prefer-takeuntil - this.modal.onClosed.subscribe(() => { - this.modal = null; + type: passwordType ? "password" : "username", }); + return; } async addFolder() { diff --git a/apps/desktop/src/vault/app/vault/view.component.ts b/apps/desktop/src/vault/app/vault/view.component.ts index ce9d3af8276..136a537dd37 100644 --- a/apps/desktop/src/vault/app/vault/view.component.ts +++ b/apps/desktop/src/vault/app/vault/view.component.ts @@ -17,8 +17,8 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index 94194a0de7d..1bd97b0130f 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -22,7 +22,7 @@ "@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"], "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/importer/ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], @@ -48,5 +48,5 @@ "strictTemplates": true, "preserveWhitespaces": true }, - "include": ["src", "../../libs/common/src/platform/services/**/*.worker.ts"] + "include": ["src", "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts"] } diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index 8387c53e5e3..ae0972a6828 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -121,6 +121,22 @@ + + {{ "accountDeprovisioningNotification" | i18n }} + + {{ "learnMore" | i18n }} + + ; enterpriseOrganization$: Observable; + showAccountDeprovisioningBanner$: Observable; + constructor( private route: ActivatedRoute, private organizationService: OrganizationService, @@ -68,19 +73,36 @@ export class OrganizationLayoutComponent implements OnInit { private configService: ConfigService, private policyService: PolicyService, private providerService: ProviderService, + protected bannerService: AccountDeprovisioningBannerService, private accountService: AccountService, ) {} async ngOnInit() { document.body.classList.remove("layout_frontend"); - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); this.organization$ = this.route.params.pipe( map((p) => p.organizationId), - switchMap((id) => this.organizationService.organizations$(userId).pipe(getById(id))), + withLatestFrom(this.accountService.activeAccount$.pipe(getUserId)), + switchMap(([orgId, userId]) => + this.organizationService.organizations$(userId).pipe(getById(orgId)), + ), filter((org) => org != null), ); + this.showAccountDeprovisioningBanner$ = combineLatest([ + this.bannerService.showBanner$, + this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioningBanner), + this.organization$, + ]).pipe( + map( + ([dismissedOrgs, featureFlagEnabled, organization]) => + organization.productTierType === ProductTierType.Enterprise && + organization.isAdmin && + !dismissedOrgs?.includes(organization.id) && + featureFlagEnabled, + ), + ); + this.canAccessExport$ = this.organization$.pipe(map((org) => org.canAccessExport)); this.showPaymentAndHistory$ = this.organization$.pipe( diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts new file mode 100644 index 00000000000..414828969df --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts @@ -0,0 +1,75 @@ +import { firstValueFrom } from "rxjs"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + FakeAccountService, + FakeStateProvider, + mockAccountServiceWith, +} from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { AccountDeprovisioningBannerService } from "./account-deprovisioning-banner.service"; + +describe("Account Deprovisioning Banner Service", () => { + const userId = Utils.newGuid() as UserId; + let accountService: FakeAccountService; + let stateProvider: FakeStateProvider; + let bannerService: AccountDeprovisioningBannerService; + + beforeEach(async () => { + accountService = mockAccountServiceWith(userId); + stateProvider = new FakeStateProvider(accountService); + bannerService = new AccountDeprovisioningBannerService(stateProvider); + }); + + it("updates state with single org", async () => { + const fakeOrg = new Organization(); + fakeOrg.id = "123"; + + await bannerService.hideBanner(fakeOrg); + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toEqual([fakeOrg.id]); + }); + + it("updates state with multiple orgs", async () => { + const fakeOrg1 = new Organization(); + fakeOrg1.id = "123"; + const fakeOrg2 = new Organization(); + fakeOrg2.id = "234"; + const fakeOrg3 = new Organization(); + fakeOrg3.id = "987"; + + await bannerService.hideBanner(fakeOrg1); + await bannerService.hideBanner(fakeOrg2); + await bannerService.hideBanner(fakeOrg3); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toContain(fakeOrg1.id); + expect(state).toContain(fakeOrg2.id); + expect(state).toContain(fakeOrg3.id); + }); + + it("does not add the same org id multiple times", async () => { + const fakeOrg = new Organization(); + fakeOrg.id = "123"; + + await bannerService.hideBanner(fakeOrg); + await bannerService.hideBanner(fakeOrg); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toEqual([fakeOrg.id]); + }); + + it("does not add null to the state", async () => { + await bannerService.hideBanner(null as unknown as Organization); + await bannerService.hideBanner(undefined as unknown as Organization); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toBeNull(); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts new file mode 100644 index 00000000000..86a6b7df3e2 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from "@angular/core"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { + ACCOUNT_DEPROVISIONING_BANNER_DISK, + StateProvider, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; + +export const SHOW_BANNER_KEY = new UserKeyDefinition( + ACCOUNT_DEPROVISIONING_BANNER_DISK, + "accountDeprovisioningBanner", + { + deserializer: (b) => b, + clearOn: [], + }, +); + +@Injectable({ providedIn: "root" }) +export class AccountDeprovisioningBannerService { + private _showBanner = this.stateProvider.getActive(SHOW_BANNER_KEY); + + showBanner$ = this._showBanner.state$; + + constructor(private stateProvider: StateProvider) {} + + async hideBanner(organization: Organization) { + await this._showBanner.update((state) => { + if (!organization) { + return state; + } + if (!state) { + return [organization.id]; + } else if (!state.includes(organization.id)) { + return [...state, organization.id]; + } + return state; + }); + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component.ts index 2396292e9af..05e302f011d 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component.ts @@ -8,8 +8,8 @@ import { } from "@bitwarden/admin-console/common"; import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response"; import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts index af827fa65fd..d3a8b8a2e71 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts @@ -14,8 +14,8 @@ import { import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response"; import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { StateProvider } from "@bitwarden/common/platform/state"; diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.ts index 704a94b0dd3..9d7752cde84 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.ts @@ -2,12 +2,17 @@ // @ts-strict-ignore import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DialogService } from "@bitwarden/components"; +import { DeleteManagedMemberWarningService } from "../../services/delete-managed-member/delete-managed-member-warning.service"; + import { BulkUserDetails } from "./bulk-status.component"; type BulkDeleteDialogParams = { @@ -31,12 +36,20 @@ export class BulkDeleteDialogComponent { @Inject(DIALOG_DATA) protected dialogParams: BulkDeleteDialogParams, protected i18nService: I18nService, private organizationUserApiService: OrganizationUserApiService, + private configService: ConfigService, + private deleteManagedMemberWarningService: DeleteManagedMemberWarningService, ) { this.organizationId = dialogParams.organizationId; this.users = dialogParams.users; } async submit() { + if ( + await firstValueFrom(this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioning)) + ) { + await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organizationId); + } + try { this.loading = true; this.error = null; diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 7a30eba9e12..34926e1d379 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -53,6 +53,7 @@ import { convertToSelectionView, PermissionMode, } from "../../../shared/components/access-selector"; +import { DeleteManagedMemberWarningService } from "../../services/delete-managed-member/delete-managed-member-warning.service"; import { commaSeparatedEmails } from "./validators/comma-separated-emails.validator"; import { inputEmailLimitValidator } from "./validators/input-email-limit.validator"; @@ -176,6 +177,7 @@ export class MemberDialogComponent implements OnDestroy { organizationService: OrganizationService, private toastService: ToastService, private configService: ConfigService, + private deleteManagedMemberWarningService: DeleteManagedMemberWarningService, ) { this.organization$ = accountService.activeAccount$.pipe( switchMap((account) => @@ -639,6 +641,27 @@ export class MemberDialogComponent implements OnDestroy { return; } + const showWarningDialog = combineLatest([ + this.organization$, + this.deleteManagedMemberWarningService.warningAcknowledged(this.params.organizationId), + this.accountDeprovisioningEnabled$, + ]).pipe( + map( + ([organization, acknowledged, featureFlagEnabled]) => + featureFlagEnabled && + organization.isOwner && + organization.productTierType === ProductTierType.Enterprise && + !acknowledged, + ), + ); + + if (await firstValueFrom(showWarningDialog)) { + const acknowledged = await this.deleteManagedMemberWarningService.showWarning(); + if (!acknowledged) { + return; + } + } + const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "deleteOrganizationUser", @@ -667,6 +690,10 @@ export class MemberDialogComponent implements OnDestroy { title: null, message: this.i18nService.t("organizationUserDeleted", this.params.name), }); + + if (await firstValueFrom(this.accountDeprovisioningEnabled$)) { + await this.deleteManagedMemberWarningService.acknowledgeWarning(this.params.organizationId); + } this.close(MemberDialogResult.Deleted); }; diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 1ea77642229..9fc27f2f199 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -46,8 +46,8 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; @@ -81,6 +81,7 @@ import { ResetPasswordComponent, ResetPasswordDialogResult, } from "./components/reset-password.component"; +import { DeleteManagedMemberWarningService } from "./services/delete-managed-member/delete-managed-member-warning.service"; class MembersTableDataSource extends PeopleTableDataSource { protected statusType = OrganizationUserStatusType; @@ -138,6 +139,7 @@ export class MembersComponent extends BaseMembersComponent private collectionService: CollectionService, private billingApiService: BillingApiServiceAbstraction, private configService: ConfigService, + protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService, ) { super( apiService, @@ -585,6 +587,23 @@ export class MembersComponent extends BaseMembersComponent } async bulkDelete() { + if (this.accountDeprovisioningEnabled) { + const warningAcknowledged = await firstValueFrom( + this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id), + ); + + if ( + !warningAcknowledged && + this.organization.isOwner && + this.organization.productTierType === ProductTierType.Enterprise + ) { + const acknowledged = await this.deleteManagedMemberWarningService.showWarning(); + if (!acknowledged) { + return; + } + } + } + if (this.actionPromise != null) { return; } @@ -774,6 +793,23 @@ export class MembersComponent extends BaseMembersComponent } async deleteUser(user: OrganizationUserView) { + if (this.accountDeprovisioningEnabled) { + const warningAcknowledged = await firstValueFrom( + this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id), + ); + + if ( + !warningAcknowledged && + this.organization.isOwner && + this.organization.productTierType === ProductTierType.Enterprise + ) { + const acknowledged = await this.deleteManagedMemberWarningService.showWarning(); + if (!acknowledged) { + return false; + } + } + } + const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "deleteOrganizationUser", @@ -792,6 +828,10 @@ export class MembersComponent extends BaseMembersComponent return false; } + if (this.accountDeprovisioningEnabled) { + await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organization.id); + } + this.actionPromise = this.organizationUserApiService.deleteOrganizationUser( this.organization.id, user.id, diff --git a/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.spec.ts new file mode 100644 index 00000000000..45271f6f78b --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.spec.ts @@ -0,0 +1,51 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { firstValueFrom } from "rxjs"; + +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + FakeAccountService, + FakeStateProvider, + mockAccountServiceWith, +} from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; + +import { DeleteManagedMemberWarningService } from "./delete-managed-member-warning.service"; + +describe("Delete managed member warning service", () => { + const userId = Utils.newGuid() as UserId; + let accountService: FakeAccountService; + let stateProvider: FakeStateProvider; + let dialogService: MockProxy; + let warningService: DeleteManagedMemberWarningService; + + beforeEach(() => { + accountService = mockAccountServiceWith(userId); + stateProvider = new FakeStateProvider(accountService); + dialogService = mock(); + warningService = new DeleteManagedMemberWarningService(stateProvider, dialogService); + }); + + it("warningAcknowledged returns false for ids that have not acknowledged the warning", async () => { + const id = Utils.newGuid(); + const acknowledged = await firstValueFrom(warningService.warningAcknowledged(id)); + + expect(acknowledged).toEqual(false); + }); + + it("warningAcknowledged returns true for ids that have acknowledged the warning", async () => { + const id1 = Utils.newGuid(); + const id2 = Utils.newGuid(); + const id3 = Utils.newGuid(); + await warningService.acknowledgeWarning(id1); + await warningService.acknowledgeWarning(id3); + + const acknowledged1 = await firstValueFrom(warningService.warningAcknowledged(id1)); + const acknowledged2 = await firstValueFrom(warningService.warningAcknowledged(id2)); + const acknowledged3 = await firstValueFrom(warningService.warningAcknowledged(id3)); + + expect(acknowledged1).toEqual(true); + expect(acknowledged2).toEqual(false); + expect(acknowledged3).toEqual(true); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.ts b/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.ts new file mode 100644 index 00000000000..53ca0bbcaf8 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/delete-managed-member/delete-managed-member-warning.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from "@angular/core"; +import { map } from "rxjs"; + +import { + DELETE_MANAGED_USER_WARNING, + StateProvider, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; +import { DialogService } from "@bitwarden/components"; + +export const SHOW_WARNING_KEY = new UserKeyDefinition( + DELETE_MANAGED_USER_WARNING, + "showDeleteManagedUserWarning", + { + deserializer: (b) => b, + clearOn: [], + }, +); + +@Injectable({ providedIn: "root" }) +export class DeleteManagedMemberWarningService { + private _acknowledged = this.stateProvider.getActive(SHOW_WARNING_KEY); + private acknowledgedState$ = this._acknowledged.state$; + + constructor( + private stateProvider: StateProvider, + private dialogService: DialogService, + ) {} + + async acknowledgeWarning(organizationId: string) { + await this._acknowledged.update((state) => { + if (!organizationId) { + return state; + } + if (!state) { + return [organizationId]; + } else if (!state.includes(organizationId)) { + return [...state, organizationId]; + } + return state; + }); + } + + async showWarning() { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { + key: "deleteManagedUserWarning", + }, + content: { + key: "deleteManagedUserWarningDesc", + }, + type: "danger", + icon: "bwi-exclamation-circle", + acceptButtonText: { key: "continue" }, + cancelButtonText: { key: "cancel" }, + }); + + if (!confirmed) { + return false; + } + + return confirmed; + } + + warningAcknowledged(organizationId: string) { + return this.acknowledgedState$.pipe( + map((acknowledgedIds) => acknowledgedIds?.includes(organizationId) ?? false), + ); + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.spec.ts index 9b595859b21..eff417ead32 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.spec.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.spec.ts @@ -11,7 +11,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { OrganizationKeysResponse } from "@bitwarden/common/admin-console/models/response/organization-keys.response"; import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncryptionType } from "@bitwarden/common/platform/enums"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index 0fe9b75aa98..8e583d3106c 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -10,7 +10,7 @@ import { } from "@bitwarden/admin-console/common"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts b/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts index 5ea7e2ab52d..78784bdd3d2 100644 --- a/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts @@ -11,8 +11,8 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { ImportCollectionServiceAbstraction } from "@bitwarden/importer/core"; import { ImportComponent } from "@bitwarden/importer/ui"; +import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core"; import { LooseComponentsModule, SharedModule } from "../../../shared"; import { ImportCollectionAdminService } from "../../../tools/import/import-collection-admin.service"; diff --git a/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts b/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts index e032f198291..1241ea88fe9 100644 --- a/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts +++ b/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts @@ -1,7 +1,7 @@ import { TestBed } from "@angular/core/testing"; import { mock, MockProxy } from "jest-mock-extended"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts index 044f140c53c..dca37f93c36 100644 --- a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts +++ b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from "@angular/core"; import { RotateableKeySet } from "@bitwarden/auth/common"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts index 1c7d870175d..dfeb53f0c19 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts @@ -4,11 +4,11 @@ import { MockProxy } from "jest-mock-extended"; import mock from "jest-mock-extended/lib/Mock"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { UserKeyResponse } from "@bitwarden/common/models/response/user-key.response"; -import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { EncryptionType } from "@bitwarden/common/platform/enums"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts index 62a59da2995..7ac2f21a223 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts @@ -6,9 +6,9 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; +import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.service.spec.ts b/apps/web/src/app/auth/organization-invite/accept-organization.service.spec.ts index b3709a15882..f613bdc7ddc 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.service.spec.ts +++ b/apps/web/src/app/auth/organization-invite/accept-organization.service.spec.ts @@ -12,7 +12,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { ResetPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/reset-password-policy-options"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { FakeGlobalState } from "@bitwarden/common/spec/fake-state"; diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.service.ts b/apps/web/src/app/auth/organization-invite/accept-organization.service.ts index a964d676159..22a79ef3696 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.service.ts +++ b/apps/web/src/app/auth/organization-invite/accept-organization.service.ts @@ -16,7 +16,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/web/src/app/auth/register-form/register-form.component.html b/apps/web/src/app/auth/register-form/register-form.component.html deleted file mode 100644 index 19a7a95b298..00000000000 --- a/apps/web/src/app/auth/register-form/register-form.component.html +++ /dev/null @@ -1,158 +0,0 @@ - - -
-
-
- - {{ "emailAddress" | i18n }} - - {{ "emailAddressDesc" | i18n }} - -
- -
- - {{ "name" | i18n }} - - {{ "yourNameDesc" | i18n }} - -
- -
- - - - {{ "masterPass" | i18n }} - - - - {{ "important" | i18n }} - {{ "masterPassImportant" | i18n }} {{ characterMinimumMessage }} - - - - -
- -
- - {{ "reTypeMasterPass" | i18n }} - - - -
- -
- - {{ "masterPassHintLabel" | i18n }} - - {{ "masterPassHintDesc" | i18n }} - -
- -
- -
-
- - {{ "checkForBreaches" | i18n }} -
-
- - - - {{ "acceptPolicies" | i18n }}
- {{ - "termsOfService" | i18n - }}, - {{ - "privacyPolicy" | i18n - }} -
-
- -
- - - - - - -
-

- {{ "alreadyHaveAccount" | i18n }} - {{ "logIn" | i18n }} -

- -
-
diff --git a/apps/web/src/app/auth/register-form/register-form.component.ts b/apps/web/src/app/auth/register-form/register-form.component.ts deleted file mode 100644 index e8c9f0291a5..00000000000 --- a/apps/web/src/app/auth/register-form/register-form.component.ts +++ /dev/null @@ -1,115 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, Input, OnInit } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; -import { Router } from "@angular/router"; - -import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/auth/components/register.component"; -import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request"; -import { RegisterRequest } from "@bitwarden/common/models/request/register.request"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; - -import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service"; - -@Component({ - selector: "app-register-form", - templateUrl: "./register-form.component.html", -}) -export class RegisterFormComponent extends BaseRegisterComponent implements OnInit { - @Input() queryParamEmail: string; - @Input() queryParamFromOrgInvite: boolean; - @Input() enforcedPolicyOptions: MasterPasswordPolicyOptions; - @Input() referenceDataValue: ReferenceEventRequest; - - showErrorSummary = false; - characterMinimumMessage: string; - - constructor( - formValidationErrorService: FormValidationErrorsService, - formBuilder: UntypedFormBuilder, - loginStrategyService: LoginStrategyServiceAbstraction, - router: Router, - i18nService: I18nService, - keyService: KeyService, - apiService: ApiService, - platformUtilsService: PlatformUtilsService, - private policyService: PolicyService, - environmentService: EnvironmentService, - logService: LogService, - auditService: AuditService, - dialogService: DialogService, - acceptOrgInviteService: AcceptOrganizationInviteService, - toastService: ToastService, - ) { - super( - formValidationErrorService, - formBuilder, - loginStrategyService, - router, - i18nService, - keyService, - apiService, - platformUtilsService, - environmentService, - logService, - auditService, - dialogService, - toastService, - ); - this.modifyRegisterRequest = async (request: RegisterRequest) => { - // Org invites are deep linked. Non-existent accounts are redirected to the register page. - // Org user id and token are included here only for validation and two factor purposes. - const orgInvite = await acceptOrgInviteService.getOrganizationInvite(); - if (orgInvite != null) { - request.organizationUserId = orgInvite.organizationUserId; - request.token = orgInvite.token; - } - // Invite is accepted after login (on deep link redirect). - }; - } - - async ngOnInit() { - await super.ngOnInit(); - this.referenceData = this.referenceDataValue; - if (this.queryParamEmail) { - this.formGroup.get("email")?.setValue(this.queryParamEmail); - } - - if (this.enforcedPolicyOptions != null && this.enforcedPolicyOptions.minLength > 0) { - this.characterMinimumMessage = ""; - } else { - this.characterMinimumMessage = this.i18nService.t("characterMinimum", this.minimumLength); - } - } - - async submit() { - if ( - this.enforcedPolicyOptions != null && - !this.policyService.evaluateMasterPassword( - this.passwordStrengthResult.score, - this.formGroup.value.masterPassword, - this.enforcedPolicyOptions, - ) - ) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"), - }); - return; - } - - await super.submit(false); - } -} diff --git a/apps/web/src/app/auth/register-form/register-form.module.ts b/apps/web/src/app/auth/register-form/register-form.module.ts deleted file mode 100644 index b63cb18506d..00000000000 --- a/apps/web/src/app/auth/register-form/register-form.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from "@angular/core"; - -import { PasswordCalloutComponent } from "@bitwarden/auth/angular"; - -import { SharedModule } from "../../shared"; - -import { RegisterFormComponent } from "./register-form.component"; - -@NgModule({ - imports: [SharedModule, PasswordCalloutComponent], - declarations: [RegisterFormComponent], - exports: [RegisterFormComponent], -}) -export class RegisterFormModule {} diff --git a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts index 75d43bb3bc7..73191e1539e 100644 --- a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts @@ -4,7 +4,7 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/ang import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index b5471a90fd5..ca1b9245c0b 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -118,7 +118,13 @@ ) | currency: "$" }} - /{{ "monthPerMember" | i18n }} + + /{{ + selectableProduct.productTier === productTypes.Families + ? "month" + : ("monthPerMember" | i18n) + }} {{ paymentDesc }}

- + + { - return this.isFreeFamilyFlagEnabled$.pipe( - switchMap((isFreeFamilyFlagEnabled) => - isFreeFamilyFlagEnabled ? this.getFreeFamiliesVisibility$() : this.canManageSponsorships$, - ), - ); + return this.getFreeFamiliesVisibility$(); } private getFreeFamiliesVisibility$(): Observable { @@ -143,8 +138,4 @@ export class FreeFamiliesPolicyService { const enterpriseOrganizations = organizations.filter((org) => org.canManageSponsorships); return enterpriseOrganizations.length === 1 ? enterpriseOrganizations[0].id : null; } - - private get isFreeFamilyFlagEnabled$(): Observable { - return from(this.configService.getFeatureFlag(FeatureFlag.DisableFreeFamiliesSponsorship)); - } } diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.ts b/apps/web/src/app/billing/settings/sponsored-families.component.ts index d4a93ba7cd8..c35fd3a2e61 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.ts +++ b/apps/web/src/app/billing/settings/sponsored-families.component.ts @@ -21,8 +21,6 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { PlanSponsorshipType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -41,7 +39,6 @@ interface RequestSponsorshipForm { }) export class SponsoredFamiliesComponent implements OnInit, OnDestroy { loading = false; - isFreeFamilyFlagEnabled: boolean; availableSponsorshipOrgs$: Observable; activeSponsorshipOrgs$: Observable; @@ -64,7 +61,6 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { private formBuilder: FormBuilder, private accountService: AccountService, private toastService: ToastService, - private configService: ConfigService, private policyService: PolicyService, private freeFamiliesPolicyService: FreeFamiliesPolicyService, private router: Router, @@ -87,37 +83,27 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { } async ngOnInit() { - this.isFreeFamilyFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.DisableFreeFamiliesSponsorship, - ); - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - if (this.isFreeFamilyFlagEnabled) { - await this.preventAccessToFreeFamiliesPage(); + await this.preventAccessToFreeFamiliesPage(); - this.availableSponsorshipOrgs$ = combineLatest([ - this.organizationService.organizations$(userId), - this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId), - ]).pipe( - map(([organizations, policies]) => - organizations - .filter((org) => org.familySponsorshipAvailable) - .map((org) => ({ - organization: org, - isPolicyEnabled: policies.some( - (policy) => policy.organizationId === org.id && policy.enabled, - ), - })) - .filter(({ isPolicyEnabled }) => !isPolicyEnabled) - .map(({ organization }) => organization), - ), - ); - } else { - this.availableSponsorshipOrgs$ = this.organizationService - .organizations$(userId) - .pipe(map((orgs) => orgs.filter((o) => o.familySponsorshipAvailable))); - } + this.availableSponsorshipOrgs$ = combineLatest([ + this.organizationService.organizations$(userId), + this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId), + ]).pipe( + map(([organizations, policies]) => + organizations + .filter((org) => org.familySponsorshipAvailable) + .map((org) => ({ + organization: org, + isPolicyEnabled: policies.some( + (policy) => policy.organizationId === org.id && policy.enabled, + ), + })) + .filter(({ isPolicyEnabled }) => !isPolicyEnabled) + .map(({ organization }) => organization), + ), + ); this.availableSponsorshipOrgs$.pipe(takeUntil(this._destroy)).subscribe((orgs) => { if (orgs.length === 1) { diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts index 6e9e00b0ee1..e613b862922 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts @@ -10,7 +10,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -29,7 +28,6 @@ export class SponsoringOrgRowComponent implements OnInit { statusMessage = "loading"; statusClass: "tw-text-success" | "tw-text-danger" = "tw-text-success"; isFreeFamilyPolicyEnabled$: Observable; - isFreeFamilyFlagEnabled: boolean; private locale = ""; constructor( @@ -52,25 +50,20 @@ export class SponsoringOrgRowComponent implements OnInit { this.sponsoringOrg.familySponsorshipValidUntil, this.sponsoringOrg.familySponsorshipLastSyncDate, ); - this.isFreeFamilyFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.DisableFreeFamiliesSponsorship, - ); - if (this.isFreeFamilyFlagEnabled) { - this.isFreeFamilyPolicyEnabled$ = this.accountService.activeAccount$.pipe( - getUserId, - switchMap((userId) => - this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId), - ), - map( - (policies) => - Array.isArray(policies) && - policies.some( - (policy) => policy.organizationId === this.sponsoringOrg.id && policy.enabled, - ), - ), - ); - } + this.isFreeFamilyPolicyEnabled$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => + this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId), + ), + map( + (policies) => + Array.isArray(policies) && + policies.some( + (policy) => policy.organizationId === this.sponsoringOrg.id && policy.enabled, + ), + ), + ); } async revokeSponsorship() { diff --git a/apps/web/src/app/billing/shared/self-hosting-license-uploader/organization-self-hosting-license-uploader.component.ts b/apps/web/src/app/billing/shared/self-hosting-license-uploader/organization-self-hosting-license-uploader.component.ts index 41cc977d46f..c8d5eac2099 100644 --- a/apps/web/src/app/billing/shared/self-hosting-license-uploader/organization-self-hosting-license-uploader.component.ts +++ b/apps/web/src/app/billing/shared/self-hosting-license-uploader/organization-self-hosting-license-uploader.component.ts @@ -7,7 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/platform/sync"; diff --git a/apps/web/src/app/billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html b/apps/web/src/app/billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html index 0b6e44d4eb6..dddac598a46 100644 --- a/apps/web/src/app/billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html +++ b/apps/web/src/app/billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html @@ -1,17 +1,4 @@ - - - - - - - - - - -
-

{{ "createAccount" | i18n }}

-
- -
-
-
-
-
-
- Bitwarden - -
- - - - - - - - - - - - - - -
-
-
-
-
- -
-
-
-
-
-

- {{ freeTrialText }} -

- -
- - - - - - - - - - - - - - -
- - -
-
-
-
-
-
-
-
-
diff --git a/apps/web/src/app/billing/trial-initiation/trial-initiation.component.spec.ts b/apps/web/src/app/billing/trial-initiation/trial-initiation.component.spec.ts deleted file mode 100644 index c8d4d35fb7d..00000000000 --- a/apps/web/src/app/billing/trial-initiation/trial-initiation.component.spec.ts +++ /dev/null @@ -1,336 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { StepperSelectionEvent } from "@angular/cdk/stepper"; -import { TitleCasePipe } from "@angular/common"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing"; -import { FormBuilder, UntypedFormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { RouterTestingModule } from "@angular/router/testing"; -import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject, of } from "rxjs"; - -import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service"; -import { PlanType } from "@bitwarden/common/billing/enums"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; - -import { AcceptOrganizationInviteService } from "../../auth/organization-invite/accept-organization.service"; -import { OrganizationInvite } from "../../auth/organization-invite/organization-invite"; -import { RouterService } from "../../core"; -import { SharedModule } from "../../shared"; - -import { TrialInitiationComponent } from "./trial-initiation.component"; -import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component"; - -describe("TrialInitiationComponent", () => { - let component: TrialInitiationComponent; - let fixture: ComponentFixture; - const mockQueryParams = new BehaviorSubject({ org: "enterprise" }); - const testOrgId = "91329456-5b9f-44b3-9279-6bb9ee6a0974"; - const formBuilder: FormBuilder = new FormBuilder(); - let routerSpy: jest.SpyInstance; - - let stateServiceMock: MockProxy; - let policyApiServiceMock: MockProxy; - let policyServiceMock: MockProxy; - let routerServiceMock: MockProxy; - let acceptOrgInviteServiceMock: MockProxy; - let organizationBillingServiceMock: MockProxy; - let configServiceMock: MockProxy; - - beforeEach(() => { - // only define services directly that we want to mock return values in this component - stateServiceMock = mock(); - policyApiServiceMock = mock(); - policyServiceMock = mock(); - routerServiceMock = mock(); - acceptOrgInviteServiceMock = mock(); - organizationBillingServiceMock = mock(); - configServiceMock = mock(); - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.configureTestingModule({ - imports: [ - SharedModule, - RouterTestingModule.withRoutes([ - { path: "trial", component: TrialInitiationComponent }, - { - path: `organizations/${testOrgId}/vault`, - component: BlankComponent, - }, - { - path: `organizations/${testOrgId}/members`, - component: BlankComponent, - }, - ]), - ], - declarations: [TrialInitiationComponent, I18nPipe], - providers: [ - UntypedFormBuilder, - { - provide: ActivatedRoute, - useValue: { - queryParams: mockQueryParams.asObservable(), - }, - }, - { provide: StateService, useValue: stateServiceMock }, - { provide: PolicyService, useValue: policyServiceMock }, - { provide: PolicyApiServiceAbstraction, useValue: policyApiServiceMock }, - { provide: LogService, useValue: mock() }, - { provide: I18nService, useValue: mock() }, - { provide: TitleCasePipe, useValue: mock() }, - { - provide: VerticalStepperComponent, - useClass: VerticalStepperStubComponent, - }, - { - provide: RouterService, - useValue: routerServiceMock, - }, - { - provide: AcceptOrganizationInviteService, - useValue: acceptOrgInviteServiceMock, - }, - { - provide: OrganizationBillingService, - useValue: organizationBillingServiceMock, - }, - { - provide: ConfigService, - useValue: configServiceMock, - }, - ], - schemas: [NO_ERRORS_SCHEMA], // Allows child components to be ignored (such as register component) - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); - - // These tests demonstrate mocking service calls - describe("onInit() enforcedPolicyOptions", () => { - it("should not set enforcedPolicyOptions if there isn't an org invite in deep linked url", async () => { - acceptOrgInviteServiceMock.getOrganizationInvite.mockResolvedValueOnce(null); - // Need to recreate component with new service mock - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - await component.ngOnInit(); - - expect(component.enforcedPolicyOptions).toBe(undefined); - }); - it("should set enforcedPolicyOptions if the deep linked url has an org invite", async () => { - // Set up service method mocks - acceptOrgInviteServiceMock.getOrganizationInvite.mockResolvedValueOnce({ - organizationId: testOrgId, - token: "token", - email: "testEmail", - organizationUserId: "123", - } as OrganizationInvite); - policyApiServiceMock.getPoliciesByToken.mockReturnValueOnce( - Promise.resolve([ - { - id: "345", - organizationId: testOrgId, - type: 1, - data: { - minComplexity: 4, - minLength: 10, - requireLower: null, - requireNumbers: null, - requireSpecial: null, - requireUpper: null, - }, - enabled: true, - }, - ] as Policy[]), - ); - policyServiceMock.masterPasswordPolicyOptions$.mockReturnValue( - of({ - minComplexity: 4, - minLength: 10, - requireLower: null, - requireNumbers: null, - requireSpecial: null, - requireUpper: null, - } as MasterPasswordPolicyOptions), - ); - - // Need to recreate component with new service mocks - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - await component.ngOnInit(); - expect(component.enforcedPolicyOptions).toMatchObject({ - minComplexity: 4, - minLength: 10, - requireLower: null, - requireNumbers: null, - requireSpecial: null, - requireUpper: null, - }); - }); - }); - - // These tests demonstrate route params - describe("Route params", () => { - it("should set org variable to be enterprise and plan to EnterpriseAnnually if org param is enterprise", fakeAsync(() => { - mockQueryParams.next({ org: "enterprise" }); - tick(); // wait for resolution - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - expect(component.org).toBe("enterprise"); - expect(component.plan).toBe(PlanType.EnterpriseAnnually); - })); - it("should not set org variable if no org param is provided", fakeAsync(() => { - mockQueryParams.next({}); - tick(); // wait for resolution - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - expect(component.org).toBe(""); - expect(component.accountCreateOnly).toBe(true); - })); - it("should not set the org if org param is invalid ", fakeAsync(async () => { - mockQueryParams.next({ org: "hahahaha" }); - tick(); // wait for resolution - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - expect(component.org).toBe(""); - expect(component.accountCreateOnly).toBe(true); - })); - it("should set the layout variable if layout param is valid ", fakeAsync(async () => { - mockQueryParams.next({ layout: "teams1" }); - tick(); // wait for resolution - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - expect(component.layout).toBe("teams1"); - expect(component.accountCreateOnly).toBe(false); - })); - it("should not set the layout variable and leave as 'default' if layout param is invalid ", fakeAsync(async () => { - mockQueryParams.next({ layout: "asdfasdf" }); - tick(); // wait for resolution - fixture = TestBed.createComponent(TrialInitiationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component.ngOnInit(); - expect(component.layout).toBe("default"); - expect(component.accountCreateOnly).toBe(true); - })); - }); - - // These tests demonstrate the use of a stub component - describe("createAccount()", () => { - beforeEach(() => { - component.verticalStepper = TestBed.createComponent(VerticalStepperStubComponent) - .componentInstance as VerticalStepperComponent; - }); - - it("should set email and call verticalStepper.next()", fakeAsync(() => { - const verticalStepperNext = jest.spyOn(component.verticalStepper, "next"); - component.createdAccount("test@email.com"); - expect(verticalStepperNext).toHaveBeenCalled(); - expect(component.email).toBe("test@email.com"); - })); - }); - - describe("billingSuccess()", () => { - beforeEach(() => { - component.verticalStepper = TestBed.createComponent(VerticalStepperStubComponent) - .componentInstance as VerticalStepperComponent; - }); - - it("should set orgId and call verticalStepper.next()", () => { - const verticalStepperNext = jest.spyOn(component.verticalStepper, "next"); - component.billingSuccess({ orgId: testOrgId }); - expect(verticalStepperNext).toHaveBeenCalled(); - expect(component.orgId).toBe(testOrgId); - }); - }); - - describe("stepSelectionChange()", () => { - beforeEach(() => { - component.verticalStepper = TestBed.createComponent(VerticalStepperStubComponent) - .componentInstance as VerticalStepperComponent; - }); - - it("on step 2 should show organization copy text", () => { - component.stepSelectionChange({ - selectedIndex: 1, - previouslySelectedIndex: 0, - } as StepperSelectionEvent); - - expect(component.orgInfoSubLabel).toContain("Enter your"); - expect(component.orgInfoSubLabel).toContain(" organization information"); - }); - it("going from step 2 to 3 should set the orgInforSubLabel to be the Org name from orgInfoFormGroup", () => { - component.orgInfoFormGroup = formBuilder.group({ - name: ["Hooli"], - email: [""], - }); - component.stepSelectionChange({ - selectedIndex: 2, - previouslySelectedIndex: 1, - } as StepperSelectionEvent); - - expect(component.orgInfoSubLabel).toContain("Hooli"); - }); - }); - - describe("previousStep()", () => { - beforeEach(() => { - component.verticalStepper = TestBed.createComponent(VerticalStepperStubComponent) - .componentInstance as VerticalStepperComponent; - }); - - it("should call verticalStepper.previous()", fakeAsync(() => { - const verticalStepperPrevious = jest.spyOn(component.verticalStepper, "previous"); - component.previousStep(); - expect(verticalStepperPrevious).toHaveBeenCalled(); - })); - }); - - // These tests demonstrate router navigation - describe("navigation methods", () => { - beforeEach(() => { - component.orgId = testOrgId; - const router = TestBed.inject(Router); - fixture.detectChanges(); - routerSpy = jest.spyOn(router, "navigate"); - }); - describe("navigateToOrgVault", () => { - it("should call verticalStepper.previous()", fakeAsync(() => { - component.navigateToOrgVault(); - expect(routerSpy).toHaveBeenCalledWith(["organizations", testOrgId, "vault"]); - })); - }); - describe("navigateToOrgVault", () => { - it("should call verticalStepper.previous()", fakeAsync(() => { - component.navigateToOrgInvite(); - expect(routerSpy).toHaveBeenCalledWith(["organizations", testOrgId, "members"]); - })); - }); - }); -}); - -export class VerticalStepperStubComponent extends VerticalStepperComponent {} -export class BlankComponent {} // For router tests diff --git a/apps/web/src/app/billing/trial-initiation/trial-initiation.component.ts b/apps/web/src/app/billing/trial-initiation/trial-initiation.component.ts deleted file mode 100644 index 2403c28d267..00000000000 --- a/apps/web/src/app/billing/trial-initiation/trial-initiation.component.ts +++ /dev/null @@ -1,353 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { StepperSelectionEvent } from "@angular/cdk/stepper"; -import { TitleCasePipe } from "@angular/common"; -import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; -import { UntypedFormBuilder, Validators } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; - -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { - OrganizationInformation, - PlanInformation, - OrganizationBillingServiceAbstraction as OrganizationBillingService, -} from "@bitwarden/common/billing/abstractions/organization-billing.service"; -import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; - -import { AcceptOrganizationInviteService } from "../../auth/organization-invite/accept-organization.service"; -import { OrganizationInvite } from "../../auth/organization-invite/organization-invite"; -import { - OrganizationCreatedEvent, - SubscriptionProduct, - TrialOrganizationType, -} from "../../billing/accounts/trial-initiation/trial-billing-step.component"; - -import { RouterService } from "./../../core/router.service"; -import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component"; - -export enum ValidOrgParams { - families = "families", - enterprise = "enterprise", - teams = "teams", - teamsStarter = "teamsStarter", - individual = "individual", - premium = "premium", - free = "free", -} - -enum ValidLayoutParams { - default = "default", - teams = "teams", - teams1 = "teams1", - teams2 = "teams2", - teams3 = "teams3", - enterprise = "enterprise", - enterprise1 = "enterprise1", - enterprise2 = "enterprise2", - cnetcmpgnent = "cnetcmpgnent", - cnetcmpgnind = "cnetcmpgnind", - cnetcmpgnteams = "cnetcmpgnteams", - abmenterprise = "abmenterprise", - abmteams = "abmteams", - secretsManager = "secretsManager", -} - -@Component({ - selector: "app-trial", - templateUrl: "trial-initiation.component.html", -}) -export class TrialInitiationComponent implements OnInit, OnDestroy { - email = ""; - fromOrgInvite = false; - org = ""; - orgInfoSubLabel = ""; - orgId = ""; - orgLabel = ""; - billingSubLabel = ""; - layout = "default"; - plan: PlanType; - productTier: ProductTierType; - accountCreateOnly = true; - useTrialStepper = false; - loading = false; - policies: Policy[]; - enforcedPolicyOptions: MasterPasswordPolicyOptions; - trialFlowOrgs: string[] = [ - ValidOrgParams.teams, - ValidOrgParams.teamsStarter, - ValidOrgParams.enterprise, - ValidOrgParams.families, - ]; - routeFlowOrgs: string[] = [ - ValidOrgParams.free, - ValidOrgParams.premium, - ValidOrgParams.individual, - ]; - layouts = ValidLayoutParams; - referenceData: ReferenceEventRequest; - @ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent; - - orgInfoFormGroup = this.formBuilder.group({ - name: ["", { validators: [Validators.required, Validators.maxLength(50)], updateOn: "change" }], - email: [""], - }); - - private set referenceDataId(referenceId: string) { - if (referenceId != null) { - this.referenceData.id = referenceId; - } else { - this.referenceData.id = ("; " + document.cookie) - .split("; reference=") - .pop() - .split(";") - .shift(); - } - - if (this.referenceData.id === "") { - this.referenceData.id = null; - } else { - // Matches "_ga_QBRN562QQQ=value1.value2.session" and captures values and session. - const regex = /_ga_QBRN562QQQ=([^.]+)\.([^.]+)\.(\d+)/; - const match = document.cookie.match(regex); - if (match) { - this.referenceData.session = match[3]; - } - } - } - - private destroy$ = new Subject(); - protected enableTrialPayment$ = this.configService.getFeatureFlag$( - FeatureFlag.TrialPaymentOptional, - ); - - constructor( - private route: ActivatedRoute, - protected router: Router, - private formBuilder: UntypedFormBuilder, - private titleCasePipe: TitleCasePipe, - private logService: LogService, - private policyApiService: PolicyApiServiceAbstraction, - private policyService: PolicyService, - private i18nService: I18nService, - private routerService: RouterService, - private acceptOrgInviteService: AcceptOrganizationInviteService, - private organizationBillingService: OrganizationBillingService, - private configService: ConfigService, - ) {} - - async ngOnInit(): Promise { - this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((qParams) => { - this.referenceData = new ReferenceEventRequest(); - if (qParams.email != null && qParams.email.indexOf("@") > -1) { - this.email = qParams.email; - this.fromOrgInvite = qParams.fromOrgInvite === "true"; - } - - this.referenceDataId = qParams.reference; - - if (Object.values(ValidLayoutParams).includes(qParams.layout)) { - this.layout = qParams.layout; - this.accountCreateOnly = false; - } - - if (this.trialFlowOrgs.includes(qParams.org)) { - this.org = qParams.org; - this.orgLabel = this.titleCasePipe.transform(this.orgDisplayName); - this.useTrialStepper = true; - this.referenceData.flow = qParams.org; - - if (this.org === ValidOrgParams.families) { - this.plan = PlanType.FamiliesAnnually; - this.productTier = ProductTierType.Families; - } else if (this.org === ValidOrgParams.teamsStarter) { - this.plan = PlanType.TeamsStarter; - this.productTier = ProductTierType.TeamsStarter; - } else if (this.org === ValidOrgParams.teams) { - this.plan = PlanType.TeamsAnnually; - this.productTier = ProductTierType.Teams; - } else if (this.org === ValidOrgParams.enterprise) { - this.plan = PlanType.EnterpriseAnnually; - this.productTier = ProductTierType.Enterprise; - } - } else if (this.routeFlowOrgs.includes(qParams.org)) { - this.referenceData.flow = qParams.org; - const route = this.router.createUrlTree(["create-organization"], { - queryParams: { plan: qParams.org }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - - // Are they coming from an email for sponsoring a families organization - // After logging in redirect them to setup the families sponsorship - this.setupFamilySponsorship(qParams.sponsorshipToken); - - this.referenceData.initiationPath = this.accountCreateOnly - ? "Registration form" - : "Password Manager trial from marketing website"; - }); - - // If there's a deep linked org invite, use it to get the password policies - const orgInvite = await this.acceptOrgInviteService.getOrganizationInvite(); - if (orgInvite != null) { - await this.initPasswordPolicies(orgInvite); - } - - this.orgInfoFormGroup.controls.name.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.orgInfoFormGroup.controls.name.markAsTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - stepSelectionChange(event: StepperSelectionEvent) { - // Set org info sub label - if (event.selectedIndex === 1 && this.orgInfoFormGroup.controls.name.value === "") { - this.orgInfoSubLabel = - "Enter your " + - this.titleCasePipe.transform(this.orgDisplayName) + - " organization information"; - } else if (event.previouslySelectedIndex === 1) { - this.orgInfoSubLabel = this.orgInfoFormGroup.controls.name.value; - } - - //set billing sub label - if (event.selectedIndex === 2) { - this.billingSubLabel = this.i18nService.t("billingTrialSubLabel"); - } - } - - async createOrganizationOnTrial() { - this.loading = true; - const organization: OrganizationInformation = { - name: this.orgInfoFormGroup.get("name").value, - billingEmail: this.orgInfoFormGroup.get("email").value, - initiationPath: "Password Manager trial from marketing website", - }; - - const plan: PlanInformation = { - type: this.plan, - passwordManagerSeats: 1, - }; - - const response = await this.organizationBillingService.purchaseSubscriptionNoPaymentMethod({ - organization, - plan, - }); - - this.orgId = response?.id; - this.billingSubLabel = `${this.i18nService.t("annual")} ($0/${this.i18nService.t("yr")})`; - this.loading = false; - this.verticalStepper.next(); - } - - createdAccount(email: string) { - this.email = email; - this.orgInfoFormGroup.get("email")?.setValue(email); - this.verticalStepper.next(); - } - - billingSuccess(event: any) { - this.orgId = event?.orgId; - this.billingSubLabel = event?.subLabelText; - this.verticalStepper.next(); - } - - createdOrganization(event: OrganizationCreatedEvent) { - this.orgId = event.organizationId; - this.billingSubLabel = event.planDescription; - this.verticalStepper.next(); - } - - navigateToOrgVault() { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["organizations", this.orgId, "vault"]); - } - - navigateToOrgInvite() { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["organizations", this.orgId, "members"]); - } - - previousStep() { - this.verticalStepper.previous(); - } - - get orgDisplayName() { - if (this.org === "teamsStarter") { - return "Teams Starter"; - } - - return this.org; - } - - get freeTrialText() { - const translationKey = - this.layout === this.layouts.secretsManager - ? "startYour7DayFreeTrialOfBitwardenSecretsManagerFor" - : "startYour7DayFreeTrialOfBitwardenFor"; - - return this.i18nService.t(translationKey, this.org); - } - - get trialOrganizationType(): TrialOrganizationType { - switch (this.productTier) { - case ProductTierType.Free: - return null; - default: - return this.productTier; - } - } - - private setupFamilySponsorship(sponsorshipToken: string) { - if (sponsorshipToken != null) { - const route = this.router.createUrlTree(["setup/families-for-enterprise"], { - queryParams: { plan: sponsorshipToken }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - } - - private async initPasswordPolicies(invite: OrganizationInvite): Promise { - if (invite == null) { - return; - } - - try { - this.policies = await this.policyApiService.getPoliciesByToken( - invite.organizationId, - invite.token, - invite.email, - invite.organizationUserId, - ); - } catch (e) { - this.logService.error(e); - } - - if (this.policies != null) { - this.policyService - .masterPasswordPolicyOptions$(this.policies) - .pipe(takeUntil(this.destroy$)) - .subscribe((enforcedPasswordPolicyOptions) => { - this.enforcedPolicyOptions = enforcedPasswordPolicyOptions; - }); - } - } - - protected readonly SubscriptionProduct = SubscriptionProduct; -} diff --git a/apps/web/src/app/billing/trial-initiation/trial-initiation.module.ts b/apps/web/src/app/billing/trial-initiation/trial-initiation.module.ts index 7b81f57e33b..2c6481ec1bf 100644 --- a/apps/web/src/app/billing/trial-initiation/trial-initiation.module.ts +++ b/apps/web/src/app/billing/trial-initiation/trial-initiation.module.ts @@ -6,7 +6,6 @@ import { InputPasswordComponent } from "@bitwarden/auth/angular"; import { FormFieldModule } from "@bitwarden/components"; import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module"; -import { RegisterFormModule } from "../../auth/register-form/register-form.module"; import { TaxInfoComponent } from "../../billing"; import { TrialBillingStepComponent } from "../../billing/accounts/trial-initiation/trial-billing-step.component"; import { SecretsManagerTrialFreeStepperComponent } from "../../billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component"; @@ -39,7 +38,6 @@ import { TeamsContentComponent } from "./content/teams-content.component"; import { Teams1ContentComponent } from "./content/teams1-content.component"; import { Teams2ContentComponent } from "./content/teams2-content.component"; import { Teams3ContentComponent } from "./content/teams3-content.component"; -import { TrialInitiationComponent } from "./trial-initiation.component"; import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.module"; @NgModule({ @@ -48,7 +46,6 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul CdkStepperModule, VerticalStepperModule, FormFieldModule, - RegisterFormModule, OrganizationCreateModule, EnvironmentSelectorModule, TaxInfoComponent, @@ -56,7 +53,6 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul InputPasswordComponent, ], declarations: [ - TrialInitiationComponent, CompleteTrialInitiationComponent, EnterpriseContentComponent, TeamsContentComponent, @@ -87,7 +83,7 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul SecretsManagerTrialFreeStepperComponent, SecretsManagerTrialPaidStepperComponent, ], - exports: [TrialInitiationComponent, CompleteTrialInitiationComponent], + exports: [CompleteTrialInitiationComponent], providers: [TitleCasePipe], }) export class TrialInitiationModule {} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 7b567460dab..be42a9ba34e 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -52,10 +52,10 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { ClientType } from "@bitwarden/common/enums"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService, Urls, diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 4efec67e767..3623d9b0d2f 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -7,7 +7,7 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts index 4f2ae8f77e0..0101bc5aa97 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts @@ -7,8 +7,8 @@ import { OrganizationUserResetPasswordWithIdRequest } from "@bitwarden/admin-con import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendWithIdRequest } from "@bitwarden/common/tools/send/models/request/send-with-id.request"; diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index ae47798420e..1acbc2012c5 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -8,7 +8,7 @@ import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractio import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts index f0ac3ef9b48..e859993af32 100644 --- a/apps/web/src/app/layouts/user-layout.component.ts +++ b/apps/web/src/app/layouts/user-layout.component.ts @@ -31,7 +31,6 @@ import { WebLayoutModule } from "./web-layout.module"; }) export class UserLayoutComponent implements OnInit { protected readonly logo = PasswordManagerLogo; - isFreeFamilyFlagEnabled: boolean; protected hasFamilySponsorshipAvailable$: Observable; protected showSponsoredFamilies$: Observable; protected showSubscription$: Observable; diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 3176ac81c1a..d6ba5a1990c 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -20,7 +20,6 @@ import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from import { HintComponent } from "../auth/hint.component"; import { RecoverDeleteComponent } from "../auth/recover-delete.component"; import { RecoverTwoFactorComponent } from "../auth/recover-two-factor.component"; -import { RegisterFormModule } from "../auth/register-form/register-form.module"; import { RemovePasswordComponent } from "../auth/remove-password.component"; import { SetPasswordComponent } from "../auth/set-password.component"; import { AccountComponent } from "../auth/settings/account/account.component"; @@ -90,7 +89,6 @@ import { SharedModule } from "./shared.module"; @NgModule({ imports: [ SharedModule, - RegisterFormModule, ProductSwitcherModule, UserVerificationModule, ChangeKdfModule, diff --git a/apps/web/src/app/tools/import/import-collection-admin.service.ts b/apps/web/src/app/tools/import/import-collection-admin.service.ts index 9f34966a2cd..64050eb9c06 100644 --- a/apps/web/src/app/tools/import/import-collection-admin.service.ts +++ b/apps/web/src/app/tools/import/import-collection-admin.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@angular/core"; import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common"; -import { ImportCollectionServiceAbstraction } from "@bitwarden/importer/core"; +import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core"; @Injectable() export class ImportCollectionAdminService implements ImportCollectionServiceAbstraction { diff --git a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.html b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.html index 76d4398cc0f..9c5b587e60a 100644 --- a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.html +++ b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.html @@ -39,13 +39,7 @@ {{ "owner" | i18n }} - + {{ "weakness" | i18n }} diff --git a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts index 8b781b77378..c374ecd0e4a 100644 --- a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts +++ b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts @@ -59,7 +59,6 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen this.weakPasswordCiphers = []; this.filterStatus = [0]; this.findWeakPasswords(allCiphers); - this.weakPasswordCiphers = this.sortCiphers(this.weakPasswordCiphers, "score", false); } protected findWeakPasswords(ciphers: CipherView[]): void { @@ -113,29 +112,6 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen this.filterCiphersByOrg(this.weakPasswordCiphers); } - onSortChange(field: string, event: Event) { - const target = event.target as HTMLInputElement; - const ascending = target.checked; - this.weakPasswordCiphers = this.sortCiphers(this.weakPasswordCiphers, field, ascending); - } - - protected sortCiphers( - ciphers: ReportResult[], - field: string, - ascending: boolean, - ): ReportResult[] { - return ciphers.sort((a, b) => { - const aValue = a[field as keyof ReportResult]; - const bValue = b[field as keyof ReportResult]; - - if (aValue === bValue) { - return 0; - } - const comparison = aValue > bValue ? 1 : -1; - return ascending ? comparison : -comparison; - }); - } - protected canManageCipher(c: CipherView): boolean { // this will only ever be false from the org view; return true; diff --git a/apps/web/src/app/tools/send/send-access-file.component.ts b/apps/web/src/app/tools/send/send-access-file.component.ts index b55e955f355..05408bc34f7 100644 --- a/apps/web/src/app/tools/send/send-access-file.component.ts +++ b/apps/web/src/app/tools/send/send-access-file.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Component, Input } from "@angular/core"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html index fbe0649c7aa..61fc290f6fe 100644 --- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html +++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html @@ -70,11 +70,11 @@
- {{ "grantAddAccessCollectionWarning" | i18n }} + {{ "grantManageCollectionWarning" | i18n }} {{ "grantCollectionAccess" | i18n }} {{ diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts index 19dad4e9da0..4af08e19d74 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts @@ -42,6 +42,7 @@ export class VaultCipherRowComponent implements OnInit { @Input() collections: CollectionView[]; @Input() viewingOrgVault: boolean; @Input() canEditCipher: boolean; + @Input() canAssignCollections: boolean; @Input() canManageCollection: boolean; @Output() onEvent = new EventEmitter(); @@ -101,7 +102,7 @@ export class VaultCipherRowComponent implements OnInit { } protected get showAssignToCollections() { - return this.organizations?.length && this.canEditCipher && !this.cipher.isDeleted; + return this.organizations?.length && this.canAssignCollections && !this.cipher.isDeleted; } protected get showClone() { @@ -208,6 +209,6 @@ export class VaultCipherRowComponent implements OnInit { return true; // Always show checkbox in individual vault or for non-org items } - return this.organization.canEditAllCiphers || this.cipher.edit; + return this.organization.canEditAllCiphers || (this.cipher.edit && this.cipher.viewPassword); } } diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index 653d05ef129..a32def5fc0c 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -144,6 +144,7 @@ [collections]="allCollections" [checked]="selection.isSelected(item)" [canEditCipher]="canEditCipher(item.cipher)" + [canAssignCollections]="canAssignCollections(item.cipher)" [canManageCollection]="canManageCollection(item.cipher)" (checkedToggled)="selection.toggle(item)" (onEvent)="event($event)" diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 3e1cf173a47..a641c5b5908 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -236,6 +236,13 @@ export class VaultItemsComponent { return (organization.canEditAllCiphers && this.viewingOrgVault) || cipher.edit; } + protected canAssignCollections(cipher: CipherView) { + const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId); + return ( + (organization?.canEditAllCiphers && this.viewingOrgVault) || cipher.canAssignToCollections + ); + } + protected canManageCollection(cipher: CipherView) { // If the cipher is not part of an organization (personal item), user can manage it if (cipher.organizationId == null) { @@ -461,7 +468,7 @@ export class VaultItemsComponent { private allCiphersHaveEditAccess(): boolean { return this.selection.selected .filter(({ cipher }) => cipher) - .every(({ cipher }) => cipher?.edit); + .every(({ cipher }) => cipher?.edit && cipher?.viewPassword); } private getUniqueOrganizationIds(): Set { diff --git a/apps/web/src/app/vault/individual-vault/attachments.component.ts b/apps/web/src/app/vault/individual-vault/attachments.component.ts index a6c25b71fd4..c6079dbe78f 100644 --- a/apps/web/src/app/vault/individual-vault/attachments.component.ts +++ b/apps/web/src/app/vault/individual-vault/attachments.component.ts @@ -4,7 +4,7 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/ang import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts index 7e6f7eb8588..b8494c8aa54 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts @@ -4,10 +4,8 @@ import { Observable } from "rxjs"; import { CollectionAdminView, CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { FolderView } from "@bitwarden/common/src/vault/models/view/folder.view"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { CipherTypeFilter, diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts index 9b24f95e2ea..03dfa92d0b5 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts @@ -274,6 +274,7 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { folderCopy.id = f.id; folderCopy.revisionDate = f.revisionDate; folderCopy.icon = "bwi-folder"; + folderCopy.fullName = f.name; // save full folder name before separating it into parts const parts = f.name != null ? f.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; ServiceUtils.nestedTraverse(nodes, 0, parts, folderCopy, null, NestingDelimiter); }); diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts index 10888aea13e..9259dd08114 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts @@ -1,10 +1,8 @@ import { CollectionAdminView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { FolderView } from "@bitwarden/common/src/vault/models/view/folder.view"; import { CipherType } from "@bitwarden/common/vault/enums"; import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; export type CipherStatus = "all" | "favorites" | "trash" | CipherType; @@ -12,5 +10,13 @@ export type CipherTypeFilter = ITreeNodeObject & { type: CipherStatus; icon: str export type CollectionFilter = CollectionAdminView & { icon: string; }; -export type FolderFilter = FolderView & { icon: string }; +export type FolderFilter = FolderView & { + icon: string; + /** + * Full folder name. + * + * Used for when the folder `name` property is be separated into parts. + */ + fullName?: string; +}; export type OrganizationFilter = Organization & { icon: string; hideOptions?: boolean }; diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 7215c980206..950c1d77731 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -77,6 +77,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; import { DialogService, Icons, ToastService } from "@bitwarden/components"; import { + AddEditFolderDialogComponent, + AddEditFolderDialogResult, CipherFormConfig, CollectionAssignmentResult, DecryptionFailureDialogComponent, @@ -118,7 +120,6 @@ import { BulkMoveDialogResult, openBulkMoveDialog, } from "./bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component"; -import { FolderAddEditDialogResult, openFolderAddEditDialog } from "./folder-add-edit.component"; import { VaultBannersComponent } from "./vault-banners/vault-banners.component"; import { VaultFilterComponent } from "./vault-filter/components/vault-filter.component"; import { VaultFilterService } from "./vault-filter/services/abstractions/vault-filter.service"; @@ -607,20 +608,24 @@ export class VaultComponent implements OnInit, OnDestroy { await this.filterComponent.filters?.organizationFilter?.action(orgNode); } - addFolder = async (): Promise => { - openFolderAddEditDialog(this.dialogService); + addFolder = (): void => { + AddEditFolderDialogComponent.open(this.dialogService); }; editFolder = async (folder: FolderFilter): Promise => { - const dialog = openFolderAddEditDialog(this.dialogService, { - data: { - folderId: folder.id, + const dialogRef = AddEditFolderDialogComponent.open(this.dialogService, { + editFolderConfig: { + // Shallow copy is used so the original folder object is not modified + folder: { + ...folder, + name: folder.fullName ?? folder.name, // If the filter has a fullName populated, use that as the editable name + }, }, }); - const result = await lastValueFrom(dialog.closed); + const result = await lastValueFrom(dialogRef.closed); - if (result === FolderAddEditDialogResult.Deleted) { + if (result === AddEditFolderDialogResult.Deleted) { await this.router.navigate([], { queryParams: { folderId: null }, queryParamsHandling: "merge", diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index 9aa92f3995c..9b6d15c581d 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -15,6 +15,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -25,13 +26,8 @@ import { DialogService, ToastService, } from "@bitwarden/components"; +import { CipherViewComponent } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { PremiumUpgradePromptService } from "../../../../../../libs/common/src/vault/abstractions/premium-upgrade-prompt.service"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { CipherViewComponent } from "../../../../../../libs/vault/src/cipher-view/cipher-view.component"; import { SharedModule } from "../../shared/shared.module"; import { WebVaultPremiumUpgradePromptService } from "../services/web-premium-upgrade-prompt.service"; import { WebViewPasswordHistoryService } from "../services/web-view-password-history.service"; diff --git a/apps/web/src/app/vault/org-vault/attachments.component.ts b/apps/web/src/app/vault/org-vault/attachments.component.ts index c8badffb36f..37136a86cdb 100644 --- a/apps/web/src/app/vault/org-vault/attachments.component.ts +++ b/apps/web/src/app/vault/org-vault/attachments.component.ts @@ -6,7 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts index d10f83fd42b..5ccddeee4bb 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts @@ -7,13 +7,10 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { CipherId } from "@bitwarden/common/types/guid"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { Account } from "../../../../../../../libs/importer/src/importers/lastpass/access/models"; import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "./admin-console-cipher-form-config.service"; @@ -85,7 +82,14 @@ describe("AdminConsoleCipherFormConfigService", () => { { provide: CipherService, useValue: { get: getCipher } }, { provide: AccountService, - useValue: { activeAccount$: new BehaviorSubject(new Account()) }, + useValue: { + activeAccount$: new BehaviorSubject({ + id: "123-456-789" as UserId, + email: "test@email.com", + emailVerified: true, + name: "Test User", + }), + }, }, ], }); diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts index 348037fdbea..32e75644d09 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts @@ -15,14 +15,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; +import { CipherFormConfig, CipherFormConfigService, CipherFormMode } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { - CipherFormConfig, - CipherFormConfigService, - CipherFormMode, -} from "../../../../../../../libs/vault/src/cipher-form/abstractions/cipher-form-config.service"; import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; /** Admin Console implementation of the `CipherFormConfigService`. */ diff --git a/apps/web/src/app/vault/services/web-view-password-history.service.ts b/apps/web/src/app/vault/services/web-view-password-history.service.ts index 728a3b7e245..b1451b268de 100644 --- a/apps/web/src/app/vault/services/web-view-password-history.service.ts +++ b/apps/web/src/app/vault/services/web-view-password-history.service.ts @@ -1,11 +1,9 @@ import { Injectable } from "@angular/core"; +import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { ViewPasswordHistoryService } from "../../../../../../libs/common/src/vault/abstractions/view-password-history.service"; import { openPasswordHistoryDialog } from "../individual-vault/password-history.component"; /** diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 34551f68cc0..f663a4c6397 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -485,6 +485,18 @@ "editFolder": { "message": "Edit folder" }, + "newFolder": { + "message": "New folder" + }, + "folderName": { + "message": "Folder name" + }, + "folderHintText": { + "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + }, + "deleteFolderPermanently": { + "message": "Are you sure you want to permanently delete this folder?" + }, "baseDomain": { "message": "Base domain", "description": "Domain name. Example: website.com" @@ -749,15 +761,6 @@ "itemName": { "message": "Item name" }, - "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", - "placeholders": { - "collections": { - "content": "$1", - "example": "Work, Personal" - } - } - }, "ex": { "message": "ex.", "description": "Short abbreviation for 'example'." @@ -8789,11 +8792,11 @@ "readOnlyCollectionAccess": { "message": "You do not have access to manage this collection." }, - "grantAddAccessCollectionWarningTitle": { - "message": "Missing Can Manage Permissions" + "grantManageCollectionWarningTitle": { + "message": "Missing Manage Collection Permissions" }, - "grantAddAccessCollectionWarning": { - "message": "Grant Can manage permissions to allow full collection management including deletion of collection." + "grantManageCollectionWarning": { + "message": "Grant Manage collection permissions to allow full collection management including deletion of collection." }, "grantCollectionAccess": { "message": "Grant groups or members access to this collection." @@ -10143,6 +10146,15 @@ "descriptorCode": { "message": "Descriptor code" }, + "cannotRemoveViewOnlyCollections": { + "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "placeholders": { + "collections": { + "content": "$1", + "example": "Work, Personal" + } + } + }, "importantNotice": { "message": "Important notice" }, @@ -10334,6 +10346,15 @@ } } }, + "accountDeprovisioningNotification" : { + "message": "Administrators now have the ability to delete member accounts that belong to a claimed domain." + }, + "deleteManagedUserWarningDesc": { + "message": "This action will delete the member account including all items in their vault. This replaces the previous Remove action." + }, + "deleteManagedUserWarning": { + "message": "Delete is a new action!" + }, "seatsRemaining": { "message": "You have $REMAINING$ seats remaining out of $TOTAL$ seats assigned to this organization. Contact your provider to manage your subscription.", "placeholders": { @@ -10346,5 +10367,32 @@ "example": "10" } } + }, + "existingOrganization": { + "message": "Existing organization" + }, + "selectOrganizationProviderPortal": { + "message": "Select an organization to add to your Provider Portal." + }, + "noOrganizations": { + "message": "There are no organizations to list" + }, + "yourProviderSubscriptionCredit": { + "message": "Your provider subscription will receive a credit for any remaining time in the organization's subscription." + }, + "doYouWantToAddThisOrg": { + "message": "Do you want to add this organization to $PROVIDER$?", + "placeholders": { + "provider": { + "content": "$1", + "example": "Cool MSP" + } + } + }, + "addedExistingOrganization": { + "message": "Added existing organization" + }, + "assignedExceedsAvailable": { + "message": "Assigned seats exceed available seats." } } diff --git a/apps/web/tsconfig.build.json b/apps/web/tsconfig.build.json index b10c4f9d899..39ab37efbb8 100644 --- a/apps/web/tsconfig.build.json +++ b/apps/web/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", "files": ["src/polyfills.ts", "src/main.ts", "src/theme.ts"], - "include": ["src/connectors/*.ts", "../../libs/common/src/platform/services/**/*.worker.ts"] + "include": [ + "src/connectors/*.ts", + "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts" + ] } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index c88b41d6f0e..c05f24b9a8d 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -18,7 +18,7 @@ "@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"], "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/importer/ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], @@ -43,6 +43,6 @@ "src/connectors/*.ts", "src/**/*.stories.ts", "src/**/*.spec.ts", - "../../libs/common/src/platform/services/**/*.worker.ts" + "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts" ] } diff --git a/bitwarden_license/bit-cli/tsconfig.json b/bitwarden_license/bit-cli/tsconfig.json index e3d6cc5c7b7..4a972b540a7 100644 --- a/bitwarden_license/bit-cli/tsconfig.json +++ b/bitwarden_license/bit-cli/tsconfig.json @@ -18,7 +18,7 @@ "@bitwarden/auth/common": ["../../libs/auth/src/common"], "@bitwarden/auth/angular": ["../../libs/auth/src/angular"], "@bitwarden/common/*": ["../../libs/common/src/*"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/generator-core": ["../../libs/tools/generator/core/src"], "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"], diff --git a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts index e893b2dfe8c..448399a8bb0 100644 --- a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts +++ b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts @@ -4,8 +4,8 @@ import { OrganizationUserApiService, OrganizationUserResetPasswordDetailsResponse, } from "@bitwarden/admin-console/common"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts index 4c4507e5cb8..025b021f83d 100644 --- a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts +++ b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts @@ -4,7 +4,7 @@ import { OrganizationUserApiService, OrganizationUserResetPasswordDetailsResponse, } from "@bitwarden/admin-console/common"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.spec.ts b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.spec.ts index 5b89a2abb1e..64f55ccf99f 100644 --- a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.spec.ts +++ b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.spec.ts @@ -4,7 +4,7 @@ import { fakeAsync, flush } from "@angular/core/testing"; import { mock } from "jest-mock-extended"; import { of } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; diff --git a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.ts b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.ts index 00b4dc78dae..7db34757b96 100644 --- a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.ts +++ b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/critical-apps.service.ts @@ -13,7 +13,7 @@ import { zip, } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts index ac8ad3112b9..744cf2c4674 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts @@ -10,8 +10,8 @@ import { OrganizationAuthRequestApiService } from "@bitwarden/bit-common/admin-c import { OrganizationAuthRequestService } from "@bitwarden/bit-common/admin-console/auth-requests/organization-auth-request.service"; import { PendingAuthRequestView } from "@bitwarden/bit-common/admin-console/auth-requests/pending-auth-request.view"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts index 6ab07cc0794..c5b949512d7 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts @@ -13,8 +13,8 @@ import { ProviderUserBulkConfirmRequest } from "@bitwarden/common/admin-console/ import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response"; import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { DialogService } from "@bitwarden/components"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts index 16f794bd6d2..03e47569a55 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts @@ -15,8 +15,8 @@ import { ProviderUserStatusType, ProviderUserType } from "@bitwarden/common/admi import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-confirm.request"; import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html index a20dd1379e2..bf82fbd160b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html @@ -37,25 +37,5 @@ > - - {{ "providerClientVaultPrivacyNotification" | i18n }} - - {{ "contactBitwardenSupport" | i18n }} . - diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts index 3f1a7ff3989..7e47da95e2b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts @@ -10,27 +10,15 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderStatusType } from "@bitwarden/common/admin-console/enums"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { BannerModule, IconModule, LinkModule } from "@bitwarden/components"; +import { IconModule } from "@bitwarden/components"; import { ProviderPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/provider-portal-logo"; import { WebLayoutModule } from "@bitwarden/web-vault/app/layouts/web-layout.module"; -import { ProviderClientVaultPrivacyBannerService } from "./services/provider-client-vault-privacy-banner.service"; - @Component({ selector: "providers-layout", templateUrl: "providers-layout.component.html", standalone: true, - imports: [ - CommonModule, - RouterModule, - JslibModule, - WebLayoutModule, - IconModule, - LinkModule, - BannerModule, - ], + imports: [CommonModule, RouterModule, JslibModule, WebLayoutModule, IconModule], }) export class ProvidersLayoutComponent implements OnInit, OnDestroy { protected readonly logo = ProviderPortalLogo; @@ -41,15 +29,9 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { protected isBillable: Observable; protected canAccessBilling$: Observable; - protected showProviderClientVaultPrivacyWarningBanner$ = this.configService.getFeatureFlag$( - FeatureFlag.ProviderClientVaultPrivacyBanner, - ); - constructor( private route: ActivatedRoute, private providerService: ProviderService, - private configService: ConfigService, - protected providerClientVaultPrivacyBannerService: ProviderClientVaultPrivacyBannerService, ) {} ngOnInit() { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index 37cb9618b60..3310be7ba36 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -17,6 +17,7 @@ import { ProviderSubscriptionComponent, ProviderSubscriptionStatusComponent, } from "../../billing/providers"; +import { AddExistingOrganizationDialogComponent } from "../../billing/providers/clients/add-existing-organization-dialog.component"; import { AddOrganizationComponent } from "./clients/add-organization.component"; import { CreateOrganizationComponent } from "./clients/create-organization.component"; @@ -63,6 +64,7 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr SetupProviderComponent, UserAddEditComponent, AddEditMemberDialogComponent, + AddExistingOrganizationDialogComponent, CreateClientDialogComponent, ManageClientNameDialogComponent, ManageClientSubscriptionDialogComponent, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts deleted file mode 100644 index c347f5c2aae..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { - StateProvider, - AC_BANNERS_DISMISSED_DISK, - UserKeyDefinition, -} from "@bitwarden/common/platform/state"; - -export const SHOW_BANNER_KEY = new UserKeyDefinition( - AC_BANNERS_DISMISSED_DISK, - "showProviderClientVaultPrivacyBanner", - { - deserializer: (b) => b, - clearOn: [], - }, -); - -/** Displays a banner warning provider users that client organization vaults - * will soon become inaccessible directly. */ -@Injectable({ providedIn: "root" }) -export class ProviderClientVaultPrivacyBannerService { - private _showBanner = this.stateProvider.getActive(SHOW_BANNER_KEY); - - showBanner$ = this._showBanner.state$; - - constructor(private stateProvider: StateProvider) {} - - async hideBanner() { - await this._showBanner.update(() => false); - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts index 264b43aee9d..d3482ea67a5 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts @@ -1,15 +1,20 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Injectable } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; +import { switchMap } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { ProviderAddOrganizationRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-add-organization.request"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; import { PlanType } from "@bitwarden/common/billing/enums"; import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { StateProvider } from "@bitwarden/common/platform/state"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { KeyService } from "@bitwarden/key-management"; @@ -23,6 +28,8 @@ export class WebProviderService { private i18nService: I18nService, private encryptService: EncryptService, private billingApiService: BillingApiServiceAbstraction, + private stateProvider: StateProvider, + private providerApiService: ProviderApiServiceAbstraction, ) {} async addOrganizationToProvider(providerId: string, organizationId: string) { @@ -40,6 +47,22 @@ export class WebProviderService { return response; } + async addOrganizationToProviderVNext(providerId: string, organizationId: string): Promise { + const orgKey = await firstValueFrom( + this.stateProvider.activeUserId$.pipe( + switchMap((userId) => this.keyService.orgKeys$(userId)), + map((organizationKeysById) => organizationKeysById[organizationId as OrganizationId]), + ), + ); + const providerKey = await this.keyService.getProviderKey(providerId); + const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey); + await this.providerApiService.addOrganizationToProvider(providerId, { + key: encryptedOrgKey.encryptedString, + organizationId, + }); + await this.syncService.fullSync(true); + } + async createClientOrganization( providerId: string, name: string, diff --git a/bitwarden_license/bit-web/src/app/app.component.ts b/bitwarden_license/bit-web/src/app/app.component.ts index 1e0f60e2cd2..dd814f5c0d2 100644 --- a/bitwarden_license/bit-web/src/app/app.component.ts +++ b/bitwarden_license/bit-web/src/app/app.component.ts @@ -20,17 +20,10 @@ export class AppComponent extends BaseAppComponent implements OnInit { this.policyListService.addPolicies([ new MaximumVaultTimeoutPolicy(), new DisablePersonalVaultExportPolicy(), + new FreeFamiliesSponsorshipPolicy(), + new ActivateAutofillPolicy(), ]); - this.configService - .getFeatureFlag(FeatureFlag.DisableFreeFamiliesSponsorship) - .then((isFreeFamilyEnabled) => { - if (isFreeFamilyEnabled) { - this.policyListService.addPolicies([new FreeFamiliesSponsorshipPolicy()]); - } - this.policyListService.addPolicies([new ActivateAutofillPolicy()]); - }); - this.configService.getFeatureFlag(FeatureFlag.IdpAutoSubmitLogin).then((enabled) => { if ( enabled && diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.html new file mode 100644 index 00000000000..a22484ed92d --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.html @@ -0,0 +1,73 @@ + + + {{ "addExistingOrganization" | i18n }} + + + +

{{ "selectOrganizationProviderPortal" | i18n }}

+ + + + {{ "name" | i18n }} + {{ "assigned" | i18n }} + + + + + + + + + {{ addable.name }} +
+ {{ "assignedExceedsAvailable" | i18n }} +
+ + {{ addable.seats }} + + + + +
+
+

+ {{ "noOrganizations" | i18n }} +

+
+ +

{{ "yourProviderSubscriptionCredit" | i18n }}

+

{{ "doYouWantToAddThisOrg" | i18n: dialogParams.provider.name }}

+
+
{{ "organization" | i18n }}: {{ selectedOrganization.name }}
+
{{ "billingPlan" | i18n }}: {{ selectedOrganization.plan }}
+
{{ "assignedSeats" | i18n }}: {{ selectedOrganization.seats }}
+
+
+
+ + + + +
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts new file mode 100644 index 00000000000..3df0693d091 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/add-existing-organization-dialog.component.ts @@ -0,0 +1,82 @@ +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, Inject, OnInit } from "@angular/core"; + +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; +import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; +import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogService, ToastService } from "@bitwarden/components"; + +import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service"; + +export type AddExistingOrganizationDialogParams = { + provider: Provider; +}; + +export enum AddExistingOrganizationDialogResultType { + Closed = "closed", + Submitted = "submitted", +} + +@Component({ + templateUrl: "./add-existing-organization-dialog.component.html", +}) +export class AddExistingOrganizationDialogComponent implements OnInit { + protected loading: boolean = true; + + addableOrganizations: AddableOrganizationResponse[] = []; + selectedOrganization?: AddableOrganizationResponse; + + protected readonly ResultType = AddExistingOrganizationDialogResultType; + + constructor( + @Inject(DIALOG_DATA) protected dialogParams: AddExistingOrganizationDialogParams, + private dialogRef: DialogRef, + private i18nService: I18nService, + private providerApiService: ProviderApiServiceAbstraction, + private toastService: ToastService, + private webProviderService: WebProviderService, + ) {} + + async ngOnInit() { + this.addableOrganizations = await this.providerApiService.getProviderAddableOrganizations( + this.dialogParams.provider.id, + ); + this.loading = false; + } + + addExistingOrganization = async (): Promise => { + if (this.selectedOrganization) { + await this.webProviderService.addOrganizationToProviderVNext( + this.dialogParams.provider.id, + this.selectedOrganization.id, + ); + + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("addedExistingOrganization"), + }); + + this.dialogRef.close(this.ResultType.Submitted); + } + }; + + selectOrganization(organizationId: string) { + this.selectedOrganization = this.addableOrganizations.find( + (organization) => organization.id === organizationId, + ); + } + + static open = ( + dialogService: DialogService, + dialogConfig: DialogConfig< + AddExistingOrganizationDialogParams, + DialogRef + >, + ) => + dialogService.open< + AddExistingOrganizationDialogResultType, + AddExistingOrganizationDialogParams + >(AddExistingOrganizationDialogComponent, dialogConfig); +} diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html index 7c560e49579..077aeb6c124 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.html @@ -1,9 +1,39 @@ - - - {{ "addNewOrganization" | i18n }} - + + + + + + + + + + + {{ "addNewOrganization" | i18n }} + + diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts index ee2c541e72f..07434369122 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts @@ -11,6 +11,8 @@ import { Provider } from "@bitwarden/common/admin-console/models/domain/provider import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { @@ -25,6 +27,10 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service"; +import { + AddExistingOrganizationDialogComponent, + AddExistingOrganizationDialogResultType, +} from "./add-existing-organization-dialog.component"; import { CreateClientDialogResultType, openCreateClientDialog, @@ -62,6 +68,9 @@ export class ManageClientsComponent { protected searchControl = new FormControl("", { nonNullable: true }); protected plans: PlanResponse[] = []; + protected addExistingOrgsFromProviderPortal$ = this.configService.getFeatureFlag$( + FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal, + ); constructor( private billingApiService: BillingApiServiceAbstraction, @@ -73,6 +82,7 @@ export class ManageClientsComponent { private toastService: ToastService, private validationService: ValidationService, private webProviderService: WebProviderService, + private configService: ConfigService, ) { this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => { this.searchControl.setValue(queryParams.search); @@ -111,19 +121,30 @@ export class ManageClientsComponent { async load() { this.provider = await firstValueFrom(this.providerService.get$(this.providerId)); - this.isProviderAdmin = this.provider?.type === ProviderUserType.ProviderAdmin; - - const clients = (await this.billingApiService.getProviderClientOrganizations(this.providerId)) - .data; - - this.dataSource.data = clients; - + this.dataSource.data = ( + await this.billingApiService.getProviderClientOrganizations(this.providerId) + ).data; this.plans = (await this.billingApiService.getPlans()).data; - this.loading = false; } + addExistingOrganization = async () => { + if (this.provider) { + const reference = AddExistingOrganizationDialogComponent.open(this.dialogService, { + data: { + provider: this.provider, + }, + }); + + const result = await lastValueFrom(reference.closed); + + if (result === AddExistingOrganizationDialogResultType.Submitted) { + await this.load(); + } + } + }; + createClient = async () => { const reference = openCreateClientDialog(this.dialogService, { data: { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project.service.ts index ee2395b3f83..8c9f894f8f6 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.spec.ts index a3d46d2ef2e..c761d73d4a1 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.spec.ts @@ -1,7 +1,7 @@ import { mock } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts index 950d3c42ccb..51c49d79f2d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts @@ -4,7 +4,7 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access.service.ts index 8eb4a5120a2..773cb83e70a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts index a85f5c5e09a..a56111bc655 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.spec.ts index fb0ab467a02..6b527d56502 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.spec.ts @@ -1,7 +1,7 @@ import { mock } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.ts index c5934067fd7..c9d63e61400 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/services/sm-porting-api.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.spec.ts index 4ae80d4decc..d6bc807686a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.spec.ts @@ -3,7 +3,7 @@ import { mock } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts index 920e12ef0cf..5223135c17a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts @@ -4,8 +4,8 @@ import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts index 5f461ff6c49..8b78a7e8975 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts @@ -10,7 +10,7 @@ import { } from "@bitwarden/bit-common/tools/reports/risk-insights/services"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength/password-strength.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { KeyService } from "@bitwarden/key-management"; diff --git a/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts new file mode 100644 index 00000000000..014c9daa783 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts @@ -0,0 +1,34 @@ +import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; +import { SecurityTask, SecurityTaskStatus, SecurityTaskType } from "@bitwarden/vault"; + +/** + * Request type for creating tasks. + * @property cipherId - Optional. The ID of the cipher to create the task for. + * @property type - The type of task to create. Currently defined as "updateAtRiskCredential". + */ +export type CreateTasksRequest = Readonly<{ + cipherId?: CipherId; + type: SecurityTaskType.UpdateAtRiskCredential; +}>; + +export abstract class AdminTaskService { + /** + * Retrieves all tasks for a given organization. + * @param organizationId - The ID of the organization to retrieve tasks for. + * @param status - Optional. The status of the tasks to retrieve. + */ + abstract getAllTasks( + organizationId: OrganizationId, + status?: SecurityTaskStatus | undefined, + ): Promise; + + /** + * Creates multiple tasks for a given organization and sends out notifications to applicable users. + * @param organizationId - The ID of the organization to create tasks for. + * @param tasks - The tasks to create. + */ + abstract bulkCreateTasks( + organizationId: OrganizationId, + tasks: CreateTasksRequest[], + ): Promise; +} diff --git a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts new file mode 100644 index 00000000000..d6a686a071a --- /dev/null +++ b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts @@ -0,0 +1,65 @@ +import { MockProxy, mock } from "jest-mock-extended"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; +import { SecurityTaskStatus, SecurityTaskType } from "@bitwarden/vault"; + +import { CreateTasksRequest } from "./abstractions/admin-task.abstraction"; +import { DefaultAdminTaskService } from "./default-admin-task.service"; + +describe("DefaultAdminTaskService", () => { + let defaultAdminTaskService: DefaultAdminTaskService; + let apiService: MockProxy; + + beforeEach(() => { + apiService = mock(); + defaultAdminTaskService = new DefaultAdminTaskService(apiService); + }); + + describe("getAllTasks", () => { + it("should call the api service with the correct parameters with status", async () => { + const organizationId = "orgId" as OrganizationId; + const status = SecurityTaskStatus.Pending; + const expectedUrl = `/tasks/organization?organizationId=${organizationId}&status=0`; + + await defaultAdminTaskService.getAllTasks(organizationId, status); + + expect(apiService.send).toHaveBeenCalledWith("GET", expectedUrl, null, true, true); + }); + + it("should call the api service with the correct parameters without status", async () => { + const organizationId = "orgId" as OrganizationId; + const expectedUrl = `/tasks/organization?organizationId=${organizationId}`; + + await defaultAdminTaskService.getAllTasks(organizationId); + + expect(apiService.send).toHaveBeenCalledWith("GET", expectedUrl, null, true, true); + }); + }); + + describe("bulkCreateTasks", () => { + it("should call the api service with the correct parameters", async () => { + const organizationId = "orgId" as OrganizationId; + const tasks: CreateTasksRequest[] = [ + { + cipherId: "cipherId-1" as CipherId, + type: SecurityTaskType.UpdateAtRiskCredential, + }, + { + cipherId: "cipherId-2" as CipherId, + type: SecurityTaskType.UpdateAtRiskCredential, + }, + ]; + + await defaultAdminTaskService.bulkCreateTasks(organizationId, tasks); + + expect(apiService.send).toHaveBeenCalledWith( + "POST", + `/tasks/${organizationId}/bulk-create`, + tasks, + true, + true, + ); + }); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts new file mode 100644 index 00000000000..442fde9dbf6 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { + SecurityTask, + SecurityTaskData, + SecurityTaskResponse, + SecurityTaskStatus, +} from "@bitwarden/vault"; + +import { AdminTaskService, CreateTasksRequest } from "./abstractions/admin-task.abstraction"; + +@Injectable() +export class DefaultAdminTaskService implements AdminTaskService { + constructor(private apiService: ApiService) {} + + async getAllTasks( + organizationId: OrganizationId, + status?: SecurityTaskStatus | undefined, + ): Promise { + const queryParams = new URLSearchParams(); + + queryParams.append("organizationId", organizationId); + if (status !== undefined) { + queryParams.append("status", status.toString()); + } + + const r = await this.apiService.send( + "GET", + `/tasks/organization?${queryParams.toString()}`, + null, + true, + true, + ); + const response = new ListResponse(r, SecurityTaskResponse); + + return response.data.map((d) => new SecurityTask(new SecurityTaskData(d))); + } + + async bulkCreateTasks( + organizationId: OrganizationId, + tasks: CreateTasksRequest[], + ): Promise { + await this.apiService.send("POST", `/tasks/${organizationId}/bulk-create`, tasks, true, true); + } +} diff --git a/bitwarden_license/bit-web/tsconfig.build.json b/bitwarden_license/bit-web/tsconfig.build.json index 9bebbeb5061..6313ce27863 100644 --- a/bitwarden_license/bit-web/tsconfig.build.json +++ b/bitwarden_license/bit-web/tsconfig.build.json @@ -9,6 +9,6 @@ ], "include": [ "../../apps/web/src/connectors/*.ts", - "../../libs/common/src/platform/services/**/*.worker.ts" + "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts" ] } diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json index a2f9c4608c1..1c9a530d273 100644 --- a/bitwarden_license/bit-web/tsconfig.json +++ b/bitwarden_license/bit-web/tsconfig.json @@ -21,7 +21,7 @@ "../../libs/tools/export/vault-export/vault-export-core/src" ], "@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"], - "@bitwarden/importer/core": ["../../libs/importer/src"], + "@bitwarden/importer-core": ["../../libs/importer/src"], "@bitwarden/importer/ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], @@ -46,7 +46,7 @@ "../../apps/web/src/connectors/*.ts", "../../apps/web/src/**/*.stories.ts", "../../apps/web/src/**/*.spec.ts", - "../../libs/common/src/platform/services/**/*.worker.ts", + "../../libs/common/src/key-management/crypto/services/encrypt.worker.ts", "src/**/*.stories.ts", "src/**/*.spec.ts" diff --git a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts index 6aafbaf4678..890353d9039 100644 --- a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { KeyService } from "@bitwarden/key-management"; diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts index a230a20b2e3..7fe81ade4d2 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts @@ -1,7 +1,7 @@ import { mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.ts b/libs/admin-console/src/common/collections/services/default-collection.service.ts index 4070c92f27c..da50a25886e 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.ts @@ -3,7 +3,7 @@ import { combineLatest, firstValueFrom, map, Observable, of, switchMap } from "rxjs"; import { Jsonify } from "type-fest"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts index 048a4733948..9700fcb695a 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts @@ -1,7 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { first, firstValueFrom, of, ReplaySubject, takeWhile } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts index 2d5a083592b..0ef8ae99ab3 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { combineLatest, filter, firstValueFrom, map } from "rxjs"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { StateProvider, DerivedState } from "@bitwarden/common/platform/state"; diff --git a/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts b/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts index ca3906cead3..32396c878d9 100644 --- a/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts +++ b/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts @@ -195,7 +195,7 @@ export class BaseLoginDecryptionOptionsComponentV1 implements OnInit, OnDestroy async loadNewUserData() { const autoEnrollStatus$ = defer(() => - this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(), + this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(this.activeAccountId), ).pipe( switchMap((organizationIdentifier) => { if (organizationIdentifier == undefined) { diff --git a/libs/angular/src/auth/components/set-password.component.ts b/libs/angular/src/auth/components/set-password.component.ts index 13d1a4f4131..de079a7ebca 100644 --- a/libs/angular/src/auth/components/set-password.component.ts +++ b/libs/angular/src/auth/components/set-password.component.ts @@ -21,8 +21,8 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -47,7 +47,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements resetPasswordAutoEnroll = false; onSuccessfulChangePassword: () => Promise; successRoute = "vault"; - userId: UserId; + activeUserId: UserId; forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None; ForceSetPasswordReason = ForceSetPasswordReason; @@ -96,10 +96,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements await this.syncService.fullSync(true); this.syncLoading = false; - this.userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + this.activeUserId = (await firstValueFrom(this.accountService.activeAccount$))?.id; this.forceSetPasswordReason = await firstValueFrom( - this.masterPasswordService.forceSetPasswordReason$(this.userId), + this.masterPasswordService.forceSetPasswordReason$(this.activeUserId), ); this.route.queryParams @@ -111,7 +111,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements } else { // Try to get orgSsoId from state as fallback // Note: this is primarily for the TDE user w/out MP obtains admin MP reset permission scenario. - return this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(); + return this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(this.activeUserId); } }), filter((orgSsoId) => orgSsoId != null), @@ -167,10 +167,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements // in case we have a local private key, and are not sure whether it has been posted to the server, we post the local private key instead of generating a new one const existingUserPrivateKey = (await firstValueFrom( - this.keyService.userPrivateKey$(this.userId), + this.keyService.userPrivateKey$(this.activeUserId), )) as Uint8Array; const existingUserPublicKey = await firstValueFrom( - this.keyService.userPublicKey$(this.userId), + this.keyService.userPublicKey$(this.activeUserId), ); if (existingUserPrivateKey != null && existingUserPublicKey != null) { const existingUserPublicKeyB64 = Utils.fromBufferToB64(existingUserPublicKey); @@ -217,7 +217,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements return this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment( this.orgId, - this.userId, + this.activeUserId, resetRequest, ); }); @@ -260,7 +260,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements // Clear force set password reason to allow navigation back to vault. await this.masterPasswordService.setForceSetPasswordReason( ForceSetPasswordReason.None, - this.userId, + this.activeUserId, ); // User now has a password so update account decryption options in state @@ -269,9 +269,9 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements ); userDecryptionOpts.hasMasterPassword = true; await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts); - await this.kdfConfigService.setKdfConfig(this.userId, this.kdfConfig); - await this.masterPasswordService.setMasterKey(masterKey, this.userId); - await this.keyService.setUserKey(userKey[0], this.userId); + await this.kdfConfigService.setKdfConfig(this.activeUserId, this.kdfConfig); + await this.masterPasswordService.setMasterKey(masterKey, this.activeUserId); + await this.keyService.setUserKey(userKey[0], this.activeUserId); // Set private key only for new JIT provisioned users in MP encryption orgs // Existing TDE users will have private key set on sync or on login @@ -280,7 +280,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements this.forceSetPasswordReason != ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission ) { - await this.keyService.setPrivateKey(keyPair[1].encryptedString, this.userId); + await this.keyService.setPrivateKey(keyPair[1].encryptedString, this.activeUserId); } const localMasterKeyHash = await this.keyService.hashMasterKey( @@ -288,6 +288,6 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements masterKey, HashPurpose.LocalAuthorization, ); - await this.masterPasswordService.setMasterKeyHash(localMasterKeyHash, this.userId); + await this.masterPasswordService.setMasterKeyHash(localMasterKeyHash, this.activeUserId); } } diff --git a/libs/angular/src/auth/components/sso.component.ts b/libs/angular/src/auth/components/sso.component.ts index 6c13809566a..d0fc2140f06 100644 --- a/libs/angular/src/auth/components/sso.component.ts +++ b/libs/angular/src/auth/components/sso.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Directive, OnInit } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, NavigationExtras, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; import { first } from "rxjs/operators"; @@ -27,6 +28,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { UserId } from "@bitwarden/common/types/guid"; import { ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; @@ -55,6 +57,7 @@ export class SsoComponent implements OnInit { protected redirectUri: string; protected state: string; protected codeChallenge: string; + protected activeUserId: UserId; constructor( protected ssoLoginService: SsoLoginServiceAbstraction, @@ -74,7 +77,11 @@ export class SsoComponent implements OnInit { protected masterPasswordService: InternalMasterPasswordServiceAbstraction, protected accountService: AccountService, protected toastService: ToastService, - ) {} + ) { + this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { + this.activeUserId = account?.id; + }); + } async ngOnInit() { // eslint-disable-next-line rxjs/no-async-subscribe @@ -226,7 +233,10 @@ export class SsoComponent implements OnInit { // - TDE login decryption options component // - Browser SSO on extension open // Note: you cannot set this in state before 2FA b/c there won't be an account in state. - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(orgSsoIdentifier); + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( + orgSsoIdentifier, + this.activeUserId, + ); // Users enrolled in admin acct recovery can be forced to set a new password after // having the admin set a temp password for them (affects TDE & standard users) diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html index 8462a18ac2e..087ecd2764e 100644 --- a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.html @@ -69,7 +69,7 @@
diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts index 6aca189a79e..6afee461c42 100644 --- a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, Inject, OnInit, ViewChild } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { ActivatedRoute, NavigationExtras, Router, RouterLink } from "@angular/router"; import { Subject, takeUntil, lastValueFrom, first, firstValueFrom } from "rxjs"; @@ -31,6 +32,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, ButtonModule, @@ -126,6 +128,7 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements protected changePasswordRoute = "set-password"; protected forcePasswordResetRoute = "update-temp-password"; protected successRoute = "vault"; + protected activeUserId: UserId; constructor( protected loginStrategyService: LoginStrategyServiceAbstraction, @@ -148,6 +151,10 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements protected toastService: ToastService, ) { super(environmentService, i18nService, platformUtilsService, toastService); + + this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { + this.activeUserId = account?.id; + }); } async ngOnInit() { @@ -214,7 +221,7 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements } } - async selectOtherTwofactorMethod() { + async selectOtherTwoFactorMethod() { const dialogRef = TwoFactorOptionsComponent.open(this.dialogService); const response: TwoFactorOptionsDialogResultType = await lastValueFrom(dialogRef.closed); if (response.result === TwoFactorOptionsDialogResult.Provider) { @@ -262,7 +269,10 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements // Save off the OrgSsoIdentifier for use in the TDE flows // - TDE login decryption options component // - Browser SSO on extension open - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(this.orgIdentifier); + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( + this.orgIdentifier, + this.activeUserId, + ); this.loginEmailService.clearValues(); // note: this flow affects both TDE & standard users diff --git a/libs/angular/src/auth/components/two-factor.component.ts b/libs/angular/src/auth/components/two-factor.component.ts index 3b3459f42fb..49af9d057f7 100644 --- a/libs/angular/src/auth/components/two-factor.component.ts +++ b/libs/angular/src/auth/components/two-factor.component.ts @@ -35,6 +35,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { ToastService } from "@bitwarden/components"; import { CaptchaProtectedComponent } from "./captcha-protected.component"; @@ -73,6 +74,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI protected successRoute = "vault"; protected twoFactorTimeoutRoute = "authentication-timeout"; + protected activeUserId: UserId; + get isDuoProvider(): boolean { return ( this.selectedProviderType === TwoFactorProviderType.Duo || @@ -102,8 +105,13 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI protected toastService: ToastService, ) { super(environmentService, i18nService, platformUtilsService, toastService); + this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win); + this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { + this.activeUserId = account?.id; + }); + // Add subscription to authenticationSessionTimeout$ and navigate to twoFactorTimeoutRoute if expired this.loginStrategyService.authenticationSessionTimeout$ .pipe(takeUntilDestroyed()) @@ -287,7 +295,10 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI // Save off the OrgSsoIdentifier for use in the TDE flows // - TDE login decryption options component // - Browser SSO on extension open - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(this.orgIdentifier); + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( + this.orgIdentifier, + this.activeUserId, + ); this.loginEmailService.clearValues(); // note: this flow affects both TDE & standard users diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 36082f879b9..719e3a084f1 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -147,13 +147,15 @@ import { BillingApiService } from "@bitwarden/common/billing/services/billing-ap import { OrganizationBillingApiService } from "@bitwarden/common/billing/services/organization/organization-billing-api.service"; import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service"; import { TaxService } from "@bitwarden/common/billing/services/tax.service"; +import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/bulk-encrypt.service.implementation"; +import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService, RegionConfig, @@ -194,8 +196,6 @@ import { AppIdService } from "@bitwarden/common/platform/services/app-id.service import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; -import { BulkEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/bulk-encrypt.service.implementation"; -import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { DefaultBroadcasterService } from "@bitwarden/common/platform/services/default-broadcaster.service"; import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; import { DefaultServerSettingsService } from "@bitwarden/common/platform/services/default-server-settings.service"; @@ -296,7 +296,7 @@ import { DefaultUserAsymmetricKeysRegenerationApiService, } from "@bitwarden/key-management"; import { SafeInjectionToken } from "@bitwarden/ui-common"; -import { PasswordRepromptService } from "@bitwarden/vault"; +import { NewDeviceVerificationNoticeService, PasswordRepromptService } from "@bitwarden/vault"; import { VaultExportService, VaultExportServiceAbstraction, @@ -306,9 +306,6 @@ import { IndividualVaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { NewDeviceVerificationNoticeService } from "../../../vault/src/services/new-device-verification-notice.service"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; import { ViewCacheService } from "../platform/abstractions/view-cache.service"; import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service"; @@ -799,7 +796,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SsoLoginServiceAbstraction, useClass: SsoLoginService, - deps: [StateProvider], + deps: [StateProvider, LogService], }), safeProvider({ provide: STATE_FACTORY, diff --git a/libs/angular/src/vault/components/attachments.component.ts b/libs/angular/src/vault/components/attachments.component.ts index ec3dc43b447..9f1dd31da0c 100644 --- a/libs/angular/src/vault/components/attachments.component.ts +++ b/libs/angular/src/vault/components/attachments.component.ts @@ -6,8 +6,8 @@ import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index abbedf13078..227bc14f1b1 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -20,9 +20,9 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType } from "@bitwarden/common/enums"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts b/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts index 7ff3f567a00..960590dab53 100644 --- a/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts +++ b/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts @@ -8,10 +8,8 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { NewDeviceVerificationNoticeService } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { NewDeviceVerificationNoticeService } from "../../../../vault/src/services/new-device-verification-notice.service"; import { VaultProfileService } from "../services/vault-profile.service"; import { NewDeviceVerificationNoticeGuard } from "./new-device-verification-notice.guard"; diff --git a/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts index 2a21279ec3b..09d6b3313c4 100644 --- a/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts +++ b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts @@ -8,10 +8,8 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { NewDeviceVerificationNoticeService } from "@bitwarden/vault"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { NewDeviceVerificationNoticeService } from "../../../../vault/src/services/new-device-verification-notice.service"; import { VaultProfileService } from "../services/vault-profile.service"; export const NewDeviceVerificationNoticeGuard: CanActivateFn = async ( diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts index 96fb74ba96b..aab06a69add 100644 --- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts +++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts @@ -16,14 +16,11 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; +import { COLLAPSED_GROUPINGS } from "@bitwarden/common/vault/services/key-state/collapsed-groupings.state"; import { DeprecatedVaultFilterService as DeprecatedVaultFilterServiceAbstraction } from "../../abstractions/deprecated-vault-filter.service"; import { DynamicTreeNode } from "../models/dynamic-tree-node.model"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { COLLAPSED_GROUPINGS } from "./../../../../../common/src/vault/services/key-state/collapsed-groupings.state"; - const NestingDelimiter = "/"; @Injectable() diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index a3f5e062e4f..4c93c79d6fe 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -202,7 +202,7 @@ export class LoginDecryptionOptionsComponent implements OnInit { }); const autoEnrollStatus$ = defer(() => - this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(), + this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(this.activeAccountId), ).pipe( switchMap((organizationIdentifier) => { if (organizationIdentifier == undefined) { diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts index 7a2b334eb42..726110663fc 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts @@ -11,8 +11,8 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso import { OrganizationKeysResponse } from "@bitwarden/common/admin-console/models/response/organization-keys.response"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts index 84c580662be..6c9ce8f9267 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts @@ -12,8 +12,8 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index 4583332cb88..b4373bfe96e 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -36,6 +36,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { UserId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, ButtonModule, @@ -89,6 +90,7 @@ export class SsoComponent implements OnInit { protected state: string | undefined; protected codeChallenge: string | undefined; protected clientId: SsoClientType | undefined; + protected activeUserId: UserId | undefined; formPromise: Promise | undefined; initiateSsoFormPromise: Promise | undefined; @@ -130,6 +132,8 @@ export class SsoComponent implements OnInit { } async ngOnInit() { + this.activeUserId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + const qParams: QueryParams = await firstValueFrom(this.route.queryParams); // This if statement will pass on the second portion of the SSO flow @@ -384,7 +388,10 @@ export class SsoComponent implements OnInit { // - TDE login decryption options component // - Browser SSO on extension open // Note: you cannot set this in state before 2FA b/c there won't be an account in state. - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(orgSsoIdentifier); + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( + orgSsoIdentifier, + this.activeUserId, + ); // Users enrolled in admin acct recovery can be forced to set a new password after // having the admin set a temp password for them (affects TDE & standard users) diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts index cec4481cd8d..7c56e2a58c8 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts @@ -9,8 +9,8 @@ import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/id import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index a8208a1e0ad..fbd6b79f19d 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -21,8 +21,8 @@ import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/mod import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts index 6b1dcfb155c..b4eef1a0276 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.ts @@ -22,9 +22,9 @@ import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/respons import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts index d572710a2fd..1b0613d4da3 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts @@ -13,8 +13,8 @@ import { MasterPasswordPolicyResponse } from "@bitwarden/common/auth/models/resp import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts index ec3ec43134f..96b08e98e37 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts @@ -13,9 +13,9 @@ import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/mod import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts index 2bb41faa0e1..dd3e7f0134d 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts @@ -8,8 +8,8 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Environment, EnvironmentService, diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts index 9dacce2cf00..fd8817a8c21 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts @@ -11,8 +11,8 @@ import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/maste import { WebAuthnLoginAssertionResponseRequest } from "@bitwarden/common/auth/services/webauthn-login/request/webauthn-login-assertion-response.request"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 86b2a1dd3b6..2ea6d427641 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -3,9 +3,9 @@ import { mock } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.ts b/libs/auth/src/common/services/auth-request/auth-request.service.ts index 4bc0397b43a..5bc200ae1e8 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -9,9 +9,9 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable"; import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts index 3b03e8754bc..117e5c1f864 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts @@ -17,8 +17,8 @@ import { PreloginResponse } from "@bitwarden/common/auth/models/response/prelogi import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index e3a20fcfe72..849b8e5eba1 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -22,10 +22,10 @@ import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication- import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/auth/src/common/services/pin/pin.service.implementation.ts b/libs/auth/src/common/services/pin/pin.service.implementation.ts index 01fc77e4a03..9b86440b364 100644 --- a/libs/auth/src/common/services/pin/pin.service.implementation.ts +++ b/libs/auth/src/common/services/pin/pin.service.implementation.ts @@ -4,8 +4,8 @@ import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; diff --git a/libs/auth/src/common/services/pin/pin.service.spec.ts b/libs/auth/src/common/services/pin/pin.service.spec.ts index d254be4e875..1d6443535bc 100644 --- a/libs/auth/src/common/services/pin/pin.service.spec.ts +++ b/libs/auth/src/common/services/pin/pin.service.spec.ts @@ -1,8 +1,8 @@ import { mock } from "jest-mock-extended"; import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; diff --git a/libs/common/src/admin-console/abstractions/provider/provider-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/provider/provider-api.service.abstraction.ts index f348e7487de..ffe79f0ad3b 100644 --- a/libs/common/src/admin-console/abstractions/provider/provider-api.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/provider/provider-api.service.abstraction.ts @@ -1,5 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response"; + import { ProviderSetupRequest } from "../../models/request/provider/provider-setup.request"; import { ProviderUpdateRequest } from "../../models/request/provider/provider-update.request"; import { ProviderVerifyRecoverDeleteRequest } from "../../models/request/provider/provider-verify-recover-delete.request"; @@ -14,4 +16,12 @@ export class ProviderApiServiceAbstraction { request: ProviderVerifyRecoverDeleteRequest, ) => Promise; deleteProvider: (id: string) => Promise; + getProviderAddableOrganizations: (providerId: string) => Promise; + addOrganizationToProvider: ( + providerId: string, + request: { + key: string; + organizationId: string; + }, + ) => Promise; } diff --git a/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts b/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts index 1f8c4e8c42d..5f8fe3349b6 100644 --- a/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts +++ b/libs/common/src/admin-console/models/domain/encrypted-organization-key.ts @@ -1,4 +1,4 @@ -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { OrgKey, UserPrivateKey } from "../../../types/key"; diff --git a/libs/common/src/admin-console/models/response/addable-organization.response.ts b/libs/common/src/admin-console/models/response/addable-organization.response.ts new file mode 100644 index 00000000000..74ae5f45690 --- /dev/null +++ b/libs/common/src/admin-console/models/response/addable-organization.response.ts @@ -0,0 +1,18 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class AddableOrganizationResponse extends BaseResponse { + id: string; + plan: string; + name: string; + seats: number; + disabled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("id"); + this.plan = this.getResponseProperty("plan"); + this.name = this.getResponseProperty("name"); + this.seats = this.getResponseProperty("seats"); + this.disabled = this.getResponseProperty("disabled"); + } +} diff --git a/libs/common/src/admin-console/services/provider/provider-api.service.ts b/libs/common/src/admin-console/services/provider/provider-api.service.ts index 2ee921393ff..dc82ec011f4 100644 --- a/libs/common/src/admin-console/services/provider/provider-api.service.ts +++ b/libs/common/src/admin-console/services/provider/provider-api.service.ts @@ -1,3 +1,5 @@ +import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response"; + import { ApiService } from "../../../abstractions/api.service"; import { ProviderApiServiceAbstraction } from "../../abstractions/provider/provider-api.service.abstraction"; import { ProviderSetupRequest } from "../../models/request/provider/provider-setup.request"; @@ -44,4 +46,34 @@ export class ProviderApiService implements ProviderApiServiceAbstraction { async deleteProvider(id: string): Promise { await this.apiService.send("DELETE", "/providers/" + id, null, true, false); } + + async getProviderAddableOrganizations( + providerId: string, + ): Promise { + const response = await this.apiService.send( + "GET", + "/providers/" + providerId + "/clients/addable", + null, + true, + true, + ); + + return response.map((data: any) => new AddableOrganizationResponse(data)); + } + + addOrganizationToProvider( + providerId: string, + request: { + key: string; + organizationId: string; + }, + ): Promise { + return this.apiService.send( + "POST", + "/providers/" + providerId + "/clients/existing", + request, + true, + false, + ); + } } diff --git a/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts b/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts index 3f3731e0e1b..bf64dcafd69 100644 --- a/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts +++ b/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts @@ -1,5 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { UserId } from "@bitwarden/common/types/guid"; + export abstract class SsoLoginServiceAbstraction { /** * Gets the code verifier used for SSO. @@ -74,12 +76,16 @@ export abstract class SsoLoginServiceAbstraction { * Gets the value of the active user's organization sso identifier. * * This should only be used post successful SSO login once the user is initialized. + * @param userId The user id for retrieving the org identifier state. */ - getActiveUserOrganizationSsoIdentifier: () => Promise; + getActiveUserOrganizationSsoIdentifier: (userId: UserId) => Promise; /** * Sets the value of the active user's organization sso identifier. * * This should only be used post successful SSO login once the user is initialized. */ - setActiveUserOrganizationSsoIdentifier: (organizationIdentifier: string) => Promise; + setActiveUserOrganizationSsoIdentifier: ( + organizationIdentifier: string, + userId: UserId | undefined, + ) => Promise; } diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts index 15c12b7a39a..903c72d4211 100644 --- a/libs/common/src/auth/services/device-trust.service.implementation.ts +++ b/libs/common/src/auth/services/device-trust.service.implementation.ts @@ -7,10 +7,10 @@ import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common" // FIXME: remove `src` and fix import // eslint-disable-next-line no-restricted-imports import { KeyService } from "../../../../key-management/src/abstractions/key.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "../../platform/abstractions/app-id.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; diff --git a/libs/common/src/auth/services/device-trust.service.spec.ts b/libs/common/src/auth/services/device-trust.service.spec.ts index 9f344e203c9..06da1396074 100644 --- a/libs/common/src/auth/services/device-trust.service.spec.ts +++ b/libs/common/src/auth/services/device-trust.service.spec.ts @@ -15,10 +15,10 @@ import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-a import { FakeActiveUserState } from "../../../spec/fake-state"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; import { DeviceType } from "../../enums"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { AppIdService } from "../../platform/abstractions/app-id.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; diff --git a/libs/common/src/auth/services/master-password/master-password.service.ts b/libs/common/src/auth/services/master-password/master-password.service.ts index 14e7522a836..9b5ce588bd3 100644 --- a/libs/common/src/auth/services/master-password/master-password.service.ts +++ b/libs/common/src/auth/services/master-password/master-password.service.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { firstValueFrom, map, Observable } from "rxjs"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; import { LogService } from "../../../platform/abstractions/log.service"; import { StateService } from "../../../platform/abstractions/state.service"; diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts index ddd24ae7907..8953d14c8e6 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts @@ -8,7 +8,7 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationAutoEnrollStatusResponse } from "../../admin-console/models/response/organization-auto-enroll-status.response"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { UserId } from "../../types/guid"; import { Account, AccountInfo, AccountService } from "../abstractions/account.service"; diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts index 22d5384e6ac..c0a961d5bbb 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts @@ -11,7 +11,7 @@ import { // eslint-disable-next-line no-restricted-imports import { KeyService } from "../../../../key-management/src/abstractions/key.service"; import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { Utils } from "../../platform/misc/utils"; import { UserKey } from "../../types/key"; diff --git a/libs/common/src/auth/services/sso-login.service.spec.ts b/libs/common/src/auth/services/sso-login.service.spec.ts new file mode 100644 index 00000000000..9cf49a07834 --- /dev/null +++ b/libs/common/src/auth/services/sso-login.service.spec.ts @@ -0,0 +1,94 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { + CODE_VERIFIER, + GLOBAL_ORGANIZATION_SSO_IDENTIFIER, + SSO_EMAIL, + SSO_STATE, + SsoLoginService, + USER_ORGANIZATION_SSO_IDENTIFIER, +} from "@bitwarden/common/auth/services/sso-login.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec"; + +describe("SSOLoginService ", () => { + let sut: SsoLoginService; + + let accountService: FakeAccountService; + let mockSingleUserStateProvider: FakeStateProvider; + let mockLogService: MockProxy; + let userId: UserId; + + beforeEach(() => { + jest.clearAllMocks(); + + userId = Utils.newGuid() as UserId; + accountService = mockAccountServiceWith(userId); + mockSingleUserStateProvider = new FakeStateProvider(accountService); + mockLogService = mock(); + + sut = new SsoLoginService(mockSingleUserStateProvider, mockLogService); + }); + + it("instantiates", () => { + expect(sut).not.toBeFalsy(); + }); + + it("gets and sets code verifier", async () => { + const codeVerifier = "test-code-verifier"; + await sut.setCodeVerifier(codeVerifier); + mockSingleUserStateProvider.getGlobal(CODE_VERIFIER); + + const result = await sut.getCodeVerifier(); + expect(result).toBe(codeVerifier); + }); + + it("gets and sets SSO state", async () => { + const ssoState = "test-sso-state"; + await sut.setSsoState(ssoState); + mockSingleUserStateProvider.getGlobal(SSO_STATE); + + const result = await sut.getSsoState(); + expect(result).toBe(ssoState); + }); + + it("gets and sets organization SSO identifier", async () => { + const orgIdentifier = "test-org-identifier"; + await sut.setOrganizationSsoIdentifier(orgIdentifier); + mockSingleUserStateProvider.getGlobal(GLOBAL_ORGANIZATION_SSO_IDENTIFIER); + + const result = await sut.getOrganizationSsoIdentifier(); + expect(result).toBe(orgIdentifier); + }); + + it("gets and sets SSO email", async () => { + const email = "test@example.com"; + await sut.setSsoEmail(email); + mockSingleUserStateProvider.getGlobal(SSO_EMAIL); + + const result = await sut.getSsoEmail(); + expect(result).toBe(email); + }); + + it("gets and sets active user organization SSO identifier", async () => { + const userId = Utils.newGuid() as UserId; + const orgIdentifier = "test-active-org-identifier"; + await sut.setActiveUserOrganizationSsoIdentifier(orgIdentifier, userId); + mockSingleUserStateProvider.getUser(userId, USER_ORGANIZATION_SSO_IDENTIFIER); + + const result = await sut.getActiveUserOrganizationSsoIdentifier(userId); + expect(result).toBe(orgIdentifier); + }); + + it("logs error when setting active user organization SSO identifier with undefined userId", async () => { + const orgIdentifier = "test-active-org-identifier"; + await sut.setActiveUserOrganizationSsoIdentifier(orgIdentifier, undefined); + + expect(mockLogService.warning).toHaveBeenCalledWith( + "Tried to set a user organization sso identifier with an undefined user id.", + ); + }); +}); diff --git a/libs/common/src/auth/services/sso-login.service.ts b/libs/common/src/auth/services/sso-login.service.ts index 32019e8d568..c73be3630be 100644 --- a/libs/common/src/auth/services/sso-login.service.ts +++ b/libs/common/src/auth/services/sso-login.service.ts @@ -2,10 +2,13 @@ // @ts-strict-ignore import { firstValueFrom } from "rxjs"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { UserId } from "@bitwarden/common/types/guid"; + import { - ActiveUserState, GlobalState, KeyDefinition, + SingleUserState, SSO_DISK, StateProvider, UserKeyDefinition, @@ -15,21 +18,21 @@ import { SsoLoginServiceAbstraction } from "../abstractions/sso-login.service.ab /** * Uses disk storage so that the code verifier can be persisted across sso redirects. */ -const CODE_VERIFIER = new KeyDefinition(SSO_DISK, "ssoCodeVerifier", { +export const CODE_VERIFIER = new KeyDefinition(SSO_DISK, "ssoCodeVerifier", { deserializer: (codeVerifier) => codeVerifier, }); /** * Uses disk storage so that the sso state can be persisted across sso redirects. */ -const SSO_STATE = new KeyDefinition(SSO_DISK, "ssoState", { +export const SSO_STATE = new KeyDefinition(SSO_DISK, "ssoState", { deserializer: (state) => state, }); /** * Uses disk storage so that the organization sso identifier can be persisted across sso redirects. */ -const USER_ORGANIZATION_SSO_IDENTIFIER = new UserKeyDefinition( +export const USER_ORGANIZATION_SSO_IDENTIFIER = new UserKeyDefinition( SSO_DISK, "organizationSsoIdentifier", { @@ -41,7 +44,7 @@ const USER_ORGANIZATION_SSO_IDENTIFIER = new UserKeyDefinition( /** * Uses disk storage so that the organization sso identifier can be persisted across sso redirects. */ -const GLOBAL_ORGANIZATION_SSO_IDENTIFIER = new KeyDefinition( +export const GLOBAL_ORGANIZATION_SSO_IDENTIFIER = new KeyDefinition( SSO_DISK, "organizationSsoIdentifier", { @@ -52,7 +55,7 @@ const GLOBAL_ORGANIZATION_SSO_IDENTIFIER = new KeyDefinition( /** * Uses disk storage so that the user's email can be persisted across sso redirects. */ -const SSO_EMAIL = new KeyDefinition(SSO_DISK, "ssoEmail", { +export const SSO_EMAIL = new KeyDefinition(SSO_DISK, "ssoEmail", { deserializer: (state) => state, }); @@ -61,16 +64,15 @@ export class SsoLoginService implements SsoLoginServiceAbstraction { private ssoState: GlobalState; private orgSsoIdentifierState: GlobalState; private ssoEmailState: GlobalState; - private activeUserOrgSsoIdentifierState: ActiveUserState; - constructor(private stateProvider: StateProvider) { + constructor( + private stateProvider: StateProvider, + private logService: LogService, + ) { this.codeVerifierState = this.stateProvider.getGlobal(CODE_VERIFIER); this.ssoState = this.stateProvider.getGlobal(SSO_STATE); this.orgSsoIdentifierState = this.stateProvider.getGlobal(GLOBAL_ORGANIZATION_SSO_IDENTIFIER); this.ssoEmailState = this.stateProvider.getGlobal(SSO_EMAIL); - this.activeUserOrgSsoIdentifierState = this.stateProvider.getActive( - USER_ORGANIZATION_SSO_IDENTIFIER, - ); } getCodeVerifier(): Promise { @@ -105,11 +107,24 @@ export class SsoLoginService implements SsoLoginServiceAbstraction { await this.ssoEmailState.update((_) => email); } - getActiveUserOrganizationSsoIdentifier(): Promise { - return firstValueFrom(this.activeUserOrgSsoIdentifierState.state$); + getActiveUserOrganizationSsoIdentifier(userId: UserId): Promise { + return firstValueFrom(this.userOrgSsoIdentifierState(userId).state$); } - async setActiveUserOrganizationSsoIdentifier(organizationIdentifier: string): Promise { - await this.activeUserOrgSsoIdentifierState.update((_) => organizationIdentifier); + async setActiveUserOrganizationSsoIdentifier( + organizationIdentifier: string, + userId: UserId | undefined, + ): Promise { + if (userId === undefined) { + this.logService.warning( + "Tried to set a user organization sso identifier with an undefined user id.", + ); + return; + } + await this.userOrgSsoIdentifierState(userId).update((_) => organizationIdentifier); + } + + private userOrgSsoIdentifierState(userId: UserId): SingleUserState { + return this.stateProvider.getUser(userId, USER_ORGANIZATION_SSO_IDENTIFIER); } } diff --git a/libs/common/src/auth/services/token.service.spec.ts b/libs/common/src/auth/services/token.service.spec.ts index f8882e1b118..339f570a003 100644 --- a/libs/common/src/auth/services/token.service.spec.ts +++ b/libs/common/src/auth/services/token.service.spec.ts @@ -5,7 +5,7 @@ import { LogoutReason } from "@bitwarden/auth/common"; import { FakeSingleUserStateProvider, FakeGlobalStateProvider } from "../../../spec"; import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; import { AbstractStorageService } from "../../platform/abstractions/storage.service"; diff --git a/libs/common/src/auth/services/token.service.ts b/libs/common/src/auth/services/token.service.ts index 4b7cc2cab01..72e082f2002 100644 --- a/libs/common/src/auth/services/token.service.ts +++ b/libs/common/src/auth/services/token.service.ts @@ -6,7 +6,7 @@ import { Opaque } from "type-fest"; import { LogoutReason, decodeJwtTokenToJson } from "@bitwarden/auth/common"; import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; import { AbstractStorageService } from "../../platform/abstractions/storage.service"; diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index e61b092d677..83efbf0a30c 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -7,7 +7,7 @@ import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../ import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request"; import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request"; import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { SyncService } from "../../platform/sync"; diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 613572bb75b..550a8c07ff7 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -24,8 +24,12 @@ export enum FeatureFlag { NotificationRefresh = "notification-refresh", UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection", + /* Tools */ ItemShare = "item-share", GeneratorToolsModernization = "generator-tools-modernization", + CriticalApps = "pm-14466-risk-insights-critical-application", + EnableRiskInsightsNotifications = "enable-risk-insights-notifications", + AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section", ExtensionRefresh = "extension-refresh", PersistPopupView = "persist-popup-view", @@ -37,18 +41,17 @@ export enum FeatureFlag { SSHAgent = "ssh-agent", CipherKeyEncryption = "cipher-key-encryption", PM11901_RefactorSelfHostingLicenseUploader = "PM-11901-refactor-self-hosting-license-uploader", - CriticalApps = "pm-14466-risk-insights-critical-application", TrialPaymentOptional = "PM-8163-trial-payment", SecurityTasks = "security-tasks", NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss", NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss", - DisableFreeFamiliesSponsorship = "PM-12274-disable-free-families-sponsorship", MacOsNativeCredentialSync = "macos-native-credential-sync", PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form", PrivateKeyRegeneration = "pm-12241-private-key-regeneration", ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", + AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner", NewDeviceVerification = "new-device-verification", - EnableRiskInsightsNotifications = "enable-risk-insights-notifications", + PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -83,8 +86,12 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.NotificationRefresh]: FALSE, [FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE, + /* Tools */ [FeatureFlag.ItemShare]: FALSE, [FeatureFlag.GeneratorToolsModernization]: FALSE, + [FeatureFlag.CriticalApps]: FALSE, + [FeatureFlag.EnableRiskInsightsNotifications]: FALSE, + [FeatureFlag.AC1795_UpdatedSubscriptionStatusSection]: FALSE, [FeatureFlag.ExtensionRefresh]: FALSE, [FeatureFlag.PersistPopupView]: FALSE, @@ -96,18 +103,17 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.SSHAgent]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE, - [FeatureFlag.CriticalApps]: FALSE, [FeatureFlag.TrialPaymentOptional]: FALSE, [FeatureFlag.SecurityTasks]: FALSE, [FeatureFlag.NewDeviceVerificationTemporaryDismiss]: FALSE, [FeatureFlag.NewDeviceVerificationPermanentDismiss]: FALSE, - [FeatureFlag.DisableFreeFamiliesSponsorship]: FALSE, [FeatureFlag.MacOsNativeCredentialSync]: FALSE, [FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE, [FeatureFlag.PrivateKeyRegeneration]: FALSE, [FeatureFlag.ResellerManagedOrgAlert]: FALSE, + [FeatureFlag.AccountDeprovisioningBanner]: FALSE, [FeatureFlag.NewDeviceVerification]: FALSE, - [FeatureFlag.EnableRiskInsightsNotifications]: FALSE, + [FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; diff --git a/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts new file mode 100644 index 00000000000..3e47ccdb5f2 --- /dev/null +++ b/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts @@ -0,0 +1,10 @@ +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; + +export abstract class BulkEncryptService { + abstract decryptItems( + items: Decryptable[], + key: SymmetricCryptoKey, + ): Promise; +} diff --git a/libs/common/src/platform/abstractions/encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts similarity index 82% rename from libs/common/src/platform/abstractions/encrypt.service.ts rename to libs/common/src/key-management/crypto/abstractions/encrypt.service.ts index a660524699d..e00d053ce7b 100644 --- a/libs/common/src/platform/abstractions/encrypt.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts @@ -1,9 +1,9 @@ -import { Decryptable } from "../interfaces/decryptable.interface"; -import { Encrypted } from "../interfaces/encrypted"; -import { InitializerMetadata } from "../interfaces/initializer-metadata.interface"; -import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; -import { EncString } from "../models/domain/enc-string"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { Encrypted } from "@bitwarden/common/platform/interfaces/encrypted"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; export abstract class EncryptService { abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise; diff --git a/libs/common/src/platform/services/cryptography/bulk-encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts similarity index 84% rename from libs/common/src/platform/services/cryptography/bulk-encrypt.service.implementation.ts rename to libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts index 1320fbae0e0..1d1e0f52279 100644 --- a/libs/common/src/platform/services/cryptography/bulk-encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts @@ -3,15 +3,14 @@ import { firstValueFrom, fromEvent, filter, map, takeUntil, defaultIfEmpty, Subject } from "rxjs"; import { Jsonify } from "type-fest"; -import { BulkEncryptService } from "../../abstractions/bulk-encrypt.service"; -import { CryptoFunctionService } from "../../abstractions/crypto-function.service"; -import { LogService } from "../../abstractions/log.service"; -import { Decryptable } from "../../interfaces/decryptable.interface"; -import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface"; -import { Utils } from "../../misc/utils"; -import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key"; - -import { getClassInitializer } from "./get-class-initializer"; +import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; // TTL (time to live) is not strictly required but avoids tying up memory resources if inactive const workerTTL = 60000; // 1 minute @@ -88,7 +87,7 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService { new Worker( new URL( /* webpackChunkName: 'encrypt-worker' */ - "@bitwarden/common/platform/services/cryptography/encrypt.worker.ts", + "@bitwarden/common/key-management/crypto/services/encrypt.worker.ts", import.meta.url, ), ), diff --git a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts similarity index 90% rename from libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts rename to libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts index 68263cadf27..075b9da4964 100644 --- a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts @@ -1,17 +1,21 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Utils } from "../../../platform/misc/utils"; -import { CryptoFunctionService } from "../../abstractions/crypto-function.service"; -import { EncryptService } from "../../abstractions/encrypt.service"; -import { LogService } from "../../abstractions/log.service"; -import { EncryptionType, encryptionTypeToString as encryptionTypeName } from "../../enums"; -import { Decryptable } from "../../interfaces/decryptable.interface"; -import { Encrypted } from "../../interfaces/encrypted"; -import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface"; -import { EncArrayBuffer } from "../../models/domain/enc-array-buffer"; -import { EncString } from "../../models/domain/enc-string"; -import { EncryptedObject } from "../../models/domain/encrypted-object"; -import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { + EncryptionType, + encryptionTypeToString as encryptionTypeName, +} from "@bitwarden/common/platform/enums"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { Encrypted } from "@bitwarden/common/platform/interfaces/encrypted"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { EncryptedObject } from "@bitwarden/common/platform/models/domain/encrypted-object"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; + +import { EncryptService } from "../abstractions/encrypt.service"; export class EncryptServiceImplementation implements EncryptService { constructor( diff --git a/libs/common/src/platform/services/encrypt.service.spec.ts b/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts similarity index 91% rename from libs/common/src/platform/services/encrypt.service.spec.ts rename to libs/common/src/key-management/crypto/services/encrypt.service.spec.ts index 609b5100a10..8d75b528596 100644 --- a/libs/common/src/platform/services/encrypt.service.spec.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts @@ -1,15 +1,17 @@ import { mockReset, mock } from "jest-mock-extended"; -import { makeStaticByteArray } from "../../../spec"; -import { CsprngArray } from "../../types/csprng"; -import { CryptoFunctionService } from "../abstractions/crypto-function.service"; -import { LogService } from "../abstractions/log.service"; -import { EncryptionType } from "../enums"; -import { Utils } from "../misc/utils"; -import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; -import { EncString } from "../models/domain/enc-string"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; -import { EncryptServiceImplementation } from "../services/cryptography/encrypt.service.implementation"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { EncryptionType } from "@bitwarden/common/platform/enums"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; + +import { makeStaticByteArray } from "../../../../spec"; + +import { EncryptServiceImplementation } from "./encrypt.service.implementation"; describe("EncryptService", () => { const cryptoFunctionService = mock(); diff --git a/libs/common/src/platform/services/cryptography/encrypt.worker.ts b/libs/common/src/key-management/crypto/services/encrypt.worker.ts similarity index 71% rename from libs/common/src/platform/services/cryptography/encrypt.worker.ts rename to libs/common/src/key-management/crypto/services/encrypt.worker.ts index a293e1c6bb0..84ffcf56934 100644 --- a/libs/common/src/platform/services/cryptography/encrypt.worker.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.worker.ts @@ -2,14 +2,14 @@ // @ts-strict-ignore import { Jsonify } from "type-fest"; -import { Decryptable } from "../../interfaces/decryptable.interface"; -import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key"; -import { ConsoleLogService } from "../console-log.service"; -import { ContainerService } from "../container.service"; -import { WebCryptoFunctionService } from "../web-crypto-function.service"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; +import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; +import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { EncryptServiceImplementation } from "./encrypt.service.implementation"; -import { getClassInitializer } from "./get-class-initializer"; const workerApi: Worker = self as any; diff --git a/libs/common/src/platform/services/cryptography/fallback-bulk-encrypt.service.ts b/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts similarity index 68% rename from libs/common/src/platform/services/cryptography/fallback-bulk-encrypt.service.ts rename to libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts index 7a4fd8f3c1d..80fdd27895d 100644 --- a/libs/common/src/platform/services/cryptography/fallback-bulk-encrypt.service.ts +++ b/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts @@ -1,10 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { BulkEncryptService } from "../../abstractions/bulk-encrypt.service"; -import { EncryptService } from "../../abstractions/encrypt.service"; -import { Decryptable } from "../../interfaces/decryptable.interface"; -import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key"; +import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; + +import { EncryptService } from "../abstractions/encrypt.service"; /** * @deprecated For the feature flag from PM-4154, remove once feature is rolled out diff --git a/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts similarity index 81% rename from libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts rename to libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts index 100dcf152e6..0bf96851563 100644 --- a/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts @@ -3,13 +3,13 @@ import { defaultIfEmpty, filter, firstValueFrom, fromEvent, map, Subject, takeUntil } from "rxjs"; import { Jsonify } from "type-fest"; -import { Utils } from "../../../platform/misc/utils"; -import { Decryptable } from "../../interfaces/decryptable.interface"; -import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key"; +import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; import { EncryptServiceImplementation } from "./encrypt.service.implementation"; -import { getClassInitializer } from "./get-class-initializer"; // TTL (time to live) is not strictly required but avoids tying up memory resources if inactive const workerTTL = 3 * 60000; // 3 minutes @@ -40,7 +40,7 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple this.worker ??= new Worker( new URL( /* webpackChunkName: 'encrypt-worker' */ - "@bitwarden/common/platform/services/cryptography/encrypt.worker.ts", + "@bitwarden/common/key-management/crypto/services/encrypt.worker.ts", import.meta.url, ), ); diff --git a/libs/common/src/platform/abstractions/bulk-encrypt.service.ts b/libs/common/src/platform/abstractions/bulk-encrypt.service.ts deleted file mode 100644 index 4cdff0c769a..00000000000 --- a/libs/common/src/platform/abstractions/bulk-encrypt.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Decryptable } from "../interfaces/decryptable.interface"; -import { InitializerMetadata } from "../interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; - -export abstract class BulkEncryptService { - abstract decryptItems( - items: Decryptable[], - key: SymmetricCryptoKey, - ): Promise; -} diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index f654897e9e2..eaa8f0a813a 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -11,7 +11,7 @@ import { Merge } from "type-fest"; // FIXME: remove `src` and fix import // eslint-disable-next-line no-restricted-imports import { KeyService } from "../../../../key-management/src/abstractions/key.service"; -import { EncryptService } from "../abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../abstractions/i18n.service"; // FIXME: Remove when updating file. Eslint update diff --git a/libs/common/src/platform/models/domain/domain-base.spec.ts b/libs/common/src/platform/models/domain/domain-base.spec.ts index 80a4e5e8606..0c13f9a2119 100644 --- a/libs/common/src/platform/models/domain/domain-base.spec.ts +++ b/libs/common/src/platform/models/domain/domain-base.spec.ts @@ -1,7 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { makeEncString, makeSymmetricCryptoKey } from "../../../../spec"; -import { EncryptService } from "../../abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { Utils } from "../../misc/utils"; import Domain from "./domain-base"; diff --git a/libs/common/src/platform/models/domain/domain-base.ts b/libs/common/src/platform/models/domain/domain-base.ts index 110a1dc7208..192034254b9 100644 --- a/libs/common/src/platform/models/domain/domain-base.ts +++ b/libs/common/src/platform/models/domain/domain-base.ts @@ -2,8 +2,8 @@ // @ts-strict-ignore import { ConditionalExcept, ConditionalKeys, Constructor } from "type-fest"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { View } from "../../../models/view/view"; -import { EncryptService } from "../../abstractions/encrypt.service"; import { EncString } from "./enc-string"; import { SymmetricCryptoKey } from "./symmetric-crypto-key"; diff --git a/libs/common/src/platform/models/domain/enc-string.spec.ts b/libs/common/src/platform/models/domain/enc-string.spec.ts index b4916b9f70a..9af19d36015 100644 --- a/libs/common/src/platform/models/domain/enc-string.spec.ts +++ b/libs/common/src/platform/models/domain/enc-string.spec.ts @@ -4,7 +4,7 @@ import { mock, MockProxy } from "jest-mock-extended"; // eslint-disable-next-line no-restricted-imports import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { makeEncString, makeStaticByteArray } from "../../../../spec"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { UserKey, OrgKey } from "../../../types/key"; import { EncryptionType } from "../../enums"; diff --git a/libs/common/src/platform/models/domain/enc-string.ts b/libs/common/src/platform/models/domain/enc-string.ts index f148664a4f9..a8fee428b13 100644 --- a/libs/common/src/platform/models/domain/enc-string.ts +++ b/libs/common/src/platform/models/domain/enc-string.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Jsonify, Opaque } from "type-fest"; -import { EncryptService } from "../../abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EncryptionType, EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE } from "../../enums"; import { Encrypted } from "../../interfaces/encrypted"; import { Utils } from "../../misc/utils"; diff --git a/libs/common/src/platform/services/container.service.ts b/libs/common/src/platform/services/container.service.ts index c3e727a2e1e..7421de8cc2c 100644 --- a/libs/common/src/platform/services/container.service.ts +++ b/libs/common/src/platform/services/container.service.ts @@ -1,7 +1,7 @@ // FIXME: remove `src` and fix import // eslint-disable-next-line no-restricted-imports import { KeyService } from "../../../../key-management/src/abstractions/key.service"; -import { EncryptService } from "../abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; export class ContainerService { constructor( diff --git a/libs/common/src/platform/state/global-state.ts b/libs/common/src/platform/state/global-state.ts index b0f19c53faa..82a6e2b348c 100644 --- a/libs/common/src/platform/state/global-state.ts +++ b/libs/common/src/platform/state/global-state.ts @@ -9,7 +9,7 @@ import { StateUpdateOptions } from "./state-update-options"; export interface GlobalState { /** * Method for allowing you to manipulate state in an additive way. - * @param configureState callback for how you want manipulate this section of state + * @param configureState callback for how you want to manipulate this section of state * @param options Defaults given by @see {module:state-update-options#DEFAULT_OPTIONS} * @param options.shouldUpdate A callback for determining if you want to update state. Defaults to () => true * @param options.combineLatestWith An observable that you want to combine with the current state for callbacks. Defaults to null diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index c83119d9ad4..c7901bc34e2 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -29,9 +29,20 @@ export const ORGANIZATION_MANAGEMENT_PREFERENCES_DISK = new StateDefinition( web: "disk-local", }, ); -export const AC_BANNERS_DISMISSED_DISK = new StateDefinition("acBannersDismissed", "disk", { - web: "disk-local", -}); +export const ACCOUNT_DEPROVISIONING_BANNER_DISK = new StateDefinition( + "showAccountDeprovisioningBanner", + "disk", + { + web: "disk-local", + }, +); +export const DELETE_MANAGED_USER_WARNING = new StateDefinition( + "showDeleteManagedUserWarning", + "disk", + { + web: "disk-local", + }, +); // Billing export const BILLING_DISK = new StateDefinition("billing", "disk"); diff --git a/libs/common/src/platform/state/user-state.ts b/libs/common/src/platform/state/user-state.ts index 44bc8732544..22c255eb985 100644 --- a/libs/common/src/platform/state/user-state.ts +++ b/libs/common/src/platform/state/user-state.ts @@ -16,6 +16,7 @@ export interface UserState { } export const activeMarker: unique symbol = Symbol("active"); + export interface ActiveUserState extends UserState { readonly [activeMarker]: true; @@ -32,7 +33,7 @@ export interface ActiveUserState extends UserState { * @param options.shouldUpdate A callback for determining if you want to update state. Defaults to () => true * @param options.combineLatestWith An observable that you want to combine with the current state for callbacks. Defaults to null * @param options.msTimeout A timeout for how long you are willing to wait for a `combineLatestWith` option to complete. Defaults to 1000ms. Only applies if `combineLatestWith` is set. - + * * @returns A promise that must be awaited before your next action to ensure the update has been written to state. * Resolves to the new state. If `shouldUpdate` returns false, the promise will resolve to the current state. */ @@ -41,6 +42,7 @@ export interface ActiveUserState extends UserState { options?: StateUpdateOptions, ) => Promise<[UserId, T]>; } + export interface SingleUserState extends UserState { readonly userId: UserId; @@ -51,7 +53,7 @@ export interface SingleUserState extends UserState { * @param options.shouldUpdate A callback for determining if you want to update state. Defaults to () => true * @param options.combineLatestWith An observable that you want to combine with the current state for callbacks. Defaults to null * @param options.msTimeout A timeout for how long you are willing to wait for a `combineLatestWith` option to complete. Defaults to 1000ms. Only applies if `combineLatestWith` is set. - + * * @returns A promise that must be awaited before your next action to ensure the update has been written to state. * Resolves to the new state. If `shouldUpdate` returns false, the promise will resolve to the current state. */ diff --git a/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts new file mode 100644 index 00000000000..59f39d195e9 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts @@ -0,0 +1,50 @@ +import { runMigrator } from "../migration-helper.spec"; +import { IRREVERSIBLE } from "../migrator"; + +import { RemoveAcBannersDismissed } from "./70-remove-ac-banner-dismissed"; + +describe("RemoveAcBannersDismissed", () => { + const sut = new RemoveAcBannersDismissed(69, 70); + + describe("migrate", () => { + it("deletes ac banner from all users", async () => { + const output = await runMigrator(sut, { + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + user_user1_showProviderClientVaultPrivacyBanner_acBannersDismissed: true, + user_user2_showProviderClientVaultPrivacyBanner_acBannersDismissed: true, + }); + + expect(output).toEqual({ + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + }); + }); + }); + + describe("rollback", () => { + it("is irreversible", async () => { + await expect(runMigrator(sut, {}, "rollback")).rejects.toThrow(IRREVERSIBLE); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts new file mode 100644 index 00000000000..087994b508f --- /dev/null +++ b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts @@ -0,0 +1,23 @@ +import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; +import { IRREVERSIBLE, Migrator } from "../migrator"; + +export const SHOW_BANNER_KEY: KeyDefinitionLike = { + key: "acBannersDismissed", + stateDefinition: { name: "showProviderClientVaultPrivacyBanner" }, +}; + +export class RemoveAcBannersDismissed extends Migrator<69, 70> { + async migrate(helper: MigrationHelper): Promise { + await Promise.all( + (await helper.getAccounts()).map(async ({ userId }) => { + if (helper.getFromUser(userId, SHOW_BANNER_KEY) != null) { + await helper.removeFromUser(userId, SHOW_BANNER_KEY); + } + }), + ); + } + + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } +} diff --git a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts index 0b60aef4917..66edc5a4838 100644 --- a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts +++ b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts @@ -3,7 +3,7 @@ import { BehaviorSubject, Subject } from "rxjs"; import { KeyService } from "@bitwarden/key-management"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "../../types/csprng"; import { OrganizationId, UserId } from "../../types/guid"; diff --git a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts index d4a8dec7dc3..c91181a004a 100644 --- a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts +++ b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts @@ -14,7 +14,7 @@ import { import { KeyService } from "@bitwarden/key-management"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { OrganizationId, UserId } from "../../types/guid"; import { OrganizationBound, diff --git a/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts b/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts index 62c8ea24ae6..3d93db81389 100644 --- a/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts +++ b/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts @@ -1,6 +1,6 @@ import { mock } from "jest-mock-extended"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "../../types/csprng"; diff --git a/libs/common/src/tools/cryptography/organization-key-encryptor.ts b/libs/common/src/tools/cryptography/organization-key-encryptor.ts index d3b7dae10f5..31f3db91232 100644 --- a/libs/common/src/tools/cryptography/organization-key-encryptor.ts +++ b/libs/common/src/tools/cryptography/organization-key-encryptor.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Jsonify } from "type-fest"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { OrganizationId } from "../../types/guid"; import { OrgKey } from "../../types/key"; diff --git a/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts b/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts index 5b0ee5103cb..e52190500b0 100644 --- a/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts +++ b/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts @@ -1,6 +1,6 @@ import { mock } from "jest-mock-extended"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "../../types/csprng"; diff --git a/libs/common/src/tools/cryptography/user-key-encryptor.ts b/libs/common/src/tools/cryptography/user-key-encryptor.ts index 296c33ea1dc..4b7cd1516a0 100644 --- a/libs/common/src/tools/cryptography/user-key-encryptor.ts +++ b/libs/common/src/tools/cryptography/user-key-encryptor.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Jsonify } from "type-fest"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { UserId } from "../../types/guid"; import { UserKey } from "../../types/key"; diff --git a/libs/common/src/tools/send/models/domain/send.spec.ts b/libs/common/src/tools/send/models/domain/send.spec.ts index fcc273d41bb..79f6c03adc8 100644 --- a/libs/common/src/tools/send/models/domain/send.spec.ts +++ b/libs/common/src/tools/send/models/domain/send.spec.ts @@ -3,7 +3,7 @@ import { mock } from "jest-mock-extended"; import { KeyService } from "@bitwarden/key-management"; import { makeStaticByteArray, mockEnc } from "../../../../../spec"; -import { EncryptService } from "../../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../../key-management/crypto/abstractions/encrypt.service"; import { SymmetricCryptoKey } from "../../../../platform/models/domain/symmetric-crypto-key"; import { ContainerService } from "../../../../platform/services/container.service"; import { UserKey } from "../../../../types/key"; diff --git a/libs/common/src/tools/send/services/send.service.spec.ts b/libs/common/src/tools/send/services/send.service.spec.ts index 662fee02bbf..26cc0a46708 100644 --- a/libs/common/src/tools/send/services/send.service.spec.ts +++ b/libs/common/src/tools/send/services/send.service.spec.ts @@ -10,7 +10,7 @@ import { awaitAsync, mockAccountServiceWith, } from "../../../../spec"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EnvironmentService } from "../../../platform/abstractions/environment.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; diff --git a/libs/common/src/tools/send/services/send.service.ts b/libs/common/src/tools/send/services/send.service.ts index 7021c942d44..1b5e5f6aa31 100644 --- a/libs/common/src/tools/send/services/send.service.ts +++ b/libs/common/src/tools/send/services/send.service.ts @@ -4,7 +4,7 @@ import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from import { PBKDF2KdfConfig, KeyService } from "@bitwarden/key-management"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; import { Utils } from "../../../platform/misc/utils"; diff --git a/libs/common/src/vault/models/domain/attachment.spec.ts b/libs/common/src/vault/models/domain/attachment.spec.ts index 8cae7170738..d1ee1dcc1ef 100644 --- a/libs/common/src/vault/models/domain/attachment.spec.ts +++ b/libs/common/src/vault/models/domain/attachment.spec.ts @@ -1,10 +1,9 @@ import { mock, MockProxy } from "jest-mock-extended"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; +import { KeyService } from "@bitwarden/key-management"; + import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { ContainerService } from "../../../platform/services/container.service"; diff --git a/libs/common/src/vault/models/domain/cipher.spec.ts b/libs/common/src/vault/models/domain/cipher.spec.ts index dd79da3086e..9eadd20f543 100644 --- a/libs/common/src/vault/models/domain/cipher.spec.ts +++ b/libs/common/src/vault/models/domain/cipher.spec.ts @@ -1,12 +1,11 @@ import { mock } from "jest-mock-extended"; import { Jsonify } from "type-fest"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; +import { KeyService } from "@bitwarden/key-management"; + import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { UriMatchStrategy } from "../../../models/domain/domain-service"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncString } from "../../../platform/models/domain/enc-string"; import { ContainerService } from "../../../platform/services/container.service"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; diff --git a/libs/common/src/vault/models/domain/folder.spec.ts b/libs/common/src/vault/models/domain/folder.spec.ts index 785852b884e..ff1c38bdd45 100644 --- a/libs/common/src/vault/models/domain/folder.spec.ts +++ b/libs/common/src/vault/models/domain/folder.spec.ts @@ -1,7 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { makeEncString, makeSymmetricCryptoKey, mockEnc, mockFromJson } from "../../../../spec"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string"; import { FolderData } from "../../models/data/folder.data"; import { Folder } from "../../models/domain/folder"; diff --git a/libs/common/src/vault/models/domain/folder.ts b/libs/common/src/vault/models/domain/folder.ts index 93d04607af5..65018e3cf08 100644 --- a/libs/common/src/vault/models/domain/folder.ts +++ b/libs/common/src/vault/models/domain/folder.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Jsonify } from "type-fest"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import Domain from "../../../platform/models/domain/domain-base"; import { EncString } from "../../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; diff --git a/libs/common/src/vault/models/domain/login-uri.spec.ts b/libs/common/src/vault/models/domain/login-uri.spec.ts index a1ecb473597..6346f38f0de 100644 --- a/libs/common/src/vault/models/domain/login-uri.spec.ts +++ b/libs/common/src/vault/models/domain/login-uri.spec.ts @@ -2,8 +2,8 @@ import { MockProxy, mock } from "jest-mock-extended"; import { Jsonify } from "type-fest"; import { mockEnc, mockFromJson } from "../../../../spec"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { UriMatchStrategy } from "../../../models/domain/domain-service"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncString } from "../../../platform/models/domain/enc-string"; import { LoginUriData } from "../data/login-uri.data"; diff --git a/libs/common/src/vault/models/view/cipher.view.ts b/libs/common/src/vault/models/view/cipher.view.ts index 20dbd23065c..650a1e9dc45 100644 --- a/libs/common/src/vault/models/view/cipher.view.ts +++ b/libs/common/src/vault/models/view/cipher.view.ts @@ -142,6 +142,13 @@ export class CipherView implements View, InitializerMetadata { ); } + get canAssignToCollections(): boolean { + if (this.organizationId == null) { + return true; + } + + return this.edit && this.viewPassword; + } /** * Determines if the cipher can be launched in a new browser tab. */ diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 0d6578f165d..c59f6672985 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -1,12 +1,8 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, map, of } from "rxjs"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { - CipherDecryptionKeys, - KeyService, -} from "../../../../key-management/src/abstractions/key.service"; +import { CipherDecryptionKeys, KeyService } from "@bitwarden/key-management"; + import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; import { makeStaticByteArray } from "../../../spec/utils"; @@ -14,10 +10,10 @@ import { ApiService } from "../../abstractions/api.service"; import { SearchService } from "../../abstractions/search.service"; import { AutofillSettingsService } from "../../autofill/services/autofill-settings.service"; import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; +import { BulkEncryptService } from "../../key-management/crypto/abstractions/bulk-encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { UriMatchStrategy } from "../../models/domain/domain-service"; -import { BulkEncryptService } from "../../platform/abstractions/bulk-encrypt.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; import { Utils } from "../../platform/misc/utils"; diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 18295453d9a..9e06d3335c3 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -14,22 +14,21 @@ import { } from "rxjs"; import { SemVer } from "semver"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { KeyService } from "../../../../key-management/src/abstractions/key.service"; +import { KeyService } from "@bitwarden/key-management"; + import { ApiService } from "../../abstractions/api.service"; import { SearchService } from "../../abstractions/search.service"; import { AccountService } from "../../auth/abstractions/account.service"; import { AutofillSettingsServiceAbstraction } from "../../autofill/services/autofill-settings.service"; import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; import { FeatureFlag } from "../../enums/feature-flag.enum"; +import { BulkEncryptService } from "../../key-management/crypto/abstractions/bulk-encrypt.service"; +import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; import { UriMatchStrategySetting } from "../../models/domain/domain-service"; import { ErrorResponse } from "../../models/response/error.response"; import { ListResponse } from "../../models/response/list.response"; import { View } from "../../models/view/view"; -import { BulkEncryptService } from "../../platform/abstractions/bulk-encrypt.service"; import { ConfigService } from "../../platform/abstractions/config/config.service"; -import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; import { sequentialize } from "../../platform/misc/sequentialize"; diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index cc3aa1946ca..ced4e2dceb7 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -1,14 +1,13 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom } from "rxjs"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; +import { KeyService } from "@bitwarden/key-management"; + import { makeEncString } from "../../../../spec"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service"; import { FakeSingleUserState } from "../../../../spec/fake-state"; import { FakeStateProvider } from "../../../../spec/fake-state-provider"; -import { EncryptService } from "../../../platform/abstractions/encrypt.service"; +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { Utils } from "../../../platform/misc/utils"; import { EncString } from "../../../platform/models/domain/enc-string"; diff --git a/libs/common/src/vault/services/folder/folder.service.ts b/libs/common/src/vault/services/folder/folder.service.ts index c21a92fd894..3d272416fbe 100644 --- a/libs/common/src/vault/services/folder/folder.service.ts +++ b/libs/common/src/vault/services/folder/folder.service.ts @@ -2,12 +2,11 @@ // @ts-strict-ignore import { Observable, Subject, firstValueFrom, map, shareReplay, switchMap, merge } from "rxjs"; -import { EncryptService } from ".././../../platform/abstractions/encrypt.service"; -import { Utils } from ".././../../platform/misc/utils"; -// FIXME: remove `src` and fix import -// eslint-disable-next-line no-restricted-imports -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; +import { KeyService } from "@bitwarden/key-management"; + +import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; +import { Utils } from "../../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { StateProvider } from "../../../platform/state"; import { UserId } from "../../../types/guid"; diff --git a/libs/components/src/avatar/avatar.component.ts b/libs/components/src/avatar/avatar.component.ts index 76ff702e88b..0e3dbd6f1b9 100644 --- a/libs/components/src/avatar/avatar.component.ts +++ b/libs/components/src/avatar/avatar.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf, NgClass } from "@angular/common"; +import { NgClass } from "@angular/common"; import { Component, Input, OnChanges } from "@angular/core"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; @@ -18,9 +18,11 @@ const SizeClasses: Record = { @Component({ selector: "bit-avatar", - template: ``, + template: `@if (src) { + + }`, standalone: true, - imports: [NgIf, NgClass], + imports: [NgClass], }) export class AvatarComponent implements OnChanges { @Input() border = false; diff --git a/libs/components/src/badge-list/badge-list.component.html b/libs/components/src/badge-list/badge-list.component.html index ebd63117d03..c8aa7b84680 100644 --- a/libs/components/src/badge-list/badge-list.component.html +++ b/libs/components/src/badge-list/badge-list.component.html @@ -1,11 +1,15 @@
- + @for (item of filteredItems; track item; let last = $last) { {{ item }} - , - - - {{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }} - + @if (!last || isFiltered) { + , + } + } + @if (isFiltered) { + + {{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }} + + }
diff --git a/libs/components/src/badge-list/badge-list.component.ts b/libs/components/src/badge-list/badge-list.component.ts index 7d152761ed0..86e9a84cb77 100644 --- a/libs/components/src/badge-list/badge-list.component.ts +++ b/libs/components/src/badge-list/badge-list.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CommonModule } from "@angular/common"; + import { Component, Input, OnChanges } from "@angular/core"; import { I18nPipe } from "@bitwarden/ui-common"; @@ -11,7 +11,7 @@ import { BadgeModule, BadgeVariant } from "../badge"; selector: "bit-badge-list", templateUrl: "badge-list.component.html", standalone: true, - imports: [CommonModule, BadgeModule, I18nPipe], + imports: [BadgeModule, I18nPipe], }) export class BadgeListComponent implements OnChanges { private _maxItems: number; diff --git a/libs/components/src/banner/banner.component.html b/libs/components/src/banner/banner.component.html index 566494eb64a..1a9d58d342a 100644 --- a/libs/components/src/banner/banner.component.html +++ b/libs/components/src/banner/banner.component.html @@ -4,21 +4,24 @@ [attr.role]="useAlertRole ? 'status' : null" [attr.aria-live]="useAlertRole ? 'polite' : null" > - + @if (icon) { + + } - + @if (showClose) { + + } diff --git a/libs/components/src/breadcrumbs/breadcrumb.component.html b/libs/components/src/breadcrumbs/breadcrumb.component.html index dd5bac9beb4..bb4dc7cdffe 100644 --- a/libs/components/src/breadcrumbs/breadcrumb.component.html +++ b/libs/components/src/breadcrumbs/breadcrumb.component.html @@ -1,3 +1,6 @@ - + @if (icon) { + + } + diff --git a/libs/components/src/breadcrumbs/breadcrumb.component.ts b/libs/components/src/breadcrumbs/breadcrumb.component.ts index ce18bde171f..53c46a9b24a 100644 --- a/libs/components/src/breadcrumbs/breadcrumb.component.ts +++ b/libs/components/src/breadcrumbs/breadcrumb.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf } from "@angular/common"; + import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core"; import { QueryParamsHandling } from "@angular/router"; @@ -8,7 +8,6 @@ import { QueryParamsHandling } from "@angular/router"; selector: "bit-breadcrumb", templateUrl: "./breadcrumb.component.html", standalone: true, - imports: [NgIf], }) export class BreadcrumbComponent { @Input() diff --git a/libs/components/src/breadcrumbs/breadcrumbs.component.html b/libs/components/src/breadcrumbs/breadcrumbs.component.html index 502bb0bb8e7..5205e19cee5 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.component.html +++ b/libs/components/src/breadcrumbs/breadcrumbs.component.html @@ -1,5 +1,5 @@ - - +@for (breadcrumb of beforeOverflow; track breadcrumb; let last = $last) { + @if (breadcrumb.route) { - - + } + @if (!breadcrumb.route) { - - - - - - + } + @if (!last) { + + } +} +@if (hasOverflow) { + @if (beforeOverflow.length > 0) { + + } - - - + @for (breadcrumb of overflow; track breadcrumb) { + @if (breadcrumb.route) { - - + } + @if (!breadcrumb.route) { - - + } + } - - - + @for (breadcrumb of afterOverflow; track breadcrumb; let last = $last) { + @if (breadcrumb.route) { - - + } + @if (!breadcrumb.route) { - - - - + } + @if (!last) { + + } + } +} diff --git a/libs/components/src/button/button.stories.ts b/libs/components/src/button/button.stories.ts index 469c2d1b51b..6024b0559f2 100644 --- a/libs/components/src/button/button.stories.ts +++ b/libs/components/src/button/button.stories.ts @@ -86,16 +86,15 @@ export const DisabledWithAttribute: Story = { render: (args) => ({ props: args, template: ` - + @if (disabled) { - - + } @else { - + } `, }), args: { diff --git a/libs/components/src/callout/callout.component.html b/libs/components/src/callout/callout.component.html index f64e197b9ef..bb7f918df32 100644 --- a/libs/components/src/callout/callout.component.html +++ b/libs/components/src/callout/callout.component.html @@ -3,10 +3,14 @@ [ngClass]="calloutClass" [attr.aria-labelledby]="titleId" > -
- - {{ title }} -
+ @if (title) { +
+ @if (icon) { + + } + {{ title }} +
+ }
diff --git a/libs/components/src/card/card.component.ts b/libs/components/src/card/card.component.ts index 37756088e0d..fdb02f280da 100644 --- a/libs/components/src/card/card.component.ts +++ b/libs/components/src/card/card.component.ts @@ -1,10 +1,8 @@ -import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component } from "@angular/core"; @Component({ selector: "bit-card", standalone: true, - imports: [CommonModule], template: ``, changeDetection: ChangeDetectionStrategy.OnPush, host: { diff --git a/libs/components/src/chip-select/chip-select.component.html b/libs/components/src/chip-select/chip-select.component.html index 81480f107f1..e88200b6e4f 100644 --- a/libs/components/src/chip-select/chip-select.component.html +++ b/libs/components/src/chip-select/chip-select.component.html @@ -30,78 +30,80 @@ {{ label }} - + @if (!selectedOption) { + + } - + @if (selectedOption) { + + } -
- - - - - - - -
+ @if (getParent(renderedOptions); as parent) { + + + } + @for (option of renderedOptions.children; track option) { + + } + + }
diff --git a/libs/components/src/chip-select/chip-select.component.ts b/libs/components/src/chip-select/chip-select.component.ts index a653d79f83f..39543db5ed5 100644 --- a/libs/components/src/chip-select/chip-select.component.ts +++ b/libs/components/src/chip-select/chip-select.component.ts @@ -46,6 +46,7 @@ export type ChipSelectOption = Option & { multi: true, }, ], + preserveWhitespaces: false, }) export class ChipSelectComponent implements ControlValueAccessor, AfterViewInit { @ViewChild(MenuComponent) menu: MenuComponent; diff --git a/libs/components/src/color-password/color-password.component.ts b/libs/components/src/color-password/color-password.component.ts index cbf746e9d73..e48758ca59a 100644 --- a/libs/components/src/color-password/color-password.component.ts +++ b/libs/components/src/color-password/color-password.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgFor, NgIf } from "@angular/common"; + import { Component, HostBinding, Input } from "@angular/core"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -14,18 +14,16 @@ enum CharacterType { @Component({ selector: "bit-color-password", - template: ` - {{ character }} - {{ - i + 1 - }} - `, + template: `@for (character of passwordArray; track character; let i = $index) { + + {{ character }} + @if (showCount) { + {{ i + 1 }} + } + + }`, preserveWhitespaces: false, standalone: true, - imports: [NgFor, NgIf], }) export class ColorPasswordComponent { @Input() password: string = null; diff --git a/libs/components/src/color-password/index.ts b/libs/components/src/color-password/index.ts index 86718f037f7..24870ca75d9 100644 --- a/libs/components/src/color-password/index.ts +++ b/libs/components/src/color-password/index.ts @@ -1 +1,2 @@ export * from "./color-password.module"; +export * from "./color-password.component"; diff --git a/libs/components/src/container/container.component.ts b/libs/components/src/container/container.component.ts index fbd9e40a7ca..1bcdb8f459b 100644 --- a/libs/components/src/container/container.component.ts +++ b/libs/components/src/container/container.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; /** @@ -7,7 +6,6 @@ import { Component } from "@angular/core"; @Component({ selector: "bit-container", templateUrl: "container.component.html", - imports: [CommonModule], standalone: true, }) export class ContainerComponent {} diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index ef9824471e7..01f05985127 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -13,9 +13,11 @@ class="tw-text-main tw-mb-0 tw-truncate" > {{ title }} - - {{ subtitle }} - + @if (subtitle) { + + {{ subtitle }} + + } + @if (showCancelButton) { + + }
diff --git a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index 60b2e1c3a3f..00026209183 100644 --- a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -1,7 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; -import { NgIf } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { FormGroup, ReactiveFormsModule } from "@angular/forms"; @@ -39,7 +38,6 @@ const DEFAULT_COLOR: Record = { IconDirective, ButtonComponent, BitFormButtonDirective, - NgIf, ], }) export class SimpleConfigurableDialogComponent { diff --git a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts index b4bf199358b..87d6eb9fbfc 100644 --- a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts @@ -12,23 +12,24 @@ import { DialogModule } from "../../dialog.module"; @Component({ template: ` -
-

{{ group.title }}

-
- + @for (group of dialogs; track group) { +
+

{{ group.title }}

+
+ @for (dialog of group.dialogs; track dialog) { + + } +
-
+ } - - {{ dialogCloseResult }} - + @if (showCallout) { + + {{ dialogCloseResult }} + + } `, }) class StoryDialogComponent { diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html index 0b56c6287dc..1f154a8d543 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html @@ -3,12 +3,11 @@ @fadeIn >
- + @if (hasIcon) { - - + } @else { - + }

- ({{ "required" | i18n }}) + @if (required) { + ({{ "required" | i18n }}) + } - + @if (!hasError) { + + } -
- {{ displayError }} -
+@if (hasError) { +
+ {{ displayError }} +
+} diff --git a/libs/components/src/form-control/form-control.component.ts b/libs/components/src/form-control/form-control.component.ts index d22d49ac03a..690c00a9dc0 100644 --- a/libs/components/src/form-control/form-control.component.ts +++ b/libs/components/src/form-control/form-control.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; -import { NgClass, NgIf } from "@angular/common"; +import { NgClass } from "@angular/common"; import { Component, ContentChild, HostBinding, Input } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -15,7 +15,7 @@ import { BitFormControlAbstraction } from "./form-control.abstraction"; selector: "bit-form-control", templateUrl: "form-control.component.html", standalone: true, - imports: [NgClass, TypographyDirective, NgIf, I18nPipe], + imports: [NgClass, TypographyDirective, I18nPipe], }) export class FormControlComponent { @Input() label: string; diff --git a/libs/components/src/form-control/label.component.html b/libs/components/src/form-control/label.component.html index 64ba1ce9501..2a0a57e35d8 100644 --- a/libs/components/src/form-control/label.component.html +++ b/libs/components/src/form-control/label.component.html @@ -5,10 +5,10 @@ - + @if (isInsideFormControl) { - + } - +@if (!isInsideFormControl) { - +} diff --git a/libs/components/src/form-field/error-summary.component.ts b/libs/components/src/form-field/error-summary.component.ts index f9325d8f82a..1709c3078fa 100644 --- a/libs/components/src/form-field/error-summary.component.ts +++ b/libs/components/src/form-field/error-summary.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf } from "@angular/common"; + import { Component, Input } from "@angular/core"; import { AbstractControl, UntypedFormGroup } from "@angular/forms"; @@ -8,15 +8,15 @@ import { I18nPipe } from "@bitwarden/ui-common"; @Component({ selector: "bit-error-summary", - template: ` + template: ` @if (errorCount > 0) { {{ "fieldsNeedAttention" | i18n: errorString }} - `, + }`, host: { class: "tw-block tw-text-danger tw-mt-2", "aria-live": "assertive", }, standalone: true, - imports: [NgIf, I18nPipe], + imports: [I18nPipe], }) export class BitErrorSummary { @Input() diff --git a/libs/components/src/form-field/form-field.component.html b/libs/components/src/form-field/form-field.component.html index f3c27aecafe..bd099859608 100644 --- a/libs/components/src/form-field/form-field.component.html +++ b/libs/components/src/form-field/form-field.component.html @@ -15,63 +15,65 @@ -
-
-
-
-
-
-
-
- -
-
- -
-
- -
-
-
- - +} @else {
- +} - - - - +@switch (input.hasError) { + @case (false) { + + } + @case (true) { + + } +} diff --git a/libs/components/src/form-field/index.ts b/libs/components/src/form-field/index.ts index 613ebaf9a9d..0c45f215ec9 100644 --- a/libs/components/src/form-field/index.ts +++ b/libs/components/src/form-field/index.ts @@ -1,4 +1,5 @@ export * from "./form-field.module"; export * from "./form-field.component"; export * from "./form-field-control"; +export * from "./password-input-toggle.directive"; export * as BitValidators from "./bit-validators"; diff --git a/libs/components/src/input/index.ts b/libs/components/src/input/index.ts index 9713d2b9192..6bd64495910 100644 --- a/libs/components/src/input/index.ts +++ b/libs/components/src/input/index.ts @@ -1,2 +1,3 @@ export * from "./input.module"; export * from "./autofocus.directive"; +export * from "./input.directive"; diff --git a/libs/components/src/item/item-content.component.ts b/libs/components/src/item/item-content.component.ts index fcd98bc55f6..2a6e06291fd 100644 --- a/libs/components/src/item/item-content.component.ts +++ b/libs/components/src/item/item-content.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CommonModule } from "@angular/common"; + +import { NgClass } from "@angular/common"; import { AfterContentChecked, ChangeDetectionStrategy, @@ -16,7 +17,7 @@ import { TypographyModule } from "../typography"; @Component({ selector: "bit-item-content, [bit-item-content]", standalone: true, - imports: [CommonModule, TypographyModule], + imports: [TypographyModule, NgClass], templateUrl: `item-content.component.html`, host: { class: diff --git a/libs/components/src/item/item.component.ts b/libs/components/src/item/item.component.ts index 97a80484373..1ef4a4af1fa 100644 --- a/libs/components/src/item/item.component.ts +++ b/libs/components/src/item/item.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component, @@ -14,7 +13,7 @@ import { ItemActionComponent } from "./item-action.component"; @Component({ selector: "bit-item", standalone: true, - imports: [CommonModule, ItemActionComponent], + imports: [ItemActionComponent], changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: "item.component.html", providers: [{ provide: A11yRowDirective, useExisting: ItemComponent }], diff --git a/libs/components/src/layout/layout.component.html b/libs/components/src/layout/layout.component.html index 489a0dc876f..33b8de81572 100644 --- a/libs/components/src/layout/layout.component.html +++ b/libs/components/src/layout/layout.component.html @@ -23,19 +23,21 @@ -
+ }; + as data + ) {
-
+ class="tw-pointer-events-none tw-fixed tw-inset-0 tw-z-10 tw-bg-black tw-bg-opacity-0 motion-safe:tw-transition-colors md:tw-hidden" + [ngClass]="[data.open ? 'tw-bg-opacity-30 md:tw-bg-opacity-0' : 'tw-bg-opacity-0']" + > + @if (data.open) { +
+ } +

+ }
diff --git a/libs/components/src/multi-select/multi-select.component.html b/libs/components/src/multi-select/multi-select.component.html index 0c45e2d333f..e157871e17a 100644 --- a/libs/components/src/multi-select/multi-select.component.html +++ b/libs/components/src/multi-select/multi-select.component.html @@ -31,7 +31,9 @@ [disabled]="disabled" (click)="clear(item)" > - + @if (item.icon != null) { + + } {{ item.labelName }} @@ -41,10 +43,14 @@
- + @if (isSelected(item)) { + + }
- + @if (item.icon != null) { + + }
{{ item.listName }} diff --git a/libs/components/src/multi-select/multi-select.component.ts b/libs/components/src/multi-select/multi-select.component.ts index 71b00404cfb..cd92eb1d7ae 100644 --- a/libs/components/src/multi-select/multi-select.component.ts +++ b/libs/components/src/multi-select/multi-select.component.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { hasModifierKey } from "@angular/cdk/keycodes"; -import { NgIf } from "@angular/common"; import { Component, Input, @@ -39,7 +38,7 @@ let nextId = 0; templateUrl: "./multi-select.component.html", providers: [{ provide: BitFormFieldControl, useExisting: MultiSelectComponent }], standalone: true, - imports: [NgSelectModule, ReactiveFormsModule, FormsModule, BadgeModule, NgIf, I18nPipe], + imports: [NgSelectModule, ReactiveFormsModule, FormsModule, BadgeModule, I18nPipe], }) /** * This component has been implemented to only support Multi-select list events diff --git a/libs/components/src/navigation/nav-divider.component.html b/libs/components/src/navigation/nav-divider.component.html index 224f6ae0657..64e43aeab4e 100644 --- a/libs/components/src/navigation/nav-divider.component.html +++ b/libs/components/src/navigation/nav-divider.component.html @@ -1 +1,3 @@ -
+@if (sideNavService.open$ | async) { +
+} diff --git a/libs/components/src/navigation/nav-group.component.html b/libs/components/src/navigation/nav-group.component.html index 9f6d9ac034d..9752fe56eb1 100644 --- a/libs/components/src/navigation/nav-group.component.html +++ b/libs/components/src/navigation/nav-group.component.html @@ -1,5 +1,5 @@ - +@if (!hideIfEmpty || nestedNavComponents.length > 0) { - - - - - - - + @if (variant === "tree") { + + } + + + @if (variant !== "tree") { + + } - - -
- -
-
-
+ @if (sideNavService.open$ | async) { + @if (open) { +
+ +
+ } + } +} diff --git a/libs/components/src/navigation/nav-group.component.ts b/libs/components/src/navigation/nav-group.component.ts index 37244f37c8d..62bdee26740 100644 --- a/libs/components/src/navigation/nav-group.component.ts +++ b/libs/components/src/navigation/nav-group.component.ts @@ -29,6 +29,7 @@ import { SideNavService } from "./side-nav.service"; ], standalone: true, imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe], + preserveWhitespaces: false, }) export class NavGroupComponent extends NavBaseComponent implements AfterContentInit { @ContentChildren(NavBaseComponent, { diff --git a/libs/components/src/navigation/nav-logo.component.html b/libs/components/src/navigation/nav-logo.component.html index 427e926f2d7..a6169315333 100644 --- a/libs/components/src/navigation/nav-logo.component.html +++ b/libs/components/src/navigation/nav-logo.component.html @@ -1,20 +1,23 @@ - - +@if (sideNavService.open) { +
+ + + +
+} +@if (!sideNavService.open) { + +} diff --git a/libs/components/src/navigation/nav-logo.component.ts b/libs/components/src/navigation/nav-logo.component.ts index 8a84970500c..de9d801e553 100644 --- a/libs/components/src/navigation/nav-logo.component.ts +++ b/libs/components/src/navigation/nav-logo.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf } from "@angular/common"; + import { Component, Input } from "@angular/core"; import { RouterLinkActive, RouterLink } from "@angular/router"; @@ -14,7 +14,7 @@ import { SideNavService } from "./side-nav.service"; selector: "bit-nav-logo", templateUrl: "./nav-logo.component.html", standalone: true, - imports: [NgIf, RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent], + imports: [RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent], }) export class NavLogoComponent { /** Icon that is displayed when the side nav is closed */ diff --git a/libs/components/src/navigation/side-nav.component.html b/libs/components/src/navigation/side-nav.component.html index 05c99c7d64e..3b77c981be4 100644 --- a/libs/components/src/navigation/side-nav.component.html +++ b/libs/components/src/navigation/side-nav.component.html @@ -1,42 +1,46 @@ - + +} diff --git a/libs/components/src/progress/progress.component.html b/libs/components/src/progress/progress.component.html index 2637f23eee8..30b68d9d645 100644 --- a/libs/components/src/progress/progress.component.html +++ b/libs/components/src/progress/progress.component.html @@ -7,13 +7,12 @@ attr.aria-valuenow="{{ barWidth }}" [ngStyle]="{ width: barWidth + '%' }" > -
- -
 
-
{{ textContent }}
-
+ @if (displayText) { +
+ +
 
+
{{ textContent }}
+
+ }
diff --git a/libs/components/src/radio-button/radio-group.component.html b/libs/components/src/radio-button/radio-group.component.html index 128a723d461..b71abd9249c 100644 --- a/libs/components/src/radio-button/radio-group.component.html +++ b/libs/components/src/radio-button/radio-group.component.html @@ -1,16 +1,18 @@ - +@if (label) {
- ({{ "required" | i18n }}) + @if (required) { + ({{ "required" | i18n }}) + }
-
+} - +@if (!label) { - +}
diff --git a/libs/components/src/radio-button/radio-group.component.ts b/libs/components/src/radio-button/radio-group.component.ts index 4ab626f7964..895f769af50 100644 --- a/libs/components/src/radio-button/radio-group.component.ts +++ b/libs/components/src/radio-button/radio-group.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf, NgTemplateOutlet } from "@angular/common"; +import { NgTemplateOutlet } from "@angular/common"; import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core"; import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; @@ -14,7 +14,7 @@ let nextId = 0; selector: "bit-radio-group", templateUrl: "radio-group.component.html", standalone: true, - imports: [NgIf, NgTemplateOutlet, I18nPipe], + imports: [NgTemplateOutlet, I18nPipe], }) export class RadioGroupComponent implements ControlValueAccessor { selected: unknown; diff --git a/libs/components/src/select/select.component.html b/libs/components/src/select/select.component.html index 848692526a1..dcca7ae195e 100644 --- a/libs/components/src/select/select.component.html +++ b/libs/components/src/select/select.component.html @@ -13,7 +13,9 @@
- + @if (item.icon != null) { + + }
{{ item.label }} diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index 8f75c5be42b..a89eb87f54b 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { NgIf } from "@angular/common"; + import { Component, ContentChildren, @@ -36,7 +36,7 @@ let nextId = 0; templateUrl: "select.component.html", providers: [{ provide: BitFormFieldControl, useExisting: SelectComponent }], standalone: true, - imports: [NgSelectModule, ReactiveFormsModule, FormsModule, NgIf], + imports: [NgSelectModule, ReactiveFormsModule, FormsModule], }) export class SelectComponent implements BitFormFieldControl, ControlValueAccessor { @ViewChild(NgSelectComponent) select: NgSelectComponent; diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts index c63b36ea89c..5fc01d37d53 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts @@ -49,11 +49,9 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; Your favorite color - + @for (color of colors; track color) { + + } diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts index 568c78566f6..9c609300ed1 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts @@ -36,9 +36,11 @@ class KitchenSinkDialog {

- - {{ item.name }} - + @for (item of navItems; track item) { + + {{ item.name }} + + }

diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts index 6f0054912cf..c71140d8166 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts @@ -16,11 +16,15 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; Mid-sized 1
-
    -
  • - {{ company.name }} -
  • -
+ @for (company of companyList; track company) { +
    + @if (company.size === selectedToggle || selectedToggle === "all") { +
  • + {{ company.name }} +
  • + } +
+ } `, }) export class KitchenSinkToggleList { diff --git a/libs/components/src/tabs/tab-group/tab-group.component.html b/libs/components/src/tabs/tab-group/tab-group.component.html index 071f5c2259f..52fa193de96 100644 --- a/libs/components/src/tabs/tab-group/tab-group.component.html +++ b/libs/components/src/tabs/tab-group/tab-group.component.html @@ -5,40 +5,41 @@ [attr.aria-label]="label" (keydown)="keyManager.onKeydown($event)" > - + + }
- - + @for (tab of tabs; track tab; let i = $index) { + + + }
diff --git a/libs/components/src/tabs/tab-group/tab-group.component.ts b/libs/components/src/tabs/tab-group/tab-group.component.ts index 54d00343b38..b525b9b6723 100644 --- a/libs/components/src/tabs/tab-group/tab-group.component.ts +++ b/libs/components/src/tabs/tab-group/tab-group.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { FocusKeyManager } from "@angular/cdk/a11y"; import { coerceNumberProperty } from "@angular/cdk/coercion"; -import { CommonModule } from "@angular/common"; +import { NgTemplateOutlet } from "@angular/common"; import { AfterContentChecked, AfterContentInit, @@ -33,7 +33,7 @@ let nextId = 0; templateUrl: "./tab-group.component.html", standalone: true, imports: [ - CommonModule, + NgTemplateOutlet, TabHeaderComponent, TabListContainerDirective, TabListItemDirective, diff --git a/libs/components/src/toast/toast.component.html b/libs/components/src/toast/toast.component.html index 84154cba611..d78cc7783aa 100644 --- a/libs/components/src/toast/toast.component.html +++ b/libs/components/src/toast/toast.component.html @@ -7,15 +7,14 @@
{{ variant | i18n }} -

{{ title }}

-

- {{ m }} -

+ @if (title) { +

{{ title }}

+ } + @for (m of messageArray; track m) { +

+ {{ m }} +

+ }