diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index cc8eb66289..80730e8b22 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -4,7 +4,7 @@
# The following owners will be the default owners for everything in the repo.
# Unless a later match takes precedence
-@bitwarden/team-leads
+* @bitwarden/team-leads-eng
## Secrets Manager team files ##
bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev
@@ -76,3 +76,10 @@ libs/components @bitwarden/team-platform-dev
## Desktop native module ##
apps/desktop/desktop_native @bitwarden/team-platform-dev
+
+## Multiple file owners ##
+/apps/web/config
+/apps/web/package.json
+
+## DevOps team files ##
+/.github/workflows @bitwarden/dept-devops
diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt
index 471602a09b..fd03af5f08 100644
--- a/.github/whitelist-capital-letters.txt
+++ b/.github/whitelist-capital-letters.txt
@@ -4,7 +4,6 @@
./apps/browser/src/safari/desktop/Base.lproj
./apps/browser/src/services/vaultTimeout
./apps/browser/store/windows/Assets
-./libs/common/src/abstractions/userVerification
./libs/common/src/abstractions/vaultTimeout
./libs/common/src/services/vaultTimeout
./bitwarden_license/README.md
@@ -26,8 +25,6 @@
./libs/common/src/misc/linkedFieldOption.decorator.ts
./libs/common/src/misc/serviceUtils.ts
./libs/common/src/misc/serviceUtils.spec.ts
-./libs/common/src/abstractions/userVerification/userVerification.service.abstraction.ts
-./libs/common/src/abstractions/userVerification/userVerification-api.service.abstraction.ts
./libs/common/src/abstractions/vaultTimeout/vaultTimeoutSettings.service.ts
./libs/common/src/abstractions/vaultTimeout/vaultTimeout.service.ts
./libs/common/src/abstractions/anonymousHub.service.ts
diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml
index 4a7918a81b..185b3b96cb 100644
--- a/.github/workflows/build-web.yml
+++ b/.github/workflows/build-web.yml
@@ -84,6 +84,8 @@ jobs:
npm_command: "build:bit:ee"
- name: "cloud-euprd"
npm_command: "build:bit:euprd"
+ - name: "cloud-euqa"
+ npm_command: "build:bit:euqa"
steps:
- name: Checkout repo
diff --git a/.github/workflows/deploy-eu-qa-web.yml b/.github/workflows/deploy-eu-qa-web.yml
new file mode 100644
index 0000000000..f4d32f7681
--- /dev/null
+++ b/.github/workflows/deploy-eu-qa-web.yml
@@ -0,0 +1,60 @@
+---
+name: Deploy Web to EU-QA Cloud
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Branch name to deploy (examples: 'master', 'feature/sm')"
+ required: true
+ type: string
+ default: master
+
+jobs:
+ azure-deploy:
+ name: Deploy to Azure
+ runs-on: ubuntu-22.04
+ env:
+ _WEB_ARTIFACT: "web-*-cloud-euqa.zip"
+ steps:
+ - name: Login to Azure - EU Subscription
+ uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
+ with:
+ creds: ${{ secrets.AZURE_KV_EU_QA_SERVICE_PRINCIPAL }}
+
+ - name: Retrieve Storage Account connection string
+ id: retrieve-secrets
+ uses: bitwarden/gh-actions/get-keyvault-secrets@37ffa14164a7308bc273829edfe75c97cd562375
+ with:
+ keyvault: webvaulteu-westeurope-qa
+ secrets: "sa-bitwarden-web-vault-dev-key-temp"
+
+ - name: Download latest cloud asset
+ uses: bitwarden/gh-actions/download-artifacts@37ffa14164a7308bc273829edfe75c97cd562375
+ with:
+ workflow: build-web.yml
+ path: apps/web
+ workflow_conclusion: success
+ branch: ${{ github.event.inputs.tag }}
+ artifacts: ${{ env._WEB_ARTIFACT }}
+
+ - name: Unzip build asset
+ working-directory: apps/web
+ run: unzip ${{ env._WEB_ARTIFACT }}
+
+ - name: Empty container in Storage Account
+ run: |
+ az storage blob delete-batch \
+ --source '$web' \
+ --pattern '*' \
+ --connection-string "${{ steps.retrieve-secrets.outputs.sa-bitwarden-web-vault-dev-key-temp }}"
+
+ - name: Deploy to Azure Storage Account
+ working-directory: apps/web
+ run: |
+ az storage blob upload-batch \
+ --source "./build" \
+ --destination '$web' \
+ --connection-string "${{ steps.retrieve-secrets.outputs.sa-bitwarden-web-vault-dev-key-temp }}" \
+ --overwrite \
+ --no-progress
diff --git a/.github/workflows/deploy-non-prod-web.yml b/.github/workflows/deploy-non-prod-web.yml
index d041369d84..c215ed3546 100644
--- a/.github/workflows/deploy-non-prod-web.yml
+++ b/.github/workflows/deploy-non-prod-web.yml
@@ -12,8 +12,6 @@ on:
type: choice
options:
- QA
- - POC2
- - eudevtest
jobs:
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index d7d37a71e7..0bc9fc5bac 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -101,6 +101,7 @@ const preview: Preview = {
},
options: {
storySort: {
+ method: "alphabetical",
order: ["Documentation", ["Introduction", "Colors", "Icons"], "Component Library"],
},
},
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 27c985453e..f23815200a 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -952,7 +952,7 @@
"message": "Server URL"
},
"apiUrl": {
- "message": "API Server URL"
+ "message": "API server URL"
},
"webVaultUrl": {
"message": "Web vault server URL"
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index 0437fa55e0..f816624097 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -7,8 +7,6 @@ import { NotificationsService as NotificationsServiceAbstraction } from "@bitwar
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
-import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
-import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -21,6 +19,8 @@ import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/ab
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
+import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
+import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts
index 1b3244064b..7b26980098 100644
--- a/apps/browser/src/popup/services/services.module.ts
+++ b/apps/browser/src/popup/services/services.module.ts
@@ -15,7 +15,6 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notificatio
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -31,6 +30,7 @@ import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-con
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { LoginService } from "@bitwarden/common/auth/services/login.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
diff --git a/apps/browser/src/tools/popup/settings/export.component.ts b/apps/browser/src/tools/popup/settings/export.component.ts
index c56b5c1f25..e21f3b9bd4 100644
--- a/apps/browser/src/tools/popup/settings/export.component.ts
+++ b/apps/browser/src/tools/popup/settings/export.component.ts
@@ -5,8 +5,8 @@ import { Router } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
diff --git a/apps/desktop/src/app/tools/export/export.component.ts b/apps/desktop/src/app/tools/export/export.component.ts
index 70f74d0ecd..770c3bf5f5 100644
--- a/apps/desktop/src/app/tools/export/export.component.ts
+++ b/apps/desktop/src/app/tools/export/export.component.ts
@@ -6,8 +6,8 @@ import { UntypedFormBuilder } from "@angular/forms";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
diff --git a/apps/web/config/eudevtest.json b/apps/web/config/eudevtest.json
deleted file mode 100644
index faaba2177c..0000000000
--- a/apps/web/config/eudevtest.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "urls": {
- "icons": "https://icons.eudevtest.bitwarden.pw",
- "notifications": "https://notifications.eudevtest.bitwarden.pw",
- "scim": "https://scim.eudevtest.bitwarden.pw"
- },
- "flags": {
- "secretsManager": true,
- "showPasswordless": true
- }
-}
diff --git a/apps/web/config/euprd.json b/apps/web/config/euprd.json
index 576b5b4cfa..4218593ede 100644
--- a/apps/web/config/euprd.json
+++ b/apps/web/config/euprd.json
@@ -1,6 +1,6 @@
{
"urls": {
- "icons": "https://icons.bitwarden.net",
+ "icons": "https://icons.bitwarden.eu",
"notifications": "https://notifications.bitwarden.eu",
"scim": "https://scim.bitwarden.eu"
},
diff --git a/apps/web/config/euqa.json b/apps/web/config/euqa.json
new file mode 100644
index 0000000000..496b52065c
--- /dev/null
+++ b/apps/web/config/euqa.json
@@ -0,0 +1,11 @@
+{
+ "urls": {
+ "icons": "https://icons.euqa.bitwarden.pw",
+ "notifications": "https://notifications.euqa.bitwarden.pw",
+ "scim": "https://scim.euqa.bitwarden.pw"
+ },
+ "flags": {
+ "secretsManager": true,
+ "showPasswordless": true
+ }
+}
diff --git a/apps/web/package.json b/apps/web/package.json
index 5b5f25d437..4be9ecc941 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -11,7 +11,7 @@
"build:bit:dev:watch": "cross-env ENV=development npm run build:bit:watch",
"build:bit:qa": "cross-env NODE_ENV=production ENV=qa npm run build:bit",
"build:bit:euprd": "cross-env NODE_ENV=production ENV=euprd npm run build:bit",
- "build:bit:eudevtest": "cross-env NODE_ENV=production ENV=eudevtest npm run build:bit",
+ "build:bit:euqa": "cross-env NODE_ENV=production ENV=euqa npm run build:bit",
"build:bit:cloud": "cross-env NODE_ENV=production ENV=cloud npm run build:bit",
"build:oss:selfhost:watch": "cross-env ENV=selfhosted npm run build:oss:watch",
"build:bit:selfhost:watch": "cross-env ENV=selfhosted npm run build:bit:watch",
diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts
index 646b542aa1..df436e687e 100644
--- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts
+++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts
@@ -4,10 +4,10 @@ import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { combineLatest, Subject, takeUntil } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
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 { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
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";
@@ -17,7 +17,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { UserVerificationModule } from "../../../../shared/components/user-verification";
+import { UserVerificationModule } from "../../../../auth/shared/components/user-verification";
import { SharedModule } from "../../../../shared/shared.module";
class CountBasedLocalizationKey {
diff --git a/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts b/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts
index 56fffd8260..35d445cb41 100644
--- a/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts
+++ b/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts
@@ -3,10 +3,9 @@ import { UntypedFormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
-import { ModalService } from "@bitwarden/angular/services/modal.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EventType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@@ -35,7 +34,6 @@ export class OrganizationExportComponent extends ExportComponent {
userVerificationService: UserVerificationService,
formBuilder: UntypedFormBuilder,
fileDownloadService: FileDownloadService,
- modalService: ModalService,
dialogService: DialogServiceAbstraction
) {
super(
@@ -49,7 +47,6 @@ export class OrganizationExportComponent extends ExportComponent {
userVerificationService,
formBuilder,
fileDownloadService,
- modalService,
dialogService
);
}
diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts
index 68203033ee..882bb78cea 100644
--- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts
+++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts
@@ -4,9 +4,9 @@ import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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/admin-console/organizations/users/organization-user.module.ts b/apps/web/src/app/admin-console/organizations/users/organization-user.module.ts
index 6799ae3ac4..30e2b5abe7 100644
--- a/apps/web/src/app/admin-console/organizations/users/organization-user.module.ts
+++ b/apps/web/src/app/admin-console/organizations/users/organization-user.module.ts
@@ -1,8 +1,8 @@
import { ScrollingModule } from "@angular/cdk/scrolling";
import { NgModule } from "@angular/core";
+import { UserVerificationModule } from "../../../auth/shared/components/user-verification";
import { LooseComponentsModule, SharedModule } from "../../../shared";
-import { UserVerificationModule } from "../../../shared/components/user-verification";
import { EnrollMasterPasswordReset } from "./enroll-master-password-reset.component";
diff --git a/apps/web/src/app/auth/settings/deauthorize-sessions.component.ts b/apps/web/src/app/auth/settings/deauthorize-sessions.component.ts
index fe758024cc..6c64933629 100644
--- a/apps/web/src/app/auth/settings/deauthorize-sessions.component.ts
+++ b/apps/web/src/app/auth/settings/deauthorize-sessions.component.ts
@@ -1,7 +1,7 @@
import { Component } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
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/web/src/app/auth/settings/two-factor-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts
index fadf03b32e..29cc6df156 100644
--- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts
@@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request";
import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response";
diff --git a/apps/web/src/app/auth/settings/two-factor-base.component.ts b/apps/web/src/app/auth/settings/two-factor-base.component.ts
index 95257421f8..d60da177a5 100644
--- a/apps/web/src/app/auth/settings/two-factor-base.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-base.component.ts
@@ -2,7 +2,7 @@ import { Directive, EventEmitter, Output } from "@angular/core";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
diff --git a/apps/web/src/app/auth/settings/two-factor-duo.component.ts b/apps/web/src/app/auth/settings/two-factor-duo.component.ts
index 81d6ed859e..354f58f7f2 100644
--- a/apps/web/src/app/auth/settings/two-factor-duo.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-duo.component.ts
@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorDuoRequest } from "@bitwarden/common/auth/models/request/update-two-factor-duo.request";
import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response";
diff --git a/apps/web/src/app/auth/settings/two-factor-email.component.ts b/apps/web/src/app/auth/settings/two-factor-email.component.ts
index f0140dd245..8a7976a19e 100644
--- a/apps/web/src/app/auth/settings/two-factor-email.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-email.component.ts
@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
import { UpdateTwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/update-two-factor-email.request";
diff --git a/apps/web/src/app/auth/settings/two-factor-verify.component.ts b/apps/web/src/app/auth/settings/two-factor-verify.component.ts
index e460a8cbd6..71a7772c8e 100644
--- a/apps/web/src/app/auth/settings/two-factor-verify.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-verify.component.ts
@@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
diff --git a/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts
index 8e57e6e152..d528e81a8c 100644
--- a/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-webauthn.component.ts
@@ -2,7 +2,7 @@ import { Component, NgZone } from "@angular/core";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { UpdateTwoFactorWebAuthnDeleteRequest } from "@bitwarden/common/auth/models/request/update-two-factor-web-authn-delete.request";
diff --git a/apps/web/src/app/auth/settings/two-factor-yubikey.component.ts b/apps/web/src/app/auth/settings/two-factor-yubikey.component.ts
index 4f6be97813..b9d2bd69f8 100644
--- a/apps/web/src/app/auth/settings/two-factor-yubikey.component.ts
+++ b/apps/web/src/app/auth/settings/two-factor-yubikey.component.ts
@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorYubioOtpRequest } from "@bitwarden/common/auth/models/request/update-two-factor-yubio-otp.request";
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response";
diff --git a/apps/web/src/app/shared/components/user-verification/index.ts b/apps/web/src/app/auth/shared/components/user-verification/index.ts
similarity index 100%
rename from apps/web/src/app/shared/components/user-verification/index.ts
rename to apps/web/src/app/auth/shared/components/user-verification/index.ts
index 9fe21e309c..4fde4e67a2 100644
--- a/apps/web/src/app/shared/components/user-verification/index.ts
+++ b/apps/web/src/app/auth/shared/components/user-verification/index.ts
@@ -1,3 +1,3 @@
export * from "./user-verification.module";
-export * from "./user-verification-prompt.component";
export * from "./user-verification.component";
+export * from "./user-verification-prompt.component";
diff --git a/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.html b/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.html
new file mode 100644
index 0000000000..8001b6b0d6
--- /dev/null
+++ b/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.html
@@ -0,0 +1,20 @@
+
diff --git a/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.ts b/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.ts
new file mode 100644
index 0000000000..2abacd3e8a
--- /dev/null
+++ b/apps/web/src/app/auth/shared/components/user-verification/user-verification-prompt.component.ts
@@ -0,0 +1,60 @@
+import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
+import { Component, Inject } from "@angular/core";
+import { FormBuilder } from "@angular/forms";
+
+import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/auth/components/user-verification-prompt.component";
+import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
+import { ModalConfig } from "@bitwarden/angular/services/modal.service";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+
+export interface UserVerificationPromptParams {
+ confirmDescription: string;
+ confirmButtonText: string;
+ modalTitle: string;
+}
+
+@Component({
+ templateUrl: "user-verification-prompt.component.html",
+})
+export class UserVerificationPromptComponent extends BaseUserVerificationPrompt {
+ constructor(
+ @Inject(DIALOG_DATA) data: UserVerificationPromptParams,
+ private dialogRef: DialogRef,
+ userVerificationService: UserVerificationService,
+ formBuilder: FormBuilder,
+ platformUtilsService: PlatformUtilsService,
+ i18nService: I18nService
+ ) {
+ // TODO: Remove when BaseUserVerificationPrompt has support for CL
+ const modalConfig: ModalConfig = { data };
+ super(
+ null,
+ modalConfig,
+ userVerificationService,
+ formBuilder,
+ platformUtilsService,
+ i18nService
+ );
+ }
+
+ override close(success: boolean) {
+ this.dialogRef.close(success);
+ }
+}
+
+/**
+ * Strongly typed helper to open a UserVerificationPrompt
+ * @param dialogService Instance of the dialog service that will be used to open the dialog
+ * @param config Configuration for the dialog
+ */
+export const openUserVerificationPrompt = (
+ dialogService: DialogServiceAbstraction,
+ config: DialogConfig
+) => {
+ return dialogService.open(
+ UserVerificationPromptComponent,
+ config
+ );
+};
diff --git a/apps/web/src/app/auth/shared/components/user-verification/user-verification.component.html b/apps/web/src/app/auth/shared/components/user-verification/user-verification.component.html
new file mode 100644
index 0000000000..a15ce41738
--- /dev/null
+++ b/apps/web/src/app/auth/shared/components/user-verification/user-verification.component.html
@@ -0,0 +1,41 @@
+
+
+ {{ "masterPass" | i18n }}
+
+
+ {{ "confirmIdentity" | i18n }}
+
+
+
+
+
+
+
+
+ {{ "codeSent" | i18n }}
+
+
+
+
+ {{ "verificationCode" | i18n }}
+
+ {{ "confirmIdentity" | i18n }}
+
+
diff --git a/apps/web/src/app/shared/components/user-verification/user-verification.component.ts b/apps/web/src/app/auth/shared/components/user-verification/user-verification.component.ts
similarity index 100%
rename from apps/web/src/app/shared/components/user-verification/user-verification.component.ts
rename to apps/web/src/app/auth/shared/components/user-verification/user-verification.component.ts
diff --git a/apps/web/src/app/shared/components/user-verification/user-verification.module.ts b/apps/web/src/app/auth/shared/components/user-verification/user-verification.module.ts
similarity index 68%
rename from apps/web/src/app/shared/components/user-verification/user-verification.module.ts
rename to apps/web/src/app/auth/shared/components/user-verification/user-verification.module.ts
index de6b32f7fe..2287b76ae5 100644
--- a/apps/web/src/app/shared/components/user-verification/user-verification.module.ts
+++ b/apps/web/src/app/auth/shared/components/user-verification/user-verification.module.ts
@@ -1,12 +1,13 @@
import { NgModule } from "@angular/core";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
-import { SharedModule } from "../../shared.module";
+import { SharedModule } from "../../../../shared/shared.module";
import { UserVerificationPromptComponent } from "./user-verification-prompt.component";
import { UserVerificationComponent } from "./user-verification.component";
@NgModule({
- imports: [SharedModule],
+ imports: [SharedModule, FormsModule, ReactiveFormsModule],
declarations: [UserVerificationComponent, UserVerificationPromptComponent],
exports: [UserVerificationComponent, UserVerificationPromptComponent],
})
diff --git a/apps/web/src/app/auth/update-password.component.ts b/apps/web/src/app/auth/update-password.component.ts
index d1d0ea8071..6c8e81fb4b 100644
--- a/apps/web/src/app/auth/update-password.component.ts
+++ b/apps/web/src/app/auth/update-password.component.ts
@@ -4,8 +4,8 @@ import { Router } from "@angular/router";
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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/billing-sync-api-key.component.ts b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
index 9bbcdf9333..8386747170 100644
--- a/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
+++ b/apps/web/src/app/billing/organizations/billing-sync-api-key.component.ts
@@ -2,10 +2,10 @@ import { Component } from "@angular/core";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
import { OrganizationApiKeyRequest } from "@bitwarden/common/admin-console/models/request/organization-api-key.request";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
diff --git a/apps/web/src/app/billing/organizations/organization-billing.module.ts b/apps/web/src/app/billing/organizations/organization-billing.module.ts
index 781e19d67b..bebf382a16 100644
--- a/apps/web/src/app/billing/organizations/organization-billing.module.ts
+++ b/apps/web/src/app/billing/organizations/organization-billing.module.ts
@@ -1,7 +1,7 @@
import { NgModule } from "@angular/core";
+import { UserVerificationModule } from "../../auth/shared/components/user-verification";
import { LooseComponentsModule, SharedModule } from "../../shared";
-import { UserVerificationModule } from "../../shared/components/user-verification";
import { AdjustSubscription } from "./adjust-subscription.component";
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";
diff --git a/apps/web/src/app/settings/api-key.component.ts b/apps/web/src/app/settings/api-key.component.ts
index 2360a84e01..bdb74845c4 100644
--- a/apps/web/src/app/settings/api-key.component.ts
+++ b/apps/web/src/app/settings/api-key.component.ts
@@ -1,6 +1,6 @@
import { Component } from "@angular/core";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
diff --git a/apps/web/src/app/settings/purge-vault.component.ts b/apps/web/src/app/settings/purge-vault.component.ts
index 23b7513268..83e8c2a8df 100644
--- a/apps/web/src/app/settings/purge-vault.component.ts
+++ b/apps/web/src/app/settings/purge-vault.component.ts
@@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
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/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.html b/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.html
deleted file mode 100644
index 10a137c51e..0000000000
--- a/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
diff --git a/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.ts b/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.ts
deleted file mode 100644
index 4b24b446a8..0000000000
--- a/apps/web/src/app/shared/components/user-verification/user-verification-prompt.component.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from "@angular/core";
-
-import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/auth/components/user-verification-prompt.component";
-
-@Component({
- templateUrl: "user-verification-prompt.component.html",
-})
-export class UserVerificationPromptComponent extends BaseUserVerificationPrompt {}
diff --git a/apps/web/src/app/shared/components/user-verification/user-verification.component.html b/apps/web/src/app/shared/components/user-verification/user-verification.component.html
deleted file mode 100644
index b90c6074d8..0000000000
--- a/apps/web/src/app/shared/components/user-verification/user-verification.component.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
- {{ "confirmIdentity" | i18n }}
-
-
-
-
-
-
-
- {{ "codeSent" | i18n }}
-
-
-
-
-
-
- {{ "confirmIdentity" | i18n }}
-
-
diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts
index cf9a43915a..588c3a156e 100644
--- a/apps/web/src/app/shared/loose-components.module.ts
+++ b/apps/web/src/app/shared/loose-components.module.ts
@@ -43,6 +43,7 @@ import { TwoFactorVerifyComponent } from "../auth/settings/two-factor-verify.com
import { TwoFactorWebAuthnComponent } from "../auth/settings/two-factor-webauthn.component";
import { TwoFactorYubiKeyComponent } from "../auth/settings/two-factor-yubikey.component";
import { VerifyEmailComponent } from "../auth/settings/verify-email.component";
+import { UserVerificationModule } from "../auth/shared/components/user-verification";
import { SsoComponent } from "../auth/sso.component";
import { TwoFactorOptionsComponent } from "../auth/two-factor-options.component";
import { TwoFactorComponent } from "../auth/two-factor.component";
@@ -109,7 +110,6 @@ import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-va
import { CollectionsComponent as OrgCollectionsComponent } from "../vault/org-vault/collections.component";
import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component";
-import { UserVerificationModule } from "./components/user-verification";
import { SharedModule } from "./shared.module";
// Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left.
@@ -234,6 +234,7 @@ import { SharedModule } from "./shared.module";
LowKdfComponent,
],
exports: [
+ UserVerificationModule,
PremiumBadgeComponent,
AcceptEmergencyComponent,
AcceptOrganizationComponent,
diff --git a/apps/web/src/app/tools/import-export/export.component.ts b/apps/web/src/app/tools/import-export/export.component.ts
index 71c25bbb7c..9d57f83f2a 100644
--- a/apps/web/src/app/tools/import-export/export.component.ts
+++ b/apps/web/src/app/tools/import-export/export.component.ts
@@ -1,12 +1,12 @@
import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
+import { firstValueFrom } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
-import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EncryptedExportType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@@ -15,7 +15,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { VaultExportServiceAbstraction } from "@bitwarden/exporter/vault-export";
-import { UserVerificationPromptComponent } from "../../shared/components/user-verification";
+import { openUserVerificationPrompt } from "../../auth/shared/components/user-verification";
@Component({
selector: "app-export",
@@ -37,7 +37,6 @@ export class ExportComponent extends BaseExportComponent {
userVerificationService: UserVerificationService,
formBuilder: UntypedFormBuilder,
fileDownloadService: FileDownloadService,
- private modalService: ModalService,
dialogService: DialogServiceAbstraction
) {
super(
@@ -101,8 +100,7 @@ export class ExportComponent extends BaseExportComponent {
confirmDescription = "encExportKeyWarningDesc";
}
- const ref = this.modalService.open(UserVerificationPromptComponent, {
- allowMultipleModals: true,
+ const ref = openUserVerificationPrompt(this.dialogService, {
data: {
confirmDescription: confirmDescription,
confirmButtonText: "exportVault",
@@ -114,7 +112,7 @@ export class ExportComponent extends BaseExportComponent {
return;
}
- return ref.onClosedPromise();
+ return firstValueFrom(ref.closed);
}
get isFileEncryptedExport() {
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 785afc0e95..e39dcb12ef 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
@@ -22,6 +22,19 @@
+
+ {{ "organization" | i18n }}
+
+
+
+
+
+
{{ "externalId" | i18n }}
diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts
index ac3c62a5b8..74b7d0fdd3 100644
--- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts
+++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts
@@ -1,7 +1,16 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
-import { combineLatest, of, shareReplay, Subject, switchMap, takeUntil } from "rxjs";
+import {
+ combineLatest,
+ map,
+ Observable,
+ of,
+ shareReplay,
+ Subject,
+ switchMap,
+ takeUntil,
+} from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
@@ -10,6 +19,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { Utils } from "@bitwarden/common/platform/misc/utils";
+import { CollectionResponse } from "@bitwarden/common/vault/models/response/collection.response";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { BitValidators } from "@bitwarden/components";
@@ -35,9 +46,16 @@ export interface CollectionDialogParams {
organizationId: string;
initialTab?: CollectionDialogTabType;
parentCollectionId?: string;
+ showOrgSelector?: boolean;
+ collectionIds?: string[];
}
-export enum CollectionDialogResult {
+export interface CollectionDialogResult {
+ action: CollectionDialogAction;
+ collection: CollectionResponse;
+}
+
+export enum CollectionDialogAction {
Saved = "saved",
Canceled = "canceled",
Deleted = "deleted",
@@ -48,6 +66,7 @@ export enum CollectionDialogResult {
})
export class CollectionDialogComponent implements OnInit, OnDestroy {
private destroy$ = new Subject();
+ protected organizations$: Observable;
protected tabIndex: CollectionDialogTabType;
protected loading = true;
@@ -56,11 +75,13 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
protected nestOptions: CollectionView[] = [];
protected accessItems: AccessItemView[] = [];
protected deletedParentName: string | undefined;
+ protected showOrgSelector = false;
protected formGroup = this.formBuilder.group({
name: ["", [Validators.required, BitValidators.forbiddenCharacters(["/"])]],
externalId: "",
parent: undefined as string | undefined,
access: [[] as AccessItemValue[]],
+ selectedOrg: "",
});
protected PermissionMode = PermissionMode;
@@ -79,8 +100,31 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.tabIndex = params.initialTab ?? CollectionDialogTabType.Info;
}
- ngOnInit() {
- const organization$ = of(this.organizationService.get(this.params.organizationId)).pipe(
+ async ngOnInit() {
+ // Opened from the individual vault
+ if (this.params.showOrgSelector) {
+ this.showOrgSelector = true;
+ this.formGroup.controls.selectedOrg.valueChanges
+ .pipe(takeUntil(this.destroy$))
+ .subscribe((id) => this.loadOrg(id, this.params.collectionIds));
+ this.organizations$ = this.organizationService.organizations$.pipe(
+ map((orgs) =>
+ orgs
+ .filter((o) => o.canCreateNewCollections)
+ .sort(Utils.getSortFunction(this.i18nService, "name"))
+ )
+ );
+ // patchValue will trigger a call to loadOrg() in this case, so no need to call it again here
+ this.formGroup.patchValue({ selectedOrg: this.params.organizationId });
+ } else {
+ // Opened from the org vault
+ this.formGroup.patchValue({ selectedOrg: this.params.organizationId });
+ this.loadOrg(this.params.organizationId, this.params.collectionIds);
+ }
+ }
+
+ async loadOrg(orgId: string, collectionIds: string[]) {
+ const organization$ = of(this.organizationService.get(orgId)).pipe(
shareReplay({ refCount: true, bufferSize: 1 })
);
const groups$ = organization$.pipe(
@@ -89,20 +133,19 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
return of([] as GroupView[]);
}
- return this.groupService.getAll(this.params.organizationId);
+ return this.groupService.getAll(orgId);
})
);
-
combineLatest({
organization: organization$,
- collections: this.collectionService.getAll(this.params.organizationId),
+ collections: this.collectionService.getAll(orgId),
collectionDetails: this.params.collectionId
- ? this.collectionService.get(this.params.organizationId, this.params.collectionId)
+ ? this.collectionService.get(orgId, this.params.collectionId)
: of(null),
groups: groups$,
- users: this.organizationUserService.getAllUsers(this.params.organizationId),
+ users: this.organizationUserService.getAllUsers(orgId),
})
- .pipe(takeUntil(this.destroy$))
+ .pipe(takeUntil(this.formGroup.controls.selectedOrg.valueChanges), takeUntil(this.destroy$))
.subscribe(({ organization, collections, collectionDetails, groups, users }) => {
this.organization = organization;
this.accessItems = [].concat(
@@ -110,6 +153,10 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
users.data.map(mapUserToAccessItemView)
);
+ if (collectionIds) {
+ collections = collections.filter((c) => collectionIds.includes(c.id));
+ }
+
if (this.params.collectionId) {
this.collection = collections.find((c) => c.id === this.collectionId);
this.nestOptions = collections.filter((c) => c.id !== this.collectionId);
@@ -149,7 +196,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
}
protected async cancel() {
- this.close(CollectionDialogResult.Canceled);
+ this.close(CollectionDialogAction.Canceled);
}
protected submit = async () => {
@@ -168,7 +215,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
const collectionView = new CollectionAdminView();
collectionView.id = this.params.collectionId;
- collectionView.organizationId = this.params.organizationId;
+ collectionView.organizationId = this.formGroup.controls.selectedOrg.value;
collectionView.externalId = this.formGroup.controls.externalId.value;
collectionView.groups = this.formGroup.controls.access.value
.filter((v) => v.type === AccessItemType.Group)
@@ -184,7 +231,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
collectionView.name = this.formGroup.controls.name.value;
}
- await this.collectionService.save(collectionView);
+ const savedCollection = await this.collectionService.save(collectionView);
this.platformUtilsService.showToast(
"success",
@@ -195,7 +242,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
)
);
- this.close(CollectionDialogResult.Saved);
+ this.close(CollectionDialogAction.Saved, savedCollection);
};
protected delete = async () => {
@@ -217,7 +264,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.i18nService.t("deletedCollectionId", this.collection?.name)
);
- this.close(CollectionDialogResult.Deleted);
+ this.close(CollectionDialogAction.Deleted);
};
ngOnDestroy(): void {
@@ -225,8 +272,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.destroy$.complete();
}
- private close(result: CollectionDialogResult) {
- this.dialogRef.close(result);
+ private close(action: CollectionDialogAction, collection?: CollectionResponse) {
+ this.dialogRef.close({ action, collection } as CollectionDialogResult);
}
}
diff --git a/apps/web/src/app/vault/core/collection-admin.service.ts b/apps/web/src/app/vault/core/collection-admin.service.ts
index efeb3817ea..08e94e58bd 100644
--- a/apps/web/src/app/vault/core/collection-admin.service.ts
+++ b/apps/web/src/app/vault/core/collection-admin.service.ts
@@ -46,7 +46,7 @@ export class CollectionAdminService {
return view;
}
- async save(collection: CollectionAdminView): Promise {
+ async save(collection: CollectionAdminView): Promise {
const request = await this.encrypt(collection);
let response: CollectionResponse;
@@ -61,9 +61,7 @@ export class CollectionAdminService {
);
}
- // TODO: Implement upsert when in PS-1083: Collection Service refactors
- // await this.collectionService.upsert(data);
- return;
+ return response;
}
async delete(organizationId: string, collectionId: string): Promise {
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
index 0cce8df38b..084573446f 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts
@@ -32,7 +32,6 @@ import { OrganizationOptionsComponent } from "./organization-options.component";
export class VaultFilterComponent implements OnInit, OnDestroy {
filters?: VaultFilterList;
@Input() activeFilter: VaultFilter = new VaultFilter();
- @Output() onAddFolder = new EventEmitter();
@Output() onEditFolder = new EventEmitter();
@Input() searchText = "";
@@ -142,10 +141,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
filter.selectedCollectionNode = collectionNode;
};
- addFolder = async (): Promise => {
- this.onAddFolder.emit();
- };
-
editFolder = async (folder: FolderFilter): Promise => {
this.onEditFolder.emit(folder);
};
@@ -249,10 +244,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
text: "editFolder",
action: this.editFolder,
},
- add: {
- text: "Add Folder",
- action: this.addFolder,
- },
};
return folderFilterSection;
}
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
index b26b827274..bcd151193a 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.html
@@ -34,16 +34,6 @@
{{ headerNode.node.name | i18n }}
-
-
-
-
- {{ "permissions" | i18n }}
-
-
-
- {{ "accessTokenPermissionsBetaNotification" | i18n }}
-
-
(AccessTokenCreateDialogComponent, {
data: {
- organizationId: organizationId,
- serviceAccountView: serviceAccountView,
+ serviceAccountView,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.html
index 3d896e6225..2a490d7914 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.html
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.html
@@ -7,7 +7,7 @@
granteeType="people"
[label]="'people' | i18n"
[hint]="'projectPeopleSelectHint' | i18n"
- [columnTitle]="'groupSlashUser' | i18n"
+ [columnTitle]="'name' | i18n"
[emptyMessage]="'projectEmptyPeopleAccessPolicies' | i18n"
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts
index 98d316467d..2e6084e06e 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts
@@ -15,6 +15,8 @@ import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { ServiceAccountView } from "../models/view/service-account.view";
+
import { AccessTokenCreateDialogComponent } from "./access/dialogs/access-token-create-dialog.component";
import { ServiceAccountService } from "./service-account.service";
@@ -32,6 +34,7 @@ export class ServiceAccountComponent implements OnInit, OnDestroy {
startWith(null)
);
+ private serviceAccountView: ServiceAccountView;
protected serviceAccount$ = combineLatest([this.route.params, this.onChange$]).pipe(
switchMap(([params, _]) =>
this.serviceAccountService.getByServiceAccountId(
@@ -61,9 +64,8 @@ export class ServiceAccountComponent implements OnInit, OnDestroy {
) {}
ngOnInit(): void {
- this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
- this.serviceAccountId = params.serviceAccountId;
- this.organizationId = params.organizationId;
+ this.serviceAccount$.pipe(takeUntil(this.destroy$)).subscribe((serviceAccountView) => {
+ this.serviceAccountView = serviceAccountView;
});
}
@@ -75,8 +77,7 @@ export class ServiceAccountComponent implements OnInit, OnDestroy {
protected openNewAccessTokenDialog() {
AccessTokenCreateDialogComponent.openNewAccessTokenDialog(
this.dialogService,
- this.serviceAccountId,
- this.organizationId
+ this.serviceAccountView
);
}
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts
index 492207415a..c3ba6bc2ef 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts
@@ -1,15 +1,15 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
-import { Subject, switchMap, takeUntil } from "rxjs";
+import { firstValueFrom, Subject, switchMap, takeUntil } from "rxjs";
-import { ModalService } from "@bitwarden/angular/services/modal.service";
+import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
-import { UserVerificationPromptComponent } from "@bitwarden/web-vault/app/shared/components/user-verification";
+import { openUserVerificationPrompt } from "@bitwarden/web-vault/app/auth/shared/components/user-verification";
import { SecretsManagerPortingApiService } from "../services/sm-porting-api.service";
import { SecretsManagerPortingService } from "../services/sm-porting.service";
@@ -42,7 +42,7 @@ export class SecretsManagerExportComponent implements OnInit, OnDestroy {
private smPortingService: SecretsManagerPortingService,
private fileDownloadService: FileDownloadService,
private logService: LogService,
- private modalService: ModalService,
+ private dialogService: DialogServiceAbstraction,
private secretsManagerApiService: SecretsManagerPortingApiService
) {}
@@ -98,12 +98,11 @@ export class SecretsManagerExportComponent implements OnInit, OnDestroy {
}
private verifyUser() {
- const ref = this.modalService.open(UserVerificationPromptComponent, {
- allowMultipleModals: true,
+ const ref = openUserVerificationPrompt(this.dialogService, {
data: {
- confirmDescription: "exportWarningDesc",
- confirmButtonText: "exportVault",
- modalTitle: "confirmVaultExport",
+ confirmDescription: "exportSecretsWarningDesc",
+ confirmButtonText: "exportSecrets",
+ modalTitle: "confirmSecretsExport",
},
});
@@ -111,6 +110,6 @@ export class SecretsManagerExportComponent implements OnInit, OnDestroy {
return;
}
- return ref.onClosedPromise();
+ return firstValueFrom(ref.closed);
}
}
diff --git a/libs/angular/src/auth/components/update-password.component.ts b/libs/angular/src/auth/components/update-password.component.ts
index 9a6e98156a..c9d3c3a8ec 100644
--- a/libs/angular/src/auth/components/update-password.component.ts
+++ b/libs/angular/src/auth/components/update-password.component.ts
@@ -2,9 +2,9 @@ import { Directive } from "@angular/core";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
diff --git a/libs/angular/src/auth/components/update-temp-password.component.ts b/libs/angular/src/auth/components/update-temp-password.component.ts
index 34833c3bbf..01128c4ba6 100644
--- a/libs/angular/src/auth/components/update-temp-password.component.ts
+++ b/libs/angular/src/auth/components/update-temp-password.component.ts
@@ -2,9 +2,9 @@ import { Directive } from "@angular/core";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.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 { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { ForceResetPasswordReason } from "@bitwarden/common/auth/models/domain/force-reset-password-reason";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
diff --git a/libs/angular/src/auth/components/user-verification-prompt.component.ts b/libs/angular/src/auth/components/user-verification-prompt.component.ts
index 1a10c96cf5..1b0a1dbed8 100644
--- a/libs/angular/src/auth/components/user-verification-prompt.component.ts
+++ b/libs/angular/src/auth/components/user-verification-prompt.component.ts
@@ -1,9 +1,10 @@
import { Directive } from "@angular/core";
-import { FormBuilder, FormControl } from "@angular/forms";
+import { FormBuilder } from "@angular/forms";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { Verification } from "@bitwarden/common/types/verification";
import { ModalRef } from "../../components/modal/modal.ref";
import { ModalConfig } from "../../services/modal.service";
@@ -16,7 +17,12 @@ export class UserVerificationPromptComponent {
confirmDescription = this.config.data.confirmDescription;
confirmButtonText = this.config.data.confirmButtonText;
modalTitle = this.config.data.modalTitle;
- secret = new FormControl();
+
+ formGroup = this.formBuilder.group({
+ secret: this.formBuilder.control(null),
+ });
+
+ protected invalidSecret = false;
constructor(
private modalRef: ModalRef,
@@ -27,19 +33,31 @@ export class UserVerificationPromptComponent {
private i18nService: I18nService
) {}
- async submit() {
- try {
- //Incorrect secret will throw an invalid password error.
- await this.userVerificationService.verifyUser(this.secret.value);
- } catch (e) {
- this.platformUtilsService.showToast(
- "error",
- this.i18nService.t("error"),
- this.i18nService.t("invalidMasterPassword")
- );
+ get secret() {
+ return this.formGroup.controls.secret;
+ }
+
+ submit = async () => {
+ this.formGroup.markAllAsTouched();
+
+ if (this.formGroup.invalid) {
return;
}
- this.modalRef.close(true);
+ try {
+ //Incorrect secret will throw an invalid password error.
+ await this.userVerificationService.verifyUser(this.secret.value);
+ this.invalidSecret = false;
+ } catch (e) {
+ this.invalidSecret = true;
+ this.platformUtilsService.showToast("error", this.i18nService.t("error"), e.message);
+ return;
+ }
+
+ this.close(true);
+ };
+
+ close(success: boolean) {
+ this.modalRef.close(success);
}
}
diff --git a/libs/angular/src/auth/components/user-verification.component.ts b/libs/angular/src/auth/components/user-verification.component.ts
index 5f9d62c3dc..9dfed5b924 100644
--- a/libs/angular/src/auth/components/user-verification.component.ts
+++ b/libs/angular/src/auth/components/user-verification.component.ts
@@ -1,9 +1,11 @@
-import { Directive, OnInit } from "@angular/core";
-import { ControlValueAccessor, FormControl } from "@angular/forms";
+import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
+import { ControlValueAccessor, FormControl, Validators } from "@angular/forms";
+import { Subject, takeUntil } from "rxjs";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { Verification } from "@bitwarden/common/types/verification";
@@ -17,29 +19,65 @@ import { Verification } from "@bitwarden/common/types/verification";
selector: "app-user-verification",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
-export class UserVerificationComponent implements ControlValueAccessor, OnInit {
- usesKeyConnector = false;
+export class UserVerificationComponent implements ControlValueAccessor, OnInit, OnDestroy {
+ private _invalidSecret = false;
+ @Input()
+ get invalidSecret() {
+ return this._invalidSecret;
+ }
+ set invalidSecret(value: boolean) {
+ this._invalidSecret = value;
+ this.invalidSecretChange.emit(value);
+
+ // ISSUE: This is pretty hacky but unfortunately there is no way of knowing if the parent
+ // control has been marked as touched, see: https://github.com/angular/angular/issues/10887
+ // When that functionality has been added we should also look into forwarding reactive form
+ // controls errors so that we don't need a separate input/output `invalidSecret`.
+ if (value) {
+ this.secret.markAsTouched();
+ }
+ this.secret.updateValueAndValidity({ emitEvent: false });
+ }
+ @Output() invalidSecretChange = new EventEmitter();
+
+ usesKeyConnector = true;
disableRequestOTP = false;
sentCode = false;
- secret = new FormControl("");
+ secret = new FormControl("", [
+ Validators.required,
+ () => {
+ if (this.invalidSecret) {
+ return {
+ invalidSecret: {
+ message: this.usesKeyConnector
+ ? this.i18nService.t("incorrectCode")
+ : this.i18nService.t("incorrectPassword"),
+ },
+ };
+ }
+ },
+ ]);
private onChange: (value: Verification) => void;
+ private destroy$ = new Subject();
constructor(
private keyConnectorService: KeyConnectorService,
- private userVerificationService: UserVerificationService
+ private userVerificationService: UserVerificationService,
+ private i18nService: I18nService
) {}
async ngOnInit() {
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
this.processChanges(this.secret.value);
- // eslint-disable-next-line rxjs-angular/prefer-takeuntil
- this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret));
+ this.secret.valueChanges
+ .pipe(takeUntil(this.destroy$))
+ .subscribe((secret: string) => this.processChanges(secret));
}
- async requestOTP() {
+ requestOTP = async () => {
if (this.usesKeyConnector) {
this.disableRequestOTP = true;
try {
@@ -49,7 +87,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
this.disableRequestOTP = false;
}
}
- }
+ };
writeValue(obj: any): void {
this.secret.setValue(obj);
@@ -72,7 +110,14 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
}
}
- private processChanges(secret: string) {
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ protected processChanges(secret: string) {
+ this.invalidSecret = false;
+
if (this.onChange == null) {
return;
}
diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts
index 234359a523..08b790c3f2 100644
--- a/libs/angular/src/services/jslib-services.module.ts
+++ b/libs/angular/src/services/jslib-services.module.ts
@@ -18,8 +18,6 @@ import { OrganizationUserService } from "@bitwarden/common/abstractions/organiza
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
-import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
-import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
@@ -48,6 +46,8 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarde
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
+import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
+import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AccountApiServiceImplementation } from "@bitwarden/common/auth/services/account-api.service";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
diff --git a/libs/angular/src/tools/export/components/export.component.ts b/libs/angular/src/tools/export/components/export.component.ts
index d7d3a1fc4c..595a6ebdfd 100644
--- a/libs/angular/src/tools/export/components/export.component.ts
+++ b/libs/angular/src/tools/export/components/export.component.ts
@@ -3,9 +3,9 @@ import { UntypedFormBuilder, Validators } from "@angular/forms";
import { merge, startWith, Subject, takeUntil } from "rxjs";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EncryptedExportType, EventType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
diff --git a/libs/common/src/abstractions/userVerification/userVerification-api.service.abstraction.ts b/libs/common/src/auth/abstractions/user-verification/user-verification-api.service.abstraction.ts
similarity index 69%
rename from libs/common/src/abstractions/userVerification/userVerification-api.service.abstraction.ts
rename to libs/common/src/auth/abstractions/user-verification/user-verification-api.service.abstraction.ts
index 78e4099eee..b861ce4471 100644
--- a/libs/common/src/abstractions/userVerification/userVerification-api.service.abstraction.ts
+++ b/libs/common/src/auth/abstractions/user-verification/user-verification-api.service.abstraction.ts
@@ -1,4 +1,4 @@
-import { VerifyOTPRequest } from "../../auth/models/request/verify-otp.request";
+import { VerifyOTPRequest } from "../../models/request/verify-otp.request";
export abstract class UserVerificationApiServiceAbstraction {
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise;
diff --git a/libs/common/src/abstractions/userVerification/userVerification.service.abstraction.ts b/libs/common/src/auth/abstractions/user-verification/user-verification.service.abstraction.ts
similarity index 66%
rename from libs/common/src/abstractions/userVerification/userVerification.service.abstraction.ts
rename to libs/common/src/auth/abstractions/user-verification/user-verification.service.abstraction.ts
index dcfd52bf05..09e4063434 100644
--- a/libs/common/src/abstractions/userVerification/userVerification.service.abstraction.ts
+++ b/libs/common/src/auth/abstractions/user-verification/user-verification.service.abstraction.ts
@@ -1,5 +1,5 @@
-import { SecretVerificationRequest } from "../../auth/models/request/secret-verification.request";
-import { Verification } from "../../types/verification";
+import { Verification } from "../../../types/verification";
+import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
export abstract class UserVerificationService {
buildRequest: (
diff --git a/libs/common/src/auth/services/account-api.service.ts b/libs/common/src/auth/services/account-api.service.ts
index 05ec1b8453..270ec1a201 100644
--- a/libs/common/src/auth/services/account-api.service.ts
+++ b/libs/common/src/auth/services/account-api.service.ts
@@ -1,9 +1,9 @@
import { ApiService } from "../../abstractions/api.service";
-import { UserVerificationService } from "../../abstractions/userVerification/userVerification.service.abstraction";
import { LogService } from "../../platform/abstractions/log.service";
import { Verification } from "../../types/verification";
import { AccountApiService } from "../abstractions/account-api.service";
import { InternalAccountService } from "../abstractions/account.service";
+import { UserVerificationService } from "../abstractions/user-verification/user-verification.service.abstraction";
export class AccountApiServiceImplementation implements AccountApiService {
constructor(
diff --git a/libs/common/src/auth/services/user-verification/user-verification-api.service.ts b/libs/common/src/auth/services/user-verification/user-verification-api.service.ts
index 1e0d9b2c57..0f0eb16e92 100644
--- a/libs/common/src/auth/services/user-verification/user-verification-api.service.ts
+++ b/libs/common/src/auth/services/user-verification/user-verification-api.service.ts
@@ -1,5 +1,5 @@
import { ApiService } from "../../../abstractions/api.service";
-import { UserVerificationApiServiceAbstraction } from "../../../abstractions/userVerification/userVerification-api.service.abstraction";
+import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
import { VerifyOTPRequest } from "../../models/request/verify-otp.request";
export class UserVerificationApiService implements UserVerificationApiServiceAbstraction {
diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.ts b/libs/common/src/auth/services/user-verification/user-verification.service.ts
index 085c6ac572..0be8d3e729 100644
--- a/libs/common/src/auth/services/user-verification/user-verification.service.ts
+++ b/libs/common/src/auth/services/user-verification/user-verification.service.ts
@@ -1,8 +1,8 @@
-import { UserVerificationApiServiceAbstraction } from "../../../abstractions/userVerification/userVerification-api.service.abstraction";
-import { UserVerificationService as UserVerificationServiceAbstraction } from "../../../abstractions/userVerification/userVerification.service.abstraction";
import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { Verification } from "../../../types/verification";
+import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
+import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "../../enums/verification-type";
import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
import { VerifyOTPRequest } from "../../models/request/verify-otp.request";
diff --git a/libs/components/src/avatar/avatar.mdx b/libs/components/src/avatar/avatar.mdx
new file mode 100644
index 0000000000..c6c5ff78ba
--- /dev/null
+++ b/libs/components/src/avatar/avatar.mdx
@@ -0,0 +1,67 @@
+import { Meta, Story, Primary, Controls } from "@storybook/addon-docs";
+
+import * as stories from "./avatar.stories";
+
+
+
+# Avatar
+
+Avatars display a unique color that helps a user visually recognize their logged in account.
+
+A variance in color across the avatar component is important as it is used in Account Switching as a
+visual indicator to recognize which of a personal or work account a user is logged into.
+
+
+
+
+## Size
+
+### Large: 64px
+
+
+
+### Default: 48px
+
+
+
+### Small 28px
+
+
+
+## Background color
+
+The Background color can be set 3 ways. The color is generated using the following order of
+priority:
+
+- Color
+- ID
+- Text, usually set to the user's Name field
+
+
+Use the user 'ID' field if `Name` is not defined.
+
+
+## Outline
+
+If the avatar is displayed on one of the theme's `background` color variables or is interactive,
+display the avatar with a 1 pixel `secondary-500` border to meet WCAG AA graphic contrast guidelines
+for interactive elements.
+
+
+
+## Avatar as a button
+
+The Avatar can be used as a button.
+
+Typically this is only in the navigation on client apps where account switching is used and in the
+web app for the account menu indicator.
+
+When the avatar is used as a button, the following states should be used:
+
+`TODO:` [Jira add stories](https://bitwarden.atlassian.net/browse/CL-101) for button avatars.
+[See Figma](https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?type=design&node-id=9730-31746&mode=design&t=IjDIHDb6FZl6bUQW-4)
+
+## Accessibility
+
+Avatar background color should have 3.1:1 contrast with it’s background; or include the
+`secondary-500` border Avatar text should have 4.5:1 contrast with the avatar background color
diff --git a/libs/components/src/badge/badge.mdx b/libs/components/src/badge/badge.mdx
new file mode 100644
index 0000000000..1c91944997
--- /dev/null
+++ b/libs/components/src/badge/badge.mdx
@@ -0,0 +1,67 @@
+import { Meta, Story, Primary, Controls } from "@storybook/addon-docs";
+
+import * as stories from "./badge.stories";
+
+
+
+# Badge
+
+The Badge directive can be used on a `` (non clickable events), or an `` or `