diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 763b48ab1d9..d23cfa58283 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -90,7 +90,9 @@ apps/web/src/app/core @bitwarden/team-platform-dev apps/web/src/app/shared @bitwarden/team-platform-dev apps/web/src/translation-constants.ts @bitwarden/team-platform-dev # Workflows -.github/workflows/brew-bump-desktop.yml @bitwarden/team-platform-dev +# Any changes here should also be reflected in Renovate configuration +.github/workflows/automatic-issue-responses.yml @bitwarden/team-platform-dev +.github/workflows/automatic-pull-request-responses.yml @bitwarden/team-platform-dev .github/workflows/build-browser-target.yml @bitwarden/team-platform-dev .github/workflows/build-browser.yml @bitwarden/team-platform-dev .github/workflows/build-cli-target.yml @bitwarden/team-platform-dev @@ -100,10 +102,13 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev .github/workflows/build-web-target.yml @bitwarden/team-platform-dev .github/workflows/build-web.yml @bitwarden/team-platform-dev .github/workflows/chromatic.yml @bitwarden/team-platform-dev +.github/workflows/crowdin-pull.yml @bitwarden/team-platform-dev +.github/workflows/enforce-labels.yml @bitwarden/team-platform-dev .github/workflows/lint.yml @bitwarden/team-platform-dev .github/workflows/locales-lint.yml @bitwarden/team-platform-dev .github/workflows/repository-management.yml @bitwarden/team-platform-dev .github/workflows/scan.yml @bitwarden/team-platform-dev +.github/workflows/stale-bot.yml @bitwarden/team-platform-dev .github/workflows/test.yml @bitwarden/team-platform-dev .github/workflows/version-auto-bump.yml @bitwarden/team-platform-dev # ESLint custom rules @@ -152,6 +157,7 @@ apps/desktop/src/locales/en/messages.json apps/web/src/locales/en/messages.json ## BRE team owns these workflows ## +# Any changes here should also be reflected in Renovate configuration ## .github/workflows/brew-bump-desktop.yml @bitwarden/dept-bre .github/workflows/deploy-web.yml @bitwarden/dept-bre .github/workflows/publish-cli.yml @bitwarden/dept-bre @@ -159,13 +165,11 @@ apps/web/src/locales/en/messages.json .github/workflows/publish-web.yml @bitwarden/dept-bre .github/workflows/retrieve-current-desktop-rollout.yml @bitwarden/dept-bre .github/workflows/staged-rollout-desktop.yml @bitwarden/dept-bre - -## Shared ownership workflows ## -.github/workflows/release-browser.yml -.github/workflows/release-cli.yml -.github/workflows/release-desktop-beta.yml -.github/workflows/release-desktop.yml -.github/workflows/release-web.yml +.github/workflows/release-browser.yml @bitwarden/dept-bre +.github/workflows/release-cli.yml @bitwarden/dept-bre +.github/workflows/release-desktop-beta.yml @bitwarden/dept-bre +.github/workflows/release-desktop.yml @bitwarden/dept-bre +.github/workflows/release-web.yml @bitwarden/dept-bre ## Docker files have shared ownership ## **/Dockerfile diff --git a/.github/renovate.json5 b/.github/renovate.json5 index b9de0084c25..b898ffc8629 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,28 +1,65 @@ { $schema: "https://docs.renovatebot.com/renovate-schema.json", - extends: ["github>bitwarden/renovate-config"], // Extends our base config for pinned dependencies + extends: ["github>bitwarden/renovate-config"], // Extends our default configuration for pinned dependencies enabledManagers: ["cargo", "github-actions", "npm"], packageRules: [ { + // Group all build/test/lint workflows for GitHub Actions together for Platform + // Since they are code owners we don't need to assign a review team in Renovate + // Any changes here should also be reflected in CODEOWNERS groupName: "github-action minor", matchManagers: ["github-actions"], - matchUpdateTypes: ["minor"], - }, - { - matchManagers: ["cargo"], + matchFileNames: [ + "./github/workflows/automatic-issue-responses.yml", + "./github/workflows/automatic-pull-request-responses.yml", + "./github/workflows/build-browser.yml", + "./github/workflows/build-cli.yml", + "./github/workflows/build-desktop.yml", + "./github/workflows/build-web.yml", + "./github/workflows/chromatic.yml", + "./github/workflows/crowdin-pull.yml", + "./github/workflows/enforce-labels.yml", + "./github/workflows/lint.yml", + "./github/workflows/locales-lint.yml", + "./github/workflows/repository-management.yml", + "./github/workflows/scan.yml", + "./github/workflows/stale-bot.yml", + "./github/workflows/test.yml", + "./github/workflows/version-auto-bump.yml", + ], commitMessagePrefix: "[deps] Platform:", }, { - groupName: "napi", - matchPackageNames: ["napi", "napi-build", "napi-derive"], + // Group all release-related workflows for GitHub Actions together for BRE + // Since they are code owners we don't need to assign a review team in Renovate + // Any changes here should also be reflected in CODEOWNERS + groupName: "github-action minor", + matchManagers: ["github-actions"], + matchFileNames: [ + "./github/workflows/brew-bump-desktop.yml", + "./github/workflows/deploy-web.yml", + "./github/workflows/publish-cli.yml", + "./github/workflows/publish-desktop.yml", + "./github/workflows/publish-web.yml", + "./github/workflows/retrieve-current-desktop-rollout.yml", + "./github/workflows/staged-rollout-desktop.yml", + "./github/workflows/release-cli.yml", + "./github/workflows/release-desktop-beta.yml", + "./github/workflows/release-desktop.yml", + "./github/workflows/release-web.yml", + ], + commitMessagePrefix: "[deps] BRE:", }, { + // Disable major and minor updates for TypeScript and Zone.js because they are managed by Angular matchPackageNames: ["typescript", "zone.js"], matchUpdateTypes: ["major", "minor"], description: "Determined by Angular", enabled: false, }, { + // Disable major updates for core Angular dependencies because they are managed through ng update + // when we decide to upgrade. matchSourceUrls: [ "https://github.com/angular-eslint/angular-eslint", "https://github.com/angular/angular-cli", @@ -35,19 +72,27 @@ enabled: false, }, { + // Renovate should manage patch updates for TypeScript and Zone.js, despite ignoring major and minor matchPackageNames: ["typescript", "zone.js"], matchUpdateTypes: "patch", }, { + // We want to update all the Jest-related packages together, to reduce PR noise groupName: "jest", matchPackageNames: ["@types/jest", "jest", "ts-jest", "jest-preset-angular"], - matchUpdateTypes: "major", }, { + // We need to group all napi-related packages together to avoid build errors caused by version incompatibilities + groupName: "napi", + matchPackageNames: ["napi", "napi-build", "napi-derive"], + }, + { + // We need to group all macOS/iOS binding-related packages together to avoid build errors caused by version incompatibilities groupName: "macOS/iOS bindings", matchPackageNames: ["core-foundation", "security-framework", "security-framework-sys"], }, { + // We need to group all zbus-related packages together to avoid build errors caused by version incompatibilities groupName: "zbus", matchPackageNames: ["zbus", "zbus_polkit"], }, diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index e9602ae96c0..5d31edd3097 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -669,8 +669,8 @@ "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, - "verifyIdentity": { - "message": "Verify identity" + "verifyYourIdentity": { + "message": "Verify your identity" }, "weDontRecognizeThisDevice": { "message": "We don't recognize this device. Enter the code sent to your email to verify your identity." @@ -5163,5 +5163,8 @@ }, "updateDesktopAppOrDisableFingerprintDialogMessage": { "message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings." + }, + "changeAtRiskPassword": { + "message": "Change at-risk password" } } diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 76894b23d0a..8f5d754b554 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -171,7 +171,7 @@ const routes: Routes = [ data: { elevation: 1, pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, showBackButton: true, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, @@ -248,7 +248,7 @@ const routes: Routes = [ data: { pageIcon: DeviceVerificationIcon, pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, pageSubtitle: { key: "weDontRecognizeThisDevice", diff --git a/apps/browser/src/safari/safari/SafariWebExtensionHandler.swift b/apps/browser/src/safari/safari/SafariWebExtensionHandler.swift index 58d95f959be..d4ce360c32a 100644 --- a/apps/browser/src/safari/safari/SafariWebExtensionHandler.swift +++ b/apps/browser/src/safari/safari/SafariWebExtensionHandler.swift @@ -164,7 +164,15 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { break } - guard let accessControl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage, .userPresence], nil) else { + var flags: SecAccessControlCreateFlags = [.privateKeyUsage]; + // https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/biometryany + if #available(macOS 10.13.4, *) { + flags.insert(.biometryAny) + } else { + flags.insert(.touchIDAny) + } + + guard let accessControl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, flags, nil) else { let messageId = message?["messageId"] as? Int response.userInfo = [ SFExtensionMessageKey: [ diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 209691869f0..b9eae380ca0 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -37,8 +37,16 @@ import { IconButtonModule, SearchModule, ToastService, + CalloutModule, } from "@bitwarden/components"; -import { CipherViewComponent, CopyCipherFieldService } from "@bitwarden/vault"; +import { + ChangeLoginPasswordService, + CipherViewComponent, + CopyCipherFieldService, + DefaultChangeLoginPasswordService, + DefaultTaskService, + TaskService, +} from "@bitwarden/vault"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; @@ -82,10 +90,13 @@ type LoadAction = CipherViewComponent, AsyncActionsModule, PopOutComponent, + CalloutModule, ], providers: [ { provide: ViewPasswordHistoryService, useClass: BrowserViewPasswordHistoryService }, { provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService }, + { provide: TaskService, useClass: DefaultTaskService }, + { provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService }, ], }) export class ViewV2Component { diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 992f218f6c6..c0dcd9a504a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.2.2", + "version": "2025.2.1", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 1ebb1f8de39..19b92d4762a 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -97,7 +97,7 @@ const routes: Routes = [ ], data: { pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, @@ -126,7 +126,7 @@ const routes: Routes = [ data: { pageIcon: DeviceVerificationIcon, pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, pageSubtitle: { key: "weDontRecognizeThisDevice", diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index dce98cde9bc..f3b9e0818db 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -21,8 +21,8 @@ import { SsoComponentV1 } from "../auth/sso-v1.component"; import { TwoFactorOptionsComponentV1 } from "../auth/two-factor-options-v1.component"; import { TwoFactorComponentV1 } from "../auth/two-factor-v1.component"; import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component"; +import { SshAgentService } from "../autofill/services/ssh-agent.service"; import { PremiumComponent } from "../billing/app/accounts/premium.component"; -import { SshAgentService } from "../platform/services/ssh-agent.service"; import { AddEditCustomFieldsComponent } from "../vault/app/vault/add-edit-custom-fields.component"; import { AddEditComponent } from "../vault/app/vault/add-edit.component"; import { AttachmentsComponent } from "../vault/app/vault/attachments.component"; diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 3c831ae11dd..08efa4a592e 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -22,8 +22,8 @@ import { UserId } from "@bitwarden/common/types/guid"; import { KeyService as KeyServiceAbstraction } from "@bitwarden/key-management"; import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service"; +import { SshAgentService } from "../../autofill/services/ssh-agent.service"; import { I18nRendererService } from "../../platform/services/i18n.renderer.service"; -import { SshAgentService } from "../../platform/services/ssh-agent.service"; import { VersionService } from "../../platform/services/version.service"; import { NativeMessagingService } from "../../services/native-messaging.service"; diff --git a/apps/desktop/src/platform/main/main-ssh-agent.service.ts b/apps/desktop/src/autofill/main/main-ssh-agent.service.ts similarity index 100% rename from apps/desktop/src/platform/main/main-ssh-agent.service.ts rename to apps/desktop/src/autofill/main/main-ssh-agent.service.ts diff --git a/apps/desktop/src/platform/services/ssh-agent.service.ts b/apps/desktop/src/autofill/services/ssh-agent.service.ts similarity index 98% rename from apps/desktop/src/platform/services/ssh-agent.service.ts rename to apps/desktop/src/autofill/services/ssh-agent.service.ts index 120732068cc..87c6de75a95 100644 --- a/apps/desktop/src/platform/services/ssh-agent.service.ts +++ b/apps/desktop/src/autofill/services/ssh-agent.service.ts @@ -34,9 +34,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherType } from "@bitwarden/common/vault/enums"; import { DialogService, ToastService } from "@bitwarden/components"; -import { ApproveSshRequestComponent } from "../components/approve-ssh-request"; - -import { DesktopSettingsService } from "./desktop-settings.service"; +import { ApproveSshRequestComponent } from "../../platform/components/approve-ssh-request"; +import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; @Injectable({ providedIn: "root", diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 726b5c4b316..f8c59affa71 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -916,7 +916,7 @@ "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, - "verifyIdentity": { + "verifyYourIdentity": { "message": "Verify your Identity" }, "weDontRecognizeThisDevice": { @@ -3604,5 +3604,8 @@ }, "updateBrowserOrDisableFingerprintDialogMessage": { "message": "The browser extension you are using is out of date. Please update it or disable browser integration fingerprint validation in the desktop app settings." + }, + "changeAtRiskPassword": { + "message": "Change at-risk password" } } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 7e417e8e5a8..4e167f30ec8 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -30,6 +30,7 @@ import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@ import { DefaultBiometricStateService } from "@bitwarden/key-management"; /* eslint-enable import/no-restricted-paths */ +import { MainSshAgentService } from "./autofill/main/main-ssh-agent.service"; import { DesktopAutofillSettingsService } from "./autofill/services/desktop-autofill-settings.service"; import { DesktopBiometricsService } from "./key-management/biometrics/desktop.biometrics.service"; import { MainBiometricsIPCListener } from "./key-management/biometrics/main-biometrics-ipc.listener"; @@ -45,7 +46,6 @@ import { NativeAutofillMain } from "./platform/main/autofill/native-autofill.mai import { ClipboardMain } from "./platform/main/clipboard.main"; import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener"; import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service"; -import { MainSshAgentService } from "./platform/main/main-ssh-agent.service"; import { VersionMain } from "./platform/main/version.main"; import { DesktopSettingsService } from "./platform/services/desktop-settings.service"; import { ElectronLogMainService } from "./platform/services/electron-log.main.service"; diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index bd54446f4e4..cf7d528d36e 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.2.2", + "version": "2025.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.2.2", + "version": "2025.2.1", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 3c504913522..4296add59ec 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.2.2", + "version": "2025.2.1", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.html similarity index 100% rename from apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html rename to apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.html diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts similarity index 95% rename from apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts rename to apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts index 4058c1151fb..dc08b32ce36 100644 --- a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts @@ -20,7 +20,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService, ToastService } from "@bitwarden/components"; -import { GroupApiService, GroupView } from "../../../admin-console/organizations/core"; +import { SharedModule } from "../../../../shared"; +import { GroupApiService, GroupView } from "../../core"; import { AccessItemType, AccessItemValue, @@ -30,8 +31,7 @@ import { mapGroupToAccessItemView, mapUserToAccessItemView, PermissionMode, -} from "../../../admin-console/organizations/shared/components/access-selector"; -import { SharedModule } from "../../../shared"; +} from "../../shared/components/access-selector"; export interface BulkCollectionsDialogParams { organizationId: string; diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/index.ts b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/index.ts similarity index 100% rename from apps/web/src/app/vault/org-vault/bulk-collections-dialog/index.ts rename to apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/index.ts diff --git a/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts b/apps/web/src/app/admin-console/organizations/collections/collection-access-restricted.component.ts similarity index 96% rename from apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts rename to apps/web/src/app/admin-console/organizations/collections/collection-access-restricted.component.ts index 1e4280626fe..e5d10d19db0 100644 --- a/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/collection-access-restricted.component.ts @@ -2,8 +2,8 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { ButtonModule, NoItemsModule, svgIcon } from "@bitwarden/components"; -import { SharedModule } from "../../shared"; -import { CollectionDialogTabType } from "../components/collection-dialog"; +import { SharedModule } from "../../../shared"; +import { CollectionDialogTabType } from "../../../vault/components/collection-dialog"; const icon = svgIcon` diff --git a/apps/web/src/app/vault/org-vault/collection-badge/collection-badge.module.ts b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-badge.module.ts similarity index 67% rename from apps/web/src/app/vault/org-vault/collection-badge/collection-badge.module.ts rename to apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-badge.module.ts index 44c27e57c8d..56919ef79fb 100644 --- a/apps/web/src/app/vault/org-vault/collection-badge/collection-badge.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-badge.module.ts @@ -1,7 +1,7 @@ import { NgModule } from "@angular/core"; -import { SharedModule } from "../../../shared/shared.module"; -import { PipesModule } from "../../individual-vault/pipes/pipes.module"; +import { SharedModule } from "../../../../shared/shared.module"; +import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; import { CollectionNameBadgeComponent } from "./collection-name.badge.component"; diff --git a/apps/web/src/app/vault/org-vault/collection-badge/collection-name-badge.component.html b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name-badge.component.html similarity index 100% rename from apps/web/src/app/vault/org-vault/collection-badge/collection-name-badge.component.html rename to apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name-badge.component.html diff --git a/apps/web/src/app/vault/org-vault/collection-badge/collection-name.badge.component.ts b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts similarity index 100% rename from apps/web/src/app/vault/org-vault/collection-badge/collection-name.badge.component.ts rename to apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts diff --git a/apps/web/src/app/vault/org-vault/group-badge/group-badge.module.ts b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-badge.module.ts similarity index 65% rename from apps/web/src/app/vault/org-vault/group-badge/group-badge.module.ts rename to apps/web/src/app/admin-console/organizations/collections/group-badge/group-badge.module.ts index 26ce689ed86..a209631bb23 100644 --- a/apps/web/src/app/vault/org-vault/group-badge/group-badge.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-badge.module.ts @@ -1,7 +1,7 @@ import { NgModule } from "@angular/core"; -import { SharedModule } from "../../../shared/shared.module"; -import { PipesModule } from "../../individual-vault/pipes/pipes.module"; +import { SharedModule } from "../../../../shared/shared.module"; +import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; import { GroupNameBadgeComponent } from "./group-name-badge.component"; diff --git a/apps/web/src/app/vault/org-vault/group-badge/group-name-badge.component.html b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html similarity index 100% rename from apps/web/src/app/vault/org-vault/group-badge/group-name-badge.component.html rename to apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html diff --git a/apps/web/src/app/vault/org-vault/group-badge/group-name-badge.component.ts b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts similarity index 92% rename from apps/web/src/app/vault/org-vault/group-badge/group-name-badge.component.ts rename to apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts index 4ed145a732f..8e5f261bc26 100644 --- a/apps/web/src/app/vault/org-vault/group-badge/group-name-badge.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts @@ -5,7 +5,7 @@ import { Component, Input, OnChanges } from "@angular/core"; import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { GroupView } from "../../../admin-console/organizations/core"; +import { GroupView } from "../../core"; @Component({ selector: "app-group-badge", diff --git a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts similarity index 89% rename from apps/web/src/app/vault/org-vault/vault-filter/vault-filter.component.ts rename to apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts index 7e08af7c7f7..73973e7ffde 100644 --- a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts @@ -12,18 +12,19 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { DialogService, ToastService } from "@bitwarden/components"; -import { VaultFilterComponent as BaseVaultFilterComponent } from "../../individual-vault/vault-filter/components/vault-filter.component"; //../../vault/vault-filter/components/vault-filter.component"; -import { VaultFilterService } from "../../individual-vault/vault-filter/services/abstractions/vault-filter.service"; +import { VaultFilterComponent as BaseVaultFilterComponent } from "../../../../vault/individual-vault/vault-filter/components/vault-filter.component"; +import { VaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; import { VaultFilterList, VaultFilterSection, VaultFilterType, -} from "../../individual-vault/vault-filter/shared/models/vault-filter-section.type"; -import { CollectionFilter } from "../../individual-vault/vault-filter/shared/models/vault-filter.type"; +} from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter-section.type"; +import { CollectionFilter } from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter.type"; @Component({ selector: "app-organization-vault-filter", - templateUrl: "../../individual-vault/vault-filter/components/vault-filter.component.html", + templateUrl: + "../../../../vault/individual-vault/vault-filter/components/vault-filter.component.html", }) export class VaultFilterComponent extends BaseVaultFilterComponent diff --git a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.module.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts similarity index 72% rename from apps/web/src/app/vault/org-vault/vault-filter/vault-filter.module.ts rename to apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts index 13a69796441..a0dba839b22 100644 --- a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts @@ -2,8 +2,8 @@ import { NgModule } from "@angular/core"; import { SearchModule } from "@bitwarden/components"; -import { VaultFilterService as VaultFilterServiceAbstraction } from "../../individual-vault/vault-filter/services/abstractions/vault-filter.service"; -import { VaultFilterSharedModule } from "../../individual-vault/vault-filter/shared/vault-filter-shared.module"; +import { VaultFilterService as VaultFilterServiceAbstraction } from "../../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; +import { VaultFilterSharedModule } from "../../../../vault/individual-vault/vault-filter/shared/vault-filter-shared.module"; import { VaultFilterComponent } from "./vault-filter.component"; import { VaultFilterService } from "./vault-filter.service"; diff --git a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts similarity index 91% rename from apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts rename to apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts index e2d713649f5..f4b6f41fab6 100644 --- a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts @@ -11,8 +11,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { VaultFilterService as BaseVaultFilterService } from "../../individual-vault/vault-filter/services/vault-filter.service"; -import { CollectionFilter } from "../../individual-vault/vault-filter/shared/models/vault-filter.type"; +import { VaultFilterService as BaseVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/vault-filter.service"; +import { CollectionFilter } from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter.type"; @Injectable() export class VaultFilterService extends BaseVaultFilterService implements OnDestroy { diff --git a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.html b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html similarity index 100% rename from apps/web/src/app/vault/org-vault/vault-header/vault-header.component.html rename to apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html diff --git a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts similarity index 92% rename from apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts rename to apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts index e3c99231a86..22a7e4b432b 100644 --- a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts @@ -1,7 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +// FIXME: rename output bindings and then remove this line +/* eslint-disable @angular-eslint/no-output-on-prefix */ import { CommonModule } from "@angular/common"; -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; @@ -13,7 +15,6 @@ import { import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { ProductTierType } 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 { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; @@ -25,13 +26,13 @@ import { SimpleDialogOptions, } from "@bitwarden/components"; -import { HeaderModule } from "../../../layouts/header/header.module"; -import { SharedModule } from "../../../shared"; -import { CollectionDialogTabType } from "../../components/collection-dialog"; +import { HeaderModule } from "../../../../layouts/header/header.module"; +import { SharedModule } from "../../../../shared"; +import { CollectionDialogTabType } from "../../../../vault/components/collection-dialog"; import { All, RoutedVaultFilterModel, -} from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model"; +} from "../../../../vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; @Component({ standalone: true, @@ -47,7 +48,7 @@ import { JslibModule, ], }) -export class VaultHeaderComponent implements OnInit { +export class VaultHeaderComponent { protected All = All; protected Unassigned = Unassigned; @@ -97,11 +98,8 @@ export class VaultHeaderComponent implements OnInit { private dialogService: DialogService, private collectionAdminService: CollectionAdminService, private router: Router, - private configService: ConfigService, ) {} - async ngOnInit() {} - get title() { const headerType = this.i18nService.t("collections").toLowerCase(); diff --git a/apps/web/src/app/vault/org-vault/vault-routing.module.ts b/apps/web/src/app/admin-console/organizations/collections/vault-routing.module.ts similarity index 84% rename from apps/web/src/app/vault/org-vault/vault-routing.module.ts rename to apps/web/src/app/admin-console/organizations/collections/vault-routing.module.ts index 4c2ed048bd1..960ddf4397f 100644 --- a/apps/web/src/app/vault/org-vault/vault-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-routing.module.ts @@ -3,7 +3,7 @@ import { RouterModule, Routes } from "@angular/router"; import { canAccessVaultTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard"; +import { organizationPermissionsGuard } from "../guards/org-permissions.guard"; import { VaultComponent } from "./vault.component"; const routes: Routes = [ diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/admin-console/organizations/collections/vault.component.html similarity index 100% rename from apps/web/src/app/vault/org-vault/vault.component.html rename to apps/web/src/app/admin-console/organizations/collections/vault.component.html diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts similarity index 95% rename from apps/web/src/app/vault/org-vault/vault.component.ts rename to apps/web/src/app/admin-console/organizations/collections/vault.component.ts index f14a8fcf5ee..a02cc599a18 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -76,55 +76,55 @@ import { PasswordRepromptService, } from "@bitwarden/vault"; -import { GroupApiService, GroupView } from "../../admin-console/organizations/core"; -import { openEntityEventsDialog } from "../../admin-console/organizations/manage/entity-events.component"; import { ResellerWarning, ResellerWarningService, -} from "../../billing/services/reseller-warning.service"; -import { TrialFlowService } from "../../billing/services/trial-flow.service"; -import { FreeTrial } from "../../billing/types/free-trial"; -import { SharedModule } from "../../shared"; -import { VaultFilterService } from "../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; -import { VaultFilter } from "../../vault/individual-vault/vault-filter/shared/models/vault-filter.model"; -import { AssignCollectionsWebComponent } from "../components/assign-collections"; +} from "../../../billing/services/reseller-warning.service"; +import { TrialFlowService } from "../../../billing/services/trial-flow.service"; +import { FreeTrial } from "../../../billing/types/free-trial"; +import { SharedModule } from "../../../shared"; +import { AssignCollectionsWebComponent } from "../../../vault/components/assign-collections"; import { CollectionDialogAction, CollectionDialogTabType, openCollectionDialog, -} from "../components/collection-dialog"; +} from "../../../vault/components/collection-dialog"; import { VaultItemDialogComponent, VaultItemDialogMode, VaultItemDialogResult, -} from "../components/vault-item-dialog/vault-item-dialog.component"; -import { VaultItemEvent } from "../components/vault-items/vault-item-event"; -import { VaultItemsModule } from "../components/vault-items/vault-items.module"; +} from "../../../vault/components/vault-item-dialog/vault-item-dialog.component"; +import { VaultItemEvent } from "../../../vault/components/vault-items/vault-item-event"; +import { VaultItemsModule } from "../../../vault/components/vault-items/vault-items.module"; import { AttachmentDialogResult, AttachmentsV2Component, -} from "../individual-vault/attachments-v2.component"; +} from "../../../vault/individual-vault/attachments-v2.component"; import { BulkDeleteDialogResult, openBulkDeleteDialog, -} from "../individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component"; -import { RoutedVaultFilterBridgeService } from "../individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../individual-vault/vault-filter/services/routed-vault-filter.service"; -import { createFilterFunction } from "../individual-vault/vault-filter/shared/models/filter-function"; +} from "../../../vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component"; +import { VaultFilterService } from "../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; +import { RoutedVaultFilterBridgeService } from "../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; +import { RoutedVaultFilterService } from "../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; +import { createFilterFunction } from "../../../vault/individual-vault/vault-filter/shared/models/filter-function"; import { All, RoutedVaultFilterModel, -} from "../individual-vault/vault-filter/shared/models/routed-vault-filter.model"; -import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.component"; -import { getNestedCollectionTree } from "../utils/collection-utils"; +} from "../../../vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; +import { VaultFilter } from "../../../vault/individual-vault/vault-filter/shared/models/vault-filter.model"; +import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; +import { getNestedCollectionTree } from "../../../vault/utils/collection-utils"; +import { GroupApiService, GroupView } from "../core"; +import { openEntityEventsDialog } from "../manage/entity-events.component"; import { BulkCollectionsDialogComponent, BulkCollectionsDialogResult, } from "./bulk-collections-dialog"; import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component"; -import { AdminConsoleCipherFormConfigService } from "./services/admin-console-cipher-form-config.service"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; +import { VaultHeaderComponent } from "./vault-header/vault-header.component"; const BroadcasterSubscriptionId = "OrgVaultComponent"; const SearchTextDebounceInterval = 200; diff --git a/apps/web/src/app/vault/org-vault/vault.module.ts b/apps/web/src/app/admin-console/organizations/collections/vault.module.ts similarity index 57% rename from apps/web/src/app/vault/org-vault/vault.module.ts rename to apps/web/src/app/admin-console/organizations/collections/vault.module.ts index db8d2256f52..8a2f3fbc2f0 100644 --- a/apps/web/src/app/vault/org-vault/vault.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.module.ts @@ -1,10 +1,10 @@ import { NgModule } from "@angular/core"; -import { LooseComponentsModule } from "../../shared/loose-components.module"; -import { SharedModule } from "../../shared/shared.module"; -import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module"; -import { CollectionDialogModule } from "../components/collection-dialog"; -import { ViewComponent } from "../individual-vault/view.component"; +import { LooseComponentsModule } from "../../../shared/loose-components.module"; +import { SharedModule } from "../../../shared/shared.module"; +import { CollectionDialogModule } from "../../../vault/components/collection-dialog"; +import { OrganizationBadgeModule } from "../../../vault/individual-vault/organization-badge/organization-badge.module"; +import { ViewComponent } from "../../../vault/individual-vault/view.component"; import { CollectionBadgeModule } from "./collection-badge/collection-badge.module"; import { GroupBadgeModule } from "./group-badge/group-badge.module"; diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts index 71be67af810..e5c68b73546 100644 --- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts @@ -14,14 +14,14 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard"; -import { organizationRedirectGuard } from "../../admin-console/organizations/guards/org-redirect.guard"; -import { OrganizationLayoutComponent } from "../../admin-console/organizations/layouts/organization-layout.component"; import { deepLinkGuard } from "../../auth/guards/deep-link.guard"; -import { VaultModule } from "../../vault/org-vault/vault.module"; +import { VaultModule } from "./collections/vault.module"; import { isEnterpriseOrgGuard } from "./guards/is-enterprise-org.guard"; +import { organizationPermissionsGuard } from "./guards/org-permissions.guard"; +import { organizationRedirectGuard } from "./guards/org-redirect.guard"; import { AdminConsoleIntegrationsComponent } from "./integrations/integrations.component"; +import { OrganizationLayoutComponent } from "./layouts/organization-layout.component"; import { GroupsComponent } from "./manage/groups.component"; const routes: Routes = [ diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts index c5114c0be6a..0021d938f82 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts @@ -7,7 +7,9 @@ import { mock } from "jest-mock-extended"; import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +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 { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; @@ -15,6 +17,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; +import { ChangeLoginPasswordService, TaskService } from "@bitwarden/vault"; import { EmergencyViewDialogComponent } from "./emergency-view-dialog.component"; @@ -52,7 +55,34 @@ describe("EmergencyViewDialogComponent", () => { { provide: DIALOG_DATA, useValue: { cipher: mockCipher } }, { provide: AccountService, useValue: accountService }, ], - }).compileComponents(); + }) + .overrideComponent(EmergencyViewDialogComponent, { + remove: { + providers: [ + { provide: PlatformUtilsService, useValue: PlatformUtilsService }, + { + provide: ChangeLoginPasswordService, + useValue: ChangeLoginPasswordService, + }, + { provide: ConfigService, useValue: ConfigService }, + ], + }, + add: { + providers: [ + { + provide: TaskService, + useValue: mock(), + }, + { provide: PlatformUtilsService, useValue: mock() }, + { + provide: ChangeLoginPasswordService, + useValue: mock(), + }, + { provide: ConfigService, useValue: mock() }, + ], + }, + }) + .compileComponents(); fixture = TestBed.createComponent(EmergencyViewDialogComponent); component = fixture.componentInstance; diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts index 68423c50d88..0ca892b40bf 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts @@ -9,7 +9,7 @@ import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; -import { CipherViewComponent } from "@bitwarden/vault"; +import { CipherViewComponent, DefaultTaskService, TaskService } from "@bitwarden/vault"; import { WebViewPasswordHistoryService } from "../../../../vault/services/web-view-password-history.service"; @@ -33,6 +33,7 @@ class PremiumUpgradePromptNoop implements PremiumUpgradePromptService { providers: [ { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: PremiumUpgradePromptService, useClass: PremiumUpgradePromptNoop }, + { provide: TaskService, useClass: DefaultTaskService }, ], }) export class EmergencyViewDialogComponent { diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 091c5440d70..730a9ad04c2 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -522,7 +522,7 @@ const routes: Routes = [ ], data: { pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, @@ -542,7 +542,7 @@ const routes: Routes = [ ], data: { pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, @@ -619,7 +619,7 @@ const routes: Routes = [ data: { pageIcon: DeviceVerificationIcon, pageTitle: { - key: "verifyIdentity", + key: "verifyYourIdentity", }, pageSubtitle: { key: "weDontRecognizeThisDevice", diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index bec54cf2c54..d57cc6bc1b4 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -67,7 +67,6 @@ import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } f import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../tools/reports/pages/organizations/unsecured-websites-report.component"; import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../tools/reports/pages/organizations/weak-passwords-report.component"; /* eslint no-restricted-imports: "error" */ -import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component"; import { PremiumBadgeComponent } from "../vault/components/premium-badge.component"; import { AddEditCustomFieldsComponent } from "../vault/individual-vault/add-edit-custom-fields.component"; import { AddEditComponent } from "../vault/individual-vault/add-edit.component"; @@ -148,7 +147,6 @@ import { SharedModule } from "./shared.module"; SecurityComponent, SecurityKeysComponent, SelectableAvatarComponent, - SendAddEditComponent, SetPasswordComponent, SponsoredFamiliesComponent, SponsoringOrgRowComponent, @@ -212,7 +210,6 @@ import { SharedModule } from "./shared.module"; SecurityComponent, SecurityKeysComponent, SelectableAvatarComponent, - SendAddEditComponent, SetPasswordComponent, SponsoredFamiliesComponent, SponsoringOrgRowComponent, diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html deleted file mode 100644 index 7eade18a7c6..00000000000 --- a/apps/web/src/app/tools/send/add-edit.component.html +++ /dev/null @@ -1,286 +0,0 @@ -
- -
- {{ title }} - - - - {{ "sendDisabledWarning" | i18n }} - - - {{ "sendOptionsPolicyInEffect" | i18n }} -
    -
  • {{ "sendDisableHideEmailInEffect" | i18n }}
  • -
-
- - {{ "name" | i18n }} - - {{ "sendNameDesc" | i18n }} - -
- - {{ "whatTypeOfSend" | i18n }} - - - - {{ o.name }} - - - - -
- - - - {{ "sendTypeText" | i18n }} - - {{ "sendTextDesc" | i18n }} - - - - {{ "textHiddenByDefault" | i18n }} - - - - -
-
- {{ "file" | i18n }} -

- {{ send.file.fileName }} ({{ send.file.sizeName }}) -

-
- - {{ "file" | i18n }} -
- - {{ selectedFile?.name ?? ("noFileChosen" | i18n) }} -
- - {{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }} -
-
-
-

{{ "share" | i18n }}

- - - {{ "sendLinkLabel" | i18n }} - - - - - - {{ "copySendLinkOnSave" | i18n }} - -
-

- -

-
-
-
-
- - {{ "deletionDate" | i18n }} - - - - - - - {{ "deletionDateDesc" | i18n }} - -
-
- - {{ "deletionDate" | i18n }} - - {{ "deletionDateDesc" | i18n }} - -
-
- - - {{ "expirationDate" | i18n }} - - - - - - - - {{ "expirationDateDesc" | i18n }} - -
-
- - - {{ "expirationDate" | i18n }} - - - - - {{ "expirationDateDesc" | i18n }} - -
-
-
- - {{ "maxAccessCount" | i18n }} - - {{ "maxAccessCountDesc" | i18n }} - - - {{ "currentAccessCount" | i18n }} - - -
-
- - {{ "password" | i18n }} - {{ "newPassword" | i18n }} - - - - {{ "sendPasswordDesc" | i18n }} - -
- - {{ "notes" | i18n }} - - {{ "sendNotesDesc" | i18n }} - - - - - {{ "hideEmail" | i18n }} - - - - - {{ "disableThisSend" | i18n }} - -
-
- - - - - - - - diff --git a/apps/web/src/app/tools/send/add-edit.component.ts b/apps/web/src/app/tools/send/add-edit.component.ts deleted file mode 100644 index 4ce126a33bc..00000000000 --- a/apps/web/src/app/tools/send/add-edit.component.ts +++ /dev/null @@ -1,102 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; -import { DatePipe } from "@angular/common"; -import { Component, Inject } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; - -import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.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"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -@Component({ - selector: "app-send-add-edit", - templateUrl: "add-edit.component.html", -}) -export class AddEditComponent extends BaseAddEditComponent { - override componentName = "app-send-add-edit"; - protected selectedFile: File; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - datePipe: DatePipe, - sendService: SendService, - stateService: StateService, - messagingService: MessagingService, - policyService: PolicyService, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - formBuilder: FormBuilder, - billingAccountProfileStateService: BillingAccountProfileStateService, - protected dialogRef: DialogRef, - @Inject(DIALOG_DATA) params: { sendId: string }, - accountService: AccountService, - toastService: ToastService, - ) { - super( - i18nService, - platformUtilsService, - environmentService, - datePipe, - sendService, - messagingService, - policyService, - logService, - stateService, - sendApiService, - dialogService, - formBuilder, - billingAccountProfileStateService, - accountService, - toastService, - ); - - this.sendId = params.sendId; - } - - async copyLinkToClipboard(link: string): Promise { - // Copy function on web depends on the modal being open or not. Since this event occurs during a transition - // of the modal closing we need to add a small delay to make sure state of the DOM is consistent. - return new Promise((resolve) => { - window.setTimeout(() => resolve(super.copyLinkToClipboard(link)), 500); - }); - } - - protected setSelectedFile(event: Event) { - const fileInputEl = event.target; - const file = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; - this.selectedFile = file; - } - - submitAndClose = async () => { - this.formGroup.markAllAsTouched(); - if (this.formGroup.invalid) { - return; - } - - const success = await this.submit(); - if (success) { - this.dialogRef.close(); - } - }; - - deleteAndClose = async () => { - const success = await this.delete(); - if (success) { - this.dialogRef.close(); - } - }; -} diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html new file mode 100644 index 00000000000..34e28be1084 --- /dev/null +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.html @@ -0,0 +1,23 @@ + + + + + {{ "sendTypeText" | i18n }} + + + + {{ "sendTypeFile" | i18n }} + + + diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts new file mode 100644 index 00000000000..8cd052aa016 --- /dev/null +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts @@ -0,0 +1,63 @@ +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable, of, switchMap } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { BadgeModule, ButtonModule, DialogService, MenuModule } from "@bitwarden/components"; +import { DefaultSendFormConfigService, SendAddEditDialogComponent } from "@bitwarden/send-ui"; + +@Component({ + selector: "tools-new-send-dropdown", + templateUrl: "new-send-dropdown.component.html", + standalone: true, + imports: [JslibModule, CommonModule, ButtonModule, MenuModule, BadgeModule], + providers: [DefaultSendFormConfigService], +}) +/** + * A dropdown component that allows the user to create a new Send of a specific type. + */ +export class NewSendDropdownComponent { + /** If true, the plus icon will be hidden */ + @Input() hideIcon: boolean = false; + + /** SendType provided for the markup to pass back the selected type of Send */ + protected sendType = SendType; + + /** Indicates whether the user can access premium features. */ + protected canAccessPremium$: Observable; + + constructor( + private router: Router, + private billingAccountProfileStateService: BillingAccountProfileStateService, + private accountService: AccountService, + private dialogService: DialogService, + private addEditFormConfigService: DefaultSendFormConfigService, + ) { + this.canAccessPremium$ = this.accountService.activeAccount$.pipe( + switchMap((account) => + account + ? this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id) + : of(false), + ), + ); + } + + /** + * Opens the SendAddEditComponent for a new Send with the provided type. + * If has user does not have premium access and the type is File, the user will be redirected to the premium settings page. + * @param type The type of Send to create. + */ + async createSend(type: SendType) { + if (!(await firstValueFrom(this.canAccessPremium$)) && type === SendType.File) { + return await this.router.navigate(["settings/subscription/premium"]); + } + + const formConfig = await this.addEditFormConfigService.buildConfig("add", undefined, type); + + await SendAddEditDialogComponent.open(this.dialogService, { formConfig }); + } +} diff --git a/apps/web/src/app/tools/send/send.component.html b/apps/web/src/app/tools/send/send.component.html index 7aab50b33e5..6f690459bb0 100644 --- a/apps/web/src/app/tools/send/send.component.html +++ b/apps/web/src/app/tools/send/send.component.html @@ -11,11 +11,7 @@ - - + @@ -198,10 +194,11 @@ {{ "sendsNoItemsTitle" | i18n }} {{ "sendsNoItemsMessage" | i18n }} - + diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 1268e4bfb50..ed6cb9a2b3c 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, NgZone, ViewChild, OnInit, OnDestroy, ViewContainerRef } from "@angular/core"; +import { DialogRef } from "@angular/cdk/dialog"; +import { Component, NgZone, OnInit, OnDestroy } from "@angular/core"; import { lastValueFrom } from "rxjs"; import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; @@ -14,6 +15,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendId } from "@bitwarden/common/types/guid"; import { DialogService, NoItemsModule, @@ -21,24 +23,30 @@ import { TableDataSource, ToastService, } from "@bitwarden/components"; -import { NoSendsIcon } from "@bitwarden/send-ui"; +import { + DefaultSendFormConfigService, + NoSendsIcon, + SendFormConfig, + SendAddEditDialogComponent, + SendItemDialogResult, +} from "@bitwarden/send-ui"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; -import { AddEditComponent } from "./add-edit.component"; +import { NewSendDropdownComponent } from "./new-send/new-send-dropdown.component"; const BroadcasterSubscriptionId = "SendComponent"; @Component({ selector: "app-send", standalone: true, - imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule], + imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule, NewSendDropdownComponent], templateUrl: "send.component.html", + providers: [DefaultSendFormConfigService], }) export class SendComponent extends BaseSendComponent implements OnInit, OnDestroy { - @ViewChild("sendAddEdit", { read: ViewContainerRef, static: true }) - sendAddEditModalRef: ViewContainerRef; + private sendItemDialogRef?: DialogRef | undefined; noItemIcon = NoSendsIcon; override set filteredSends(filteredSends: SendView[]) { @@ -65,6 +73,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro sendApiService: SendApiService, dialogService: DialogService, toastService: ToastService, + private addEditFormConfigService: DefaultSendFormConfigService, ) { super( sendService, @@ -111,17 +120,41 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro return; } - await this.editSend(null); + const config = await this.addEditFormConfigService.buildConfig("add", null, 0); + + await this.openSendItemDialog(config); } async editSend(send: SendView) { - const dialog = this.dialogService.open(AddEditComponent, { - data: { - sendId: send == null ? null : send.id, - }, + const config = await this.addEditFormConfigService.buildConfig( + send == null ? "add" : "edit", + send == null ? null : (send.id as SendId), + send.type, + ); + + await this.openSendItemDialog(config); + } + + /** + * Opens the send item dialog. + * @param formConfig The form configuration. + * */ + async openSendItemDialog(formConfig: SendFormConfig) { + // Prevent multiple dialogs from being opened. + if (this.sendItemDialogRef) { + return; + } + + this.sendItemDialogRef = SendAddEditDialogComponent.open(this.dialogService, { + formConfig, }); - await lastValueFrom(dialog.closed); - await this.load(); + const result = await lastValueFrom(this.sendItemDialogRef.closed); + this.sendItemDialogRef = undefined; + + // If the dialog was closed by deleting the cipher, refresh the vault. + if (result === SendItemDialogResult.Deleted || result === SendItemDialogResult.Saved) { + await this.load(); + } } } diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 59275eb4e7c..881903e79e5 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -36,6 +36,7 @@ import { ToastService, } from "@bitwarden/components"; import { + ChangeLoginPasswordService, CipherAttachmentsComponent, CipherFormComponent, CipherFormConfig, @@ -43,6 +44,9 @@ import { CipherFormModule, CipherViewComponent, DecryptionFailureDialogComponent, + DefaultChangeLoginPasswordService, + DefaultTaskService, + TaskService, } from "@bitwarden/vault"; import { SharedModule } from "../../../shared/shared.module"; @@ -136,6 +140,8 @@ export enum VaultItemDialogResult { { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService }, RoutedVaultFilterService, + { provide: TaskService, useClass: DefaultTaskService }, + { provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService }, ], }) export class VaultItemDialogComponent implements OnInit, OnDestroy { diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.module.ts b/apps/web/src/app/vault/components/vault-items/vault-items.module.ts index ac0d0fb1947..5a0a98a84b2 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.module.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.module.ts @@ -5,11 +5,11 @@ import { RouterModule } from "@angular/router"; import { TableModule } from "@bitwarden/components"; +import { CollectionBadgeModule } from "../../../admin-console/organizations/collections/collection-badge/collection-badge.module"; +import { GroupBadgeModule } from "../../../admin-console/organizations/collections/group-badge/group-badge.module"; import { SharedModule } from "../../../shared/shared.module"; import { OrganizationBadgeModule } from "../../individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../individual-vault/pipes/pipes.module"; -import { CollectionBadgeModule } from "../../org-vault/collection-badge/collection-badge.module"; -import { GroupBadgeModule } from "../../org-vault/group-badge/group-badge.module"; import { VaultCipherRowComponent } from "./vault-cipher-row.component"; import { VaultCollectionRowComponent } from "./vault-collection-row.component"; diff --git a/apps/web/src/app/vault/individual-vault/vault.module.ts b/apps/web/src/app/vault/individual-vault/vault.module.ts index 712b86a9803..d400d44bf0d 100644 --- a/apps/web/src/app/vault/individual-vault/vault.module.ts +++ b/apps/web/src/app/vault/individual-vault/vault.module.ts @@ -1,9 +1,9 @@ import { NgModule } from "@angular/core"; +import { CollectionBadgeModule } from "../../admin-console/organizations/collections/collection-badge/collection-badge.module"; +import { GroupBadgeModule } from "../../admin-console/organizations/collections/group-badge/group-badge.module"; import { LooseComponentsModule, SharedModule } from "../../shared"; import { CollectionDialogModule } from "../components/collection-dialog"; -import { CollectionBadgeModule } from "../org-vault/collection-badge/collection-badge.module"; -import { GroupBadgeModule } from "../org-vault/group-badge/group-badge.module"; import { BulkDialogsModule } from "./bulk-action-dialogs/bulk-dialogs.module"; import { OrganizationBadgeModule } from "./organization-badge/organization-badge.module"; diff --git a/apps/web/src/app/vault/individual-vault/view.component.spec.ts b/apps/web/src/app/vault/individual-vault/view.component.spec.ts index 9bea7f14eb5..d1117258124 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.spec.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.spec.ts @@ -12,6 +12,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co 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"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; @@ -21,6 +22,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { ChangeLoginPasswordService, DefaultTaskService, TaskService } from "@bitwarden/vault"; import { ViewCipherDialogParams, ViewCipherDialogResult, ViewComponent } from "./view.component"; @@ -82,7 +84,33 @@ describe("ViewComponent", () => { }, }, ], - }).compileComponents(); + }) + .overrideComponent(ViewComponent, { + remove: { + providers: [ + { provide: TaskService, useClass: DefaultTaskService }, + { provide: PlatformUtilsService, useValue: PlatformUtilsService }, + { + provide: ChangeLoginPasswordService, + useValue: ChangeLoginPasswordService, + }, + ], + }, + add: { + providers: [ + { + provide: TaskService, + useValue: mock(), + }, + { provide: PlatformUtilsService, useValue: mock() }, + { + provide: ChangeLoginPasswordService, + useValue: mock(), + }, + ], + }, + }) + .compileComponents(); fixture = TestBed.createComponent(ViewComponent); component = fixture.componentInstance; 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 baae6f28bf1..7a2cf3bb2f4 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -26,7 +26,7 @@ import { DialogService, ToastService, } from "@bitwarden/components"; -import { CipherViewComponent } from "@bitwarden/vault"; +import { CipherViewComponent, DefaultTaskService, TaskService } from "@bitwarden/vault"; import { SharedModule } from "../../shared/shared.module"; import { WebVaultPremiumUpgradePromptService } from "../services/web-premium-upgrade-prompt.service"; @@ -74,6 +74,7 @@ export interface ViewCipherDialogCloseResult { providers: [ { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService }, + { provide: TaskService, useClass: DefaultTaskService }, ], }) export class ViewComponent implements OnInit { diff --git a/apps/web/src/connectors/webauthn-fallback.ts b/apps/web/src/connectors/webauthn-fallback.ts index 5410b89dcfa..3561f922e03 100644 --- a/apps/web/src/connectors/webauthn-fallback.ts +++ b/apps/web/src/connectors/webauthn-fallback.ts @@ -82,8 +82,8 @@ document.addEventListener("DOMContentLoaded", async () => { const titleForSmallerScreens = document.getElementById("title-smaller-screens"); const titleForLargerScreens = document.getElementById("title-larger-screens"); - titleForSmallerScreens.innerText = localeService.t("verifyIdentity"); - titleForLargerScreens.innerText = localeService.t("verifyIdentity"); + titleForSmallerScreens.innerText = localeService.t("verifyYourIdentity"); + titleForLargerScreens.innerText = localeService.t("verifyYourIdentity"); const subtitle = document.getElementById("subtitle"); subtitle.innerText = localeService.t("followTheStepsBelowToFinishLoggingIn"); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 22a67cbff28..49613fa5c7c 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -222,6 +222,9 @@ "notes": { "message": "Notes" }, + "privateNote": { + "message": "Private note" + }, "note": { "message": "Note" }, @@ -1200,7 +1203,7 @@ "authenticationSessionTimedOut": { "message": "The authentication session timed out. Please restart the login process." }, - "verifyIdentity": { + "verifyYourIdentity": { "message": "Verify your Identity" }, "weDontRecognizeThisDevice": { @@ -5105,12 +5108,40 @@ "requireSsoExemption": { "message": "Organization owners and admins are exempt from this policy's enforcement." }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, "sendTypeFile": { "message": "File" }, "sendTypeText": { "message": "Text" }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -5135,19 +5166,15 @@ "message": "Delete Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "deleteSendConfirmation": { - "message": "Are you sure you want to delete this Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "whatTypeOfSend": { - "message": "What type of Send is this?", + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -5160,21 +5187,6 @@ "maxAccessCount": { "message": "Maximum access count" }, - "maxAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "disabled": { "message": "Disabled" }, @@ -5201,13 +5213,6 @@ "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, - "disableThisSend": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "allSends": { "message": "All Sends" }, @@ -5218,6 +5223,9 @@ "pendingDeletion": { "message": "Pending deletion" }, + "hideTextByDefault": { + "message": "Hide text by default" + }, "expired": { "message": "Expired" }, @@ -5439,13 +5447,6 @@ "message": "Always show member’s email address with recipients when creating or editing a Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendOptionsPolicyInEffect": { - "message": "The following organization policies are currently in effect:" - }, - "sendDisableHideEmailInEffect": { - "message": "Users are not allowed to hide their email address from recipients when creating or editing a Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "modifiedPolicyId": { "message": "Modified policy $ID$.", "placeholders": { @@ -5545,27 +5546,6 @@ "personalOwnershipCheckboxDesc": { "message": "Remove individual ownership for organization users" }, - "textHiddenByDefault": { - "message": "When accessing the Send, hide the text by default", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to Send." - }, - "sendFileDesc": { - "message": "The file you want to Send." - }, - "copySendLinkOnSave": { - "message": "Copy the link to share this Send to my clipboard upon save." - }, - "sendLinkLabel": { - "message": "Send link", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "send": { "message": "Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -5714,6 +5694,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, "webAuthnFallbackMsg": { "message": "To verify your 2FA please click the button below." }, @@ -9875,9 +9858,15 @@ "learnMoreAboutApi": { "message": "Learn more about Bitwarden's API" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -10514,6 +10503,9 @@ "assignedExceedsAvailable": { "message": "Assigned seats exceed available seats." }, + "changeAtRiskPassword": { + "message": "Change at-risk password" + }, "removeUnlockWithPinPolicyTitle": { "message": "Remove Unlock with PIN" }, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index fced7b977cd..c02ac6e0968 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -5,7 +5,6 @@ */ export enum FeatureFlag { /* Admin Console Team */ - ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner", AccountDeprovisioning = "pm-10308-account-deprovisioning", VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission", @@ -64,7 +63,6 @@ const FALSE = false as boolean; */ export const DefaultFeatureFlagValue = { /* Admin Console Team */ - [FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE, [FeatureFlag.AccountDeprovisioning]: FALSE, [FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE, [FeatureFlag.LimitItemDeletion]: FALSE, diff --git a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.html b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.html new file mode 100644 index 00000000000..d3f3ebedf49 --- /dev/null +++ b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.html @@ -0,0 +1,35 @@ + + + {{ headerText }} + + + + + + + + + +
+ +
+
+
diff --git a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts new file mode 100644 index 00000000000..fd15c87920f --- /dev/null +++ b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts @@ -0,0 +1,179 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component, Inject } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { + AsyncActionsModule, + ButtonModule, + DialogService, + IconButtonModule, + SearchModule, + ToastService, + DialogModule, +} from "@bitwarden/components"; + +import { SendFormConfig, SendFormMode, SendFormModule } from "../send-form"; + +export interface SendItemDialogParams { + /** + * The configuration object for the dialog and form. + */ + formConfig: SendFormConfig; + + /** + * If true, the "edit" button will be disabled in the dialog. + */ + disableForm?: boolean; +} + +export enum SendItemDialogResult { + /** + * A Send was saved (created or updated). + */ + Saved = "saved", + + /** + * A Send was deleted. + */ + Deleted = "deleted", +} + +/** + * Component for adding or editing a send item. + */ +@Component({ + templateUrl: "send-add-edit-dialog.component.html", + standalone: true, + imports: [ + CommonModule, + SearchModule, + JslibModule, + FormsModule, + ButtonModule, + IconButtonModule, + SendFormModule, + AsyncActionsModule, + DialogModule, + ], +}) +export class SendAddEditDialogComponent { + /** + * The header text for the component. + */ + headerText: string; + + /** + * The configuration for the send form. + */ + config: SendFormConfig; + + constructor( + @Inject(DIALOG_DATA) protected params: SendItemDialogParams, + private dialogRef: DialogRef, + private i18nService: I18nService, + private sendApiService: SendApiService, + private toastService: ToastService, + private dialogService: DialogService, + ) { + this.config = params.formConfig; + this.headerText = this.getHeaderText(this.config.mode, this.config.sendType); + } + + /** + * Handles the event when the send is created. + */ + async onSendCreated(send: SendView) { + // FIXME Add dialogService.open send-created dialog + this.dialogRef.close(SendItemDialogResult.Saved); + return; + } + + /** + * Handles the event when the send is updated. + */ + async onSendUpdated(send: SendView) { + this.dialogRef.close(SendItemDialogResult.Saved); + } + + /** + * Handles the event when the send is deleted. + */ + async onSendDeleted() { + this.dialogRef.close(SendItemDialogResult.Deleted); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedSend"), + }); + } + + /** + * Handles the deletion of the current Send. + */ + deleteSend = async () => { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "deleteSend" }, + content: { key: "deleteSendPermanentConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.sendApiService.delete(this.config.originalSend?.id); + } catch (e) { + this.toastService.showToast({ + variant: "error", + title: null, + message: e.message, + }); + return; + } + + await this.onSendDeleted(); + }; + + /** + * Gets the header text based on the mode and type. + * @param mode The mode of the send form. + * @param type The type of the send + * @returns The header text. + */ + private getHeaderText(mode: SendFormMode, type: SendType) { + const headerKey = + mode === "edit" || mode === "partial-edit" ? "editItemHeader" : "newItemHeader"; + + switch (type) { + case SendType.Text: + return this.i18nService.t(headerKey, this.i18nService.t("textSend")); + case SendType.File: + return this.i18nService.t(headerKey, this.i18nService.t("fileSend")); + } + } + + /** + * Opens the send add/edit dialog. + * @param dialogService Instance of the DialogService. + * @param params The parameters for the dialog. + * @returns The dialog result. + */ + static open(dialogService: DialogService, params: SendItemDialogParams) { + return dialogService.open( + SendAddEditDialogComponent, + { + data: params, + }, + ); + } +} diff --git a/libs/tools/send/send-ui/src/index.ts b/libs/tools/send/send-ui/src/index.ts index d208709c36d..db4416a13f0 100644 --- a/libs/tools/send/send-ui/src/index.ts +++ b/libs/tools/send/send-ui/src/index.ts @@ -1,6 +1,7 @@ export * from "./icons"; export * from "./send-form"; export { NewSendDropdownComponent } from "./new-send-dropdown/new-send-dropdown.component"; +export * from "./add-edit/send-add-edit-dialog.component"; export { SendListItemsContainerComponent } from "./send-list-items-container/send-list-items-container.component"; export { SendItemsService } from "./services/send-items.service"; export { SendSearchComponent } from "./send-search/send-search.component"; diff --git a/libs/vault/src/cipher-view/cipher-view.component.html b/libs/vault/src/cipher-view/cipher-view.component.html index f0ebeecdf40..def98b2fe96 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.html +++ b/libs/vault/src/cipher-view/cipher-view.component.html @@ -3,6 +3,19 @@ {{ "cardExpiredMessage" | i18n }}
+ + + + + {{ "changeAtRiskPassword" | i18n }} + + + +

- + a?.id)); + activeUserId$ = getUserId(this.accountService.activeAccount$); /** * Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the @@ -68,12 +75,18 @@ export class CipherViewComponent implements OnChanges, OnDestroy { folder$: Observable | undefined; private destroyed$: Subject = new Subject(); cardIsExpired: boolean = false; + hadPendingChangePasswordTask: boolean = false; + isSecurityTasksEnabled$ = this.configService.getFeatureFlag$(FeatureFlag.SecurityTasks); constructor( private organizationService: OrganizationService, private collectionService: CollectionService, private folderService: FolderService, private accountService: AccountService, + private defaultTaskService: TaskService, + private platformUtilsService: PlatformUtilsService, + private changeLoginPasswordService: ChangeLoginPasswordService, + private configService: ConfigService, ) {} async ngOnChanges() { @@ -137,7 +150,11 @@ export class CipherViewComponent implements OnChanges, OnDestroy { ); } - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + const userId = await firstValueFrom(this.activeUserId$); + + if (this.cipher.edit && this.cipher.viewPassword) { + await this.checkPendingChangePasswordTasks(userId); + } if (this.cipher.organizationId && userId) { this.organization$ = this.organizationService @@ -147,15 +164,29 @@ export class CipherViewComponent implements OnChanges, OnDestroy { } if (this.cipher.folderId) { - const activeUserId = await firstValueFrom(this.activeUserId$); - - if (!activeUserId) { - return; - } - this.folder$ = this.folderService - .getDecrypted$(this.cipher.folderId, activeUserId) + .getDecrypted$(this.cipher.folderId, userId) .pipe(takeUntil(this.destroyed$)); } } + + async checkPendingChangePasswordTasks(userId: UserId): Promise { + const tasks = await firstValueFrom(this.defaultTaskService.pendingTasks$(userId)); + + this.hadPendingChangePasswordTask = tasks?.some((task) => { + return ( + task.cipherId === this.cipher?.id && task.type === SecurityTaskType.UpdateAtRiskCredential + ); + }); + } + + launchChangePassword = async () => { + if (this.cipher != null) { + const url = await this.changeLoginPasswordService.getChangePasswordUrl(this.cipher); + if (url == null) { + return; + } + this.platformUtilsService.launchUri(url); + } + }; } diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html index 8503604bf7c..6de6fb6848d 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html @@ -89,6 +89,12 @@ (click)="logCopyEvent()" > + + + {{ "changeAtRiskPassword" | i18n }} + + +

{ { provide: PlatformUtilsService, useValue: mock() }, { provide: ToastService, useValue: mock() }, { provide: I18nService, useValue: { t: (...keys: string[]) => keys.join(" ") } }, + { provide: ConfigService, useValue: mock() }, ], }).compileComponents(); diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts index c95b2040fd2..27d81f32ee6 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule, DatePipe } from "@angular/common"; -import { Component, inject, Input } from "@angular/core"; +import { Component, EventEmitter, inject, Input, Output } from "@angular/core"; import { Observable, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -10,6 +10,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { EventType } from "@bitwarden/common/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -17,6 +18,7 @@ import { SectionComponent, SectionHeaderComponent, TypographyModule, + LinkModule, IconButtonModule, BadgeModule, ColorPasswordModule, @@ -46,10 +48,14 @@ type TotpCodeValues = { ColorPasswordModule, BitTotpCountdownComponent, ReadOnlyCipherCardComponent, + LinkModule, ], }) export class LoginCredentialsViewComponent { @Input() cipher: CipherView; + @Input() activeUserId: UserId; + @Input() hadPendingChangePasswordTask: boolean; + @Output() handleChangePassword = new EventEmitter(); isPremium$: Observable = this.accountService.activeAccount$.pipe( switchMap((account) => @@ -59,6 +65,7 @@ export class LoginCredentialsViewComponent { showPasswordCount: boolean = false; passwordRevealed: boolean = false; totpCodeCopyObj: TotpCodeValues; + private datePipe = inject(DatePipe); constructor( @@ -111,4 +118,8 @@ export class LoginCredentialsViewComponent { this.cipher.organizationId, ); } + + launchChangePasswordEvent(): void { + this.handleChangePassword.emit(); + } } diff --git a/libs/vault/src/services/default-change-login-password.service.ts b/libs/vault/src/services/default-change-login-password.service.ts index 25648318c14..82f272f1ede 100644 --- a/libs/vault/src/services/default-change-login-password.service.ts +++ b/libs/vault/src/services/default-change-login-password.service.ts @@ -35,7 +35,7 @@ export class DefaultChangeLoginPasswordService implements ChangeLoginPasswordSer ]); if (!reliable || wellKnownChangeUrl == null) { - return cipher.login.uri; + return url.origin; } return wellKnownChangeUrl; diff --git a/package-lock.json b/package-lock.json index ab526f2730b..142a4e13c21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -230,7 +230,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.2.2", + "version": "2025.2.1", "hasInstallScript": true, "license": "GPL-3.0" },