From 8b0e8b9350b5e4baf873cb694c249e0fe066bf5b Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Tue, 24 Jun 2025 12:44:24 -0700 Subject: [PATCH 01/57] use note over secure note in menu ribbon (#15315) --- apps/desktop/src/main/menu/menu.file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index f132a464788..19ba5e99792 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -99,7 +99,7 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { }, { id: "typeSecureNote", - label: this.localize("typeSecureNote"), + label: this.localize("typeNote"), click: () => this.sendMessage("newSecureNote"), accelerator: "CmdOrCtrl+Shift+S", }, From ffd9072a980a31d15b93fb61206a7b328c859869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Wed, 25 Jun 2025 11:58:18 +0200 Subject: [PATCH 02/57] Enable asarIntegrity on Windows (#15215) --- apps/desktop/scripts/after-pack.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/desktop/scripts/after-pack.js b/apps/desktop/scripts/after-pack.js index cdb5e098440..997edf2ed6a 100644 --- a/apps/desktop/scripts/after-pack.js +++ b/apps/desktop/scripts/after-pack.js @@ -172,10 +172,8 @@ async function addElectronFuses(context) { // Currently, asar integrity is only implemented for macOS and Windows // https://www.electronjs.org/docs/latest/tutorial/asar-integrity - // On macOS, it works by default, but on Windows it requires the - // asarIntegrity feature of electron-builder v25, currently in alpha - // https://github.com/electron-userland/electron-builder/releases/tag/v25.0.0-alpha.10 - [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: platform === "darwin", + [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: + platform == "darwin" || platform == "win32", [FuseV1Options.OnlyLoadAppFromAsar]: true, From 1b441e8a0f3460ce78618a86c840c96be1c01c2e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 25 Jun 2025 07:25:41 -0700 Subject: [PATCH 03/57] fix(routing): [PM-22995] update routing and tests (#15320) Updates routing in 2 components to account for feature flag: `PM16117_SetInitialPasswordRefactor` --- libs/auth/src/angular/sso/sso.component.ts | 10 +- .../two-factor-auth.component.spec.ts | 96 ++++++++++++++----- .../two-factor-auth.component.ts | 10 +- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index b78ca098dea..07b59ac661f 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -23,10 +23,12 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { SsoPreValidateResponse } from "@bitwarden/common/auth/models/response/sso-pre-validate.response"; import { ClientType, HttpStatusCode } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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"; @@ -116,6 +118,7 @@ export class SsoComponent implements OnInit { private toastService: ToastService, private ssoComponentService: SsoComponentService, private loginSuccessHandlerService: LoginSuccessHandlerService, + private configService: ConfigService, ) { environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => { this.redirectUri = env.getWebVaultUrl() + "/sso-connector.html"; @@ -531,7 +534,12 @@ export class SsoComponent implements OnInit { } private async handleChangePasswordRequired(orgIdentifier: string) { - await this.router.navigate(["set-password-jit"], { + const isSetInitialPasswordRefactorFlagOn = await this.configService.getFeatureFlag( + FeatureFlag.PM16117_SetInitialPasswordRefactor, + ); + const route = isSetInitialPasswordRefactorFlagOn ? "set-initial-password" : "set-password-jit"; + + await this.router.navigate([route], { queryParams: { identifier: orgIdentifier, }, diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts index 00cad105f95..4ab3841e48e 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts @@ -27,6 +27,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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"; @@ -75,6 +76,7 @@ describe("TwoFactorAuthComponent", () => { let mockLoginSuccessHandlerService: MockProxy; let mockTwoFactorAuthCompCacheService: MockProxy; let mockAuthService: MockProxy; + let mockConfigService: MockProxy; let mockUserDecryptionOpts: { noMasterPassword: UserDecryptionOptions; @@ -110,6 +112,7 @@ describe("TwoFactorAuthComponent", () => { mockToastService = mock(); mockTwoFactorAuthCompService = mock(); mockAuthService = mock(); + mockConfigService = mock(); mockEnvService = mock(); mockLoginSuccessHandlerService = mock(); @@ -209,6 +212,7 @@ describe("TwoFactorAuthComponent", () => { useValue: mockTwoFactorAuthCompCacheService, }, { provide: AuthService, useValue: mockAuthService }, + { provide: ConfigService, useValue: mockConfigService }, ], }); @@ -225,22 +229,6 @@ describe("TwoFactorAuthComponent", () => { expect(component).toBeTruthy(); }); - // Shared tests - const testChangePasswordOnSuccessfulLogin = () => { - it("navigates to the component's defined change password route when user doesn't have a MP and key connector isn't enabled", async () => { - // Act - await component.submit("testToken"); - - // Assert - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate).toHaveBeenCalledWith(["set-password"], { - queryParams: { - identifier: component.orgSsoIdentifier, - }, - }); - }); - }; - describe("Standard 2FA scenarios", () => { describe("submit", () => { const token = "testToken"; @@ -280,20 +268,76 @@ describe("TwoFactorAuthComponent", () => { selectedUserDecryptionOptions.next(mockUserDecryptionOpts.noMasterPassword); }); - testChangePasswordOnSuccessfulLogin(); + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is ON", () => { + it("navigates to the /set-initial-password route when user doesn't have a MP and key connector isn't enabled", async () => { + // Arrange + mockConfigService.getFeatureFlag.mockResolvedValue(true); + + // Act + await component.submit("testToken"); + + // Assert + expect(mockRouter.navigate).toHaveBeenCalledTimes(1); + expect(mockRouter.navigate).toHaveBeenCalledWith(["set-initial-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); + + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is OFF", () => { + it("navigates to the /set-password route when user doesn't have a MP and key connector isn't enabled", async () => { + // Arrange + mockConfigService.getFeatureFlag.mockResolvedValue(false); + + // Act + await component.submit("testToken"); + + // Assert + expect(mockRouter.navigate).toHaveBeenCalledTimes(1); + expect(mockRouter.navigate).toHaveBeenCalledWith(["set-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); }); - it("does not navigate to the change password route when the user has key connector even if user has no master password", async () => { - selectedUserDecryptionOptions.next( - mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, - ); + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is ON", () => { + it("does not navigate to the /set-initial-password route when the user has key connector even if user has no master password", async () => { + mockConfigService.getFeatureFlag.mockResolvedValue(true); - await component.submit(token, remember); + selectedUserDecryptionOptions.next( + mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, + ); - expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-password"], { - queryParams: { - identifier: component.orgSsoIdentifier, - }, + await component.submit(token, remember); + + expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-initial-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); + }); + }); + + describe("Given the PM16117_SetInitialPasswordRefactor feature flag is OFF", () => { + it("does not navigate to the /set-password route when the user has key connector even if user has no master password", async () => { + mockConfigService.getFeatureFlag.mockResolvedValue(false); + + selectedUserDecryptionOptions.next( + mockUserDecryptionOpts.noMasterPasswordWithKeyConnector, + ); + + await component.submit(token, remember); + + expect(mockRouter.navigate).not.toHaveBeenCalledWith(["set-password"], { + queryParams: { + identifier: component.orgSsoIdentifier, + }, + }); }); }); }); diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts index b811d48a48f..43a63498634 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts @@ -32,7 +32,9 @@ import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-p import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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"; @@ -169,6 +171,7 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { private loginSuccessHandlerService: LoginSuccessHandlerService, private twoFactorAuthComponentCacheService: TwoFactorAuthComponentCacheService, private authService: AuthService, + private configService: ConfigService, ) {} async ngOnInit() { @@ -559,7 +562,12 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { } private async handleChangePasswordRequired(orgIdentifier: string | undefined) { - await this.router.navigate(["set-password"], { + const isSetInitialPasswordRefactorFlagOn = await this.configService.getFeatureFlag( + FeatureFlag.PM16117_SetInitialPasswordRefactor, + ); + const route = isSetInitialPasswordRefactorFlagOn ? "set-initial-password" : "set-password"; + + await this.router.navigate([route], { queryParams: { identifier: orgIdentifier, }, From 1df54c71bee71561e25fcef64852a030245362ad Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 25 Jun 2025 07:29:22 -0700 Subject: [PATCH 04/57] refactor(account-recovery): [PM-18721][PM-21272] Integrate InputPasswordComponent in AccountRecoveryDialogComponent (#14662) Integrates the `InputPasswordComponent` within the new `AccountRecoveryDialogComponent`. Feature flag: `PM16117_ChangeExistingPasswordRefactor` --- .../common/base-members.component.ts | 2 +- .../account-recovery-dialog.component.html | 21 +++ .../account-recovery-dialog.component.ts | 146 ++++++++++++++++++ .../components/account-recovery/index.ts | 1 + .../components/reset-password.component.ts | 15 +- .../members/members.component.ts | 40 ++++- ...rganization-user-reset-password.service.ts | 4 +- apps/web/src/locales/en/messages.json | 3 + .../input-password.component.ts | 11 +- 9 files changed, 229 insertions(+), 14 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.html create mode 100644 apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/components/account-recovery/index.ts diff --git a/apps/web/src/app/admin-console/common/base-members.component.ts b/apps/web/src/app/admin-console/common/base-members.component.ts index 488af7ee518..624615edd6a 100644 --- a/apps/web/src/app/admin-console/common/base-members.component.ts +++ b/apps/web/src/app/admin-console/common/base-members.component.ts @@ -86,7 +86,7 @@ export abstract class BaseMembersComponent { protected i18nService: I18nService, protected keyService: KeyService, protected validationService: ValidationService, - private logService: LogService, + protected logService: LogService, protected userNamePipe: UserNamePipe, protected dialogService: DialogService, protected organizationManagementPreferencesService: OrganizationManagementPreferencesService, diff --git a/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.html new file mode 100644 index 00000000000..7fa063364e3 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.html @@ -0,0 +1,21 @@ + + + {{ "resetPasswordLoggedOutWarning" | i18n: loggedOutWarningName }} + + + + + + + + + + diff --git a/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.ts new file mode 100644 index 00000000000..3240b8d707a --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/account-recovery-dialog.component.ts @@ -0,0 +1,146 @@ +import { CommonModule } from "@angular/common"; +import { Component, Inject, ViewChild } from "@angular/core"; +import { switchMap } from "rxjs"; + +import { InputPasswordComponent, InputPasswordFlow } from "@bitwarden/auth/angular"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { + AsyncActionsModule, + ButtonModule, + CalloutModule, + DIALOG_DATA, + DialogConfig, + DialogModule, + DialogRef, + DialogService, + ToastService, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +import { OrganizationUserResetPasswordService } from "../../services/organization-user-reset-password/organization-user-reset-password.service"; + +/** + * Encapsulates a few key data inputs needed to initiate an account recovery + * process for the organization user in question. + */ +export type AccountRecoveryDialogData = { + /** + * The organization user's full name + */ + name: string; + + /** + * The organization user's email address + */ + email: string; + + /** + * The `organizationUserId` for the user + */ + organizationUserId: string; + + /** + * The organization's `organizationId` + */ + organizationId: OrganizationId; +}; + +export const AccountRecoveryDialogResultType = { + Ok: "ok", +} as const; + +export type AccountRecoveryDialogResultType = + (typeof AccountRecoveryDialogResultType)[keyof typeof AccountRecoveryDialogResultType]; + +/** + * Used in a dialog for initiating the account recovery process against a + * given organization user. An admin will access this form when they want to + * reset a user's password and log them out of sessions. + */ +@Component({ + standalone: true, + selector: "app-account-recovery-dialog", + templateUrl: "account-recovery-dialog.component.html", + imports: [ + AsyncActionsModule, + ButtonModule, + CalloutModule, + CommonModule, + DialogModule, + I18nPipe, + InputPasswordComponent, + ], +}) +export class AccountRecoveryDialogComponent { + @ViewChild(InputPasswordComponent) + inputPasswordComponent: InputPasswordComponent | undefined = undefined; + + masterPasswordPolicyOptions$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)), + ); + + inputPasswordFlow = InputPasswordFlow.ChangePasswordDelegation; + + get loggedOutWarningName() { + return this.dialogData.name != null ? this.dialogData.name : this.i18nService.t("thisUser"); + } + + constructor( + @Inject(DIALOG_DATA) protected dialogData: AccountRecoveryDialogData, + private accountService: AccountService, + private dialogRef: DialogRef, + private i18nService: I18nService, + private policyService: PolicyService, + private resetPasswordService: OrganizationUserResetPasswordService, + private toastService: ToastService, + ) {} + + handlePrimaryButtonClick = async () => { + if (!this.inputPasswordComponent) { + throw new Error("InputPasswordComponent is not initialized"); + } + + const passwordInputResult = await this.inputPasswordComponent.submit(); + if (!passwordInputResult) { + return; + } + + await this.resetPasswordService.resetMasterPassword( + passwordInputResult.newPassword, + this.dialogData.email, + this.dialogData.organizationUserId, + this.dialogData.organizationId, + ); + + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("resetPasswordSuccess"), + }); + + this.dialogRef.close(AccountRecoveryDialogResultType.Ok); + }; + + /** + * Strongly typed helper to open an `AccountRecoveryDialogComponent` + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param dialogConfig Configuration for the dialog + */ + static open = ( + dialogService: DialogService, + dialogConfig: DialogConfig< + AccountRecoveryDialogData, + DialogRef + >, + ) => { + return dialogService.open( + AccountRecoveryDialogComponent, + dialogConfig, + ); + }; +} diff --git a/apps/web/src/app/admin-console/organizations/members/components/account-recovery/index.ts b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/index.ts new file mode 100644 index 00000000000..e15fb7b40ef --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/components/account-recovery/index.ts @@ -0,0 +1 @@ +export * from "./account-recovery-dialog.component"; diff --git a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts index 80f0745f6d5..961d5482d8a 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts @@ -13,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { DIALOG_DATA, DialogConfig, @@ -47,7 +48,7 @@ export type ResetPasswordDialogData = { /** * The organization's `organizationId` */ - organizationId: string; + organizationId: OrganizationId; }; // FIXME: update to use a const object instead of a typescript enum @@ -56,16 +57,18 @@ export enum ResetPasswordDialogResult { Ok = "ok", } +/** + * Used in a dialog for initiating the account recovery process against a + * given organization user. An admin will access this form when they want to + * reset a user's password and log them out of sessions. + * + * @deprecated Use the `AccountRecoveryDialogComponent` instead. + */ @Component({ selector: "app-reset-password", templateUrl: "reset-password.component.html", standalone: false, }) -/** - * Used in a dialog for initiating the account recovery process against a - * given organization user. An admin will access this form when they want to - * reset a user's password and log them out of sessions. - */ export class ResetPasswordComponent implements OnInit, OnDestroy { formGroup = this.formBuilder.group({ newPassword: ["", Validators.required], diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 49c57f5e5a6..94f268cde21 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -52,6 +52,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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; @@ -66,6 +67,10 @@ import { GroupApiService } from "../core"; import { OrganizationUserView } from "../core/views/organization-user.view"; import { openEntityEventsDialog } from "../manage/entity-events.component"; +import { + AccountRecoveryDialogComponent, + AccountRecoveryDialogResultType, +} from "./components/account-recovery/account-recovery-dialog.component"; import { BulkConfirmDialogComponent } from "./components/bulk/bulk-confirm-dialog.component"; import { BulkDeleteDialogComponent } from "./components/bulk/bulk-delete-dialog.component"; import { BulkEnableSecretsManagerDialogComponent } from "./components/bulk/bulk-enable-sm-dialog.component"; @@ -749,11 +754,44 @@ export class MembersComponent extends BaseMembersComponent } async resetPassword(user: OrganizationUserView) { + const changePasswordRefactorFlag = await this.configService.getFeatureFlag( + FeatureFlag.PM16117_ChangeExistingPasswordRefactor, + ); + + if (changePasswordRefactorFlag) { + if (!user || !user.email || !user.id) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("orgUserDetailsNotFound"), + }); + this.logService.error("Org user details not found when attempting account recovery"); + + return; + } + + const dialogRef = AccountRecoveryDialogComponent.open(this.dialogService, { + data: { + name: this.userNamePipe.transform(user), + email: user.email, + organizationId: this.organization.id as OrganizationId, + organizationUserId: user.id, + }, + }); + + const result = await lastValueFrom(dialogRef.closed); + if (result === AccountRecoveryDialogResultType.Ok) { + await this.load(); + } + + return; + } + const dialogRef = ResetPasswordComponent.open(this.dialogService, { data: { name: this.userNamePipe.transform(user), email: user != null ? user.email : null, - organizationId: this.organization.id, + organizationId: this.organization.id as OrganizationId, id: user != null ? user.id : null, }, }); diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index ecf4d26eb52..d54e12c0ee7 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -14,7 +14,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; import { Argon2KdfConfig, @@ -96,7 +96,7 @@ export class OrganizationUserResetPasswordService newMasterPassword: string, email: string, orgUserId: string, - orgId: string, + orgId: OrganizationId, ): Promise { const response = await this.organizationUserApiService.getOrganizationUserResetPasswordDetails( orgId, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 43c3cec090e..79197f1eb06 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -2225,6 +2225,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts index 79157cae901..2d469e89fcd 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -261,7 +261,7 @@ export class InputPasswordComponent implements OnInit { } } - submit = async () => { + submit = async (): Promise => { try { this.isSubmitting.emit(true); @@ -280,8 +280,7 @@ export class InputPasswordComponent implements OnInit { const checkForBreaches = this.formGroup.controls.checkForBreaches?.value ?? true; if (this.flow === InputPasswordFlow.ChangePasswordDelegation) { - await this.handleChangePasswordDelegationFlow(newPassword); - return; + return await this.handleChangePasswordDelegationFlow(newPassword); } if (!this.email) { @@ -388,6 +387,7 @@ export class InputPasswordComponent implements OnInit { // 5. Emit cryptographic keys and other password related properties this.onPasswordFormSubmit.emit(passwordInputResult); + return passwordInputResult; } catch (e) { this.validationService.showError(e); } finally { @@ -441,7 +441,9 @@ export class InputPasswordComponent implements OnInit { } } - private async handleChangePasswordDelegationFlow(newPassword: string) { + private async handleChangePasswordDelegationFlow( + newPassword: string, + ): Promise { const newPasswordVerified = await this.verifyNewPassword( newPassword, this.passwordStrengthScore, @@ -456,6 +458,7 @@ export class InputPasswordComponent implements OnInit { }; this.onPasswordFormSubmit.emit(passwordInputResult); + return passwordInputResult; } /** From cf6b087491243993f77fe614b8c78c43cdcb59bc Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:32:47 -0700 Subject: [PATCH 05/57] docs(password-management): [PM-18573] Document Master Password Management Flows (#15248) Adds documentation for our set/change password flows (master password management flows) --- .../src/auth/password-management/README.md | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 libs/angular/src/auth/password-management/README.md diff --git a/libs/angular/src/auth/password-management/README.md b/libs/angular/src/auth/password-management/README.md new file mode 100644 index 00000000000..ca5a6355fcb --- /dev/null +++ b/libs/angular/src/auth/password-management/README.md @@ -0,0 +1,216 @@ +# Master Password Management Flows + +The Auth Team manages several components that allow a user to either: + +1. Set an initial master password +2. Change an existing master password + +This document maps all of our password management flows to the components that handle them. + +
+ +**Table of Contents** + +> - [The Base `InputPasswordComponent`](#the-base-inputpasswordcomponent) +> - [Set Initial Password Flows](#set-initial-password-flows) +> - [Change Password Flows](#change-password-flows) + +
+ +**Acronyms** + +
    +
  • MP = "master password"
  • +
  • MPE = "master password encryption"
  • +
  • TDE = "trusted device encryption"
  • +
  • JIT provision = "just-in-time provision"
  • +
+ +
+ +## The Base `InputPasswordComponent` + +Central to our master password management flows is the base [InputPasswordComponent](https://components.bitwarden.com/?path=/docs/auth-input-password--docs), which is responsible for displaying the appropriate form fields in the UI, performing form validation, and generating appropriate cryptographic properties for each flow. This keeps our UI, validation, and key generation consistent across all master password management flows. + +
+ +## Set Initial Password Flows + + + + + + + + + + + + + + + + + + + + + + + + + +
FlowRoute
(on which user sets MP)
Component(s)
+
+ Account Registration +

+
    +
  1. Standard Flow
  2. +
    +
  3. Self Hosted Flow
  4. +
    +
  5. Email Invite Flows (🌐 web only)
  6. +
    +
+
/finish-signup + RegistrationFinishComponent +
+ - embeds InputPasswordComponent +
+ Trial Initiation (🌐 web only) + /trial-initiation or
/secrets-manager-trial-initiation
+ CompleteTrialInitiationComponent +
+ - embeds InputPasswordComponent +
+
+ Upon Authentication (an existing authed user) +

+
    +
  1. User JIT provisions* into an MPE org
  2. +
    +
  3. + User JIT provisions* into a TDE org with the "manage account recovery" permission +

    That is, the user was given this permission on invitation or by the time they JIT provision.

    +
  4. +
    +
  5. + TDE user permissions upgraded +

    TDE user authenticates after permissions were upgraded to include "manage account recovery".

    +
  6. +
    +
  7. + TDE offboarding +

    User authenticates after their org offboarded from TDE and is now a MPE org.

    +

    User must be on a trusted device to set MP, otherwise user must go through Account Recovery.

    +
  8. +
+
/set-initial-password + SetInitialPasswordComponent +
+ - embeds InputPasswordComponent +
+ +\* A note on JIT provisioned user flows: + +- Even though a JIT provisioned user is a brand-new user who was “just” created, we consider them to be an “existing authed user” _from the perspective of the set initial password flow_. This is because at the time they set their initial password, their account already exists in the database (before setting their password) and they have already authenticated via SSO. +- The same is not true in the _Account Registration_ flows above—that is, during account registration when a user reaches the `/finish-signup` or `/trial-initiation` page to set their initial password, their account does not yet exist in the database, and will only be created once they set an initial password. + +
+ +## Change Password Flows + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FlowRoute
(on which user changes MP)
Component(s)
+
+ Account Settings + (Docs) +
+ (🌐 web only) +

+

User changes MP via account settings.

+
+
+ /settings/security/password +
(security-routing.module.ts) +
+ PasswordSettingsComponent +
- embeds ChangePasswordComponent +
- embeds InputPasswordComponent +
+
+ Upon Authentication +

+
    +
  1. + Login with non-compliant MP after email accept (🌐 web only) +

    User clicks an org email invite link and logs in with their MP that does not meet the org’s policy requirements.

    +
  2. +
    +
  3. + Login with non-compliant MP +

    Existing org user logs in with their MP that does not meet updated org policy requirements.

    +
  4. +
    +
  5. + Login after Account Recovery +

    User logs in after their MP was reset via Account Recovery.

    +
  6. +
+
/change-password + ChangePasswordComponent +
- embeds InputPasswordComponent +
+
+ Emergency Access Takeover + (Docs) +
+ (🌐 web only) +

+

Emergency access Grantee changes the MP for the Grantor.

+
+
Grantee opens dialog while on /settings/emergency-access + EmergencyAccessTakeoverDialogComponent +
- embeds InputPasswordComponent +
+
+ Account Recovery + (Docs) +
+ (🌐 web only) +

+

Org member with "manage account recovery" permission changes the MP for another org user via Account Recovery.

+
+
Org member opens dialog while on /organizations/{org-id}/members + AccountRecoveryDialogComponent +
- embeds InputPasswordComponent +
From 400360801915c561ec1cd7971314db30ee1c9f98 Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Wed, 25 Jun 2025 12:51:04 -0400 Subject: [PATCH 06/57] Hide card option from context menu when user is affected by card policy (#15272) * Hide card option from context menu when user is affected by card policy * Remove unused code --- .../browser/main-context-menu-handler.spec.ts | 31 ++++++++++++++++++- .../browser/main-context-menu-handler.ts | 11 ++++++- .../browser/src/background/main.background.ts | 10 ++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts index 267a832a671..901d6595fc8 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts @@ -1,5 +1,5 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { of } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { @@ -22,6 +22,10 @@ import { UserId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + RestrictedCipherType, + RestrictedItemTypesService, +} from "@bitwarden/common/vault/services/restricted-item-types.service"; import { MainContextMenuHandler } from "./main-context-menu-handler"; @@ -69,6 +73,8 @@ describe("context-menu", () => { let logService: MockProxy; let billingAccountProfileStateService: MockProxy; let accountService: MockProxy; + let restricted$: BehaviorSubject; + let restrictedItemTypesService: RestrictedItemTypesService; let removeAllSpy: jest.SpyInstance void]>; let createSpy: jest.SpyInstance< @@ -85,6 +91,10 @@ describe("context-menu", () => { logService = mock(); billingAccountProfileStateService = mock(); accountService = mock(); + restricted$ = new BehaviorSubject([]); + restrictedItemTypesService = { + restricted$, + } as Partial as RestrictedItemTypesService; removeAllSpy = jest .spyOn(chrome.contextMenus, "removeAll") @@ -105,6 +115,7 @@ describe("context-menu", () => { logService, billingAccountProfileStateService, accountService, + restrictedItemTypesService, ); jest.spyOn(MainContextMenuHandler, "remove"); @@ -147,6 +158,24 @@ describe("context-menu", () => { expect(createdMenu).toBeTruthy(); expect(createSpy).toHaveBeenCalledTimes(11); }); + + it("has menu enabled and has premium, but card type is restricted", async () => { + billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(true)); + + restricted$.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]); + + const createdMenu = await sut.init(); + expect(createdMenu).toBeTruthy(); + expect(createSpy).toHaveBeenCalledTimes(10); + }); + it("has menu enabled, does not have premium, and card type is restricted", async () => { + billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(false)); + restricted$.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]); + + const createdMenu = await sut.init(); + expect(createdMenu).toBeTruthy(); + expect(createSpy).toHaveBeenCalledTimes(9); + }); }); describe("loadOptions", () => { diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.ts index ad9dc34e501..abfa2465c51 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.ts @@ -25,8 +25,9 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { InitContextMenuItems } from "./abstractions/main-context-menu-handler"; @@ -157,6 +158,7 @@ export class MainContextMenuHandler { private logService: LogService, private billingAccountProfileStateService: BillingAccountProfileStateService, private accountService: AccountService, + private restrictedItemTypesService: RestrictedItemTypesService, ) {} /** @@ -181,6 +183,10 @@ export class MainContextMenuHandler { this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), ); + const isCardRestricted = ( + await firstValueFrom(this.restrictedItemTypesService.restricted$) + ).some((rt) => rt.cipherType === CipherType.Card); + for (const menuItem of this.initContextMenuItems) { const { requiresPremiumAccess, @@ -192,6 +198,9 @@ export class MainContextMenuHandler { if (requiresPremiumAccess && !hasPremium) { continue; } + if (menuItem.id.startsWith(AUTOFILL_CARD_ID) && isCardRestricted) { + continue; + } await MainContextMenuHandler.create({ ...otherOptions, contexts: ["all"] }); } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 4ba869768f5..3f448ca1b0c 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -437,6 +437,8 @@ export default class MainBackground { private popupViewCacheBackgroundService: PopupViewCacheBackgroundService; + private restrictedItemTypesService: RestrictedItemTypesService; + constructor() { // Services const lockedCallback = async (userId: UserId) => { @@ -1307,6 +1309,13 @@ export default class MainBackground { this.stateProvider, ); + this.restrictedItemTypesService = new RestrictedItemTypesService( + this.configService, + this.accountService, + this.organizationService, + this.policyService, + ); + this.mainContextMenuHandler = new MainContextMenuHandler( this.stateService, this.autofillSettingsService, @@ -1314,6 +1323,7 @@ export default class MainBackground { this.logService, this.billingAccountProfileStateService, this.accountService, + this.restrictedItemTypesService, ); this.cipherContextMenuHandler = new CipherContextMenuHandler( From 7403b38f3991a6db0c5893770f890d692f09a195 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:45:42 -0700 Subject: [PATCH 07/57] [CL-715] - [Defect] SSH key Private Key field visibility should toggle to hidden when switching items to view in desktop (#15224) * fix sshKey visibility * add missing ngIf * use two-way binding over explicit key --- .../sshkey-sections/sshkey-view.component.html | 1 + .../sshkey-sections/sshkey-view.component.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.html b/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.html index 2a31cd01c3a..e74c0b06818 100644 --- a/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.html +++ b/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.html @@ -19,6 +19,7 @@ bitIconButton bitPasswordInputToggle data-testid="toggle-privateKey" + [(toggled)]="revealSshKey" > @@ -116,7 +117,7 @@ export const WithLongContent: Story = {
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam? Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit.
-
+
Secondary Projected Content (optional)

Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Expedita, quod est?

@@ -133,9 +134,9 @@ export const WithThinPrimaryContent: Story = { // Projected content (the
's) and styling is just a sample and can be replaced with any content/styling. ` -
Lorem ipsum
+
Lorem ipsum
-
+
Secondary Projected Content (optional)
@@ -160,6 +161,27 @@ export const WithCustomIcon: Story = { }), }; +export const HideCardWrapper: Story = { + render: (args) => ({ + props: { + ...args, + hideCardWrapper: true, + }, + template: ` + +
+
Primary Projected Content Area (customizable)
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
+
+
+
Secondary Projected Content (optional)
+ +
+
+ `, + }), +}; + export const HideIcon: Story = { render: (args) => ({ props: args, From 4c2475a515681e8b1a66c509bbc890de8af23736 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:05:37 -0400 Subject: [PATCH 22/57] [PM-22343] Bump non-cli to Node 22 (#15058) * Bump non-cli to Node 22 * Fix working-directory * Lets see what breaks * Maybe this works --- .github/workflows/build-cli.yml | 1 - .nvmrc | 2 +- apps/cli/.nvmrc | 1 + apps/cli/package.json | 4 ++++ package.json | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 apps/cli/.nvmrc diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 45d57bbe202..ac314a4c33a 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -71,7 +71,6 @@ jobs: - name: Get Node Version id: retrieve-node-version - working-directory: ./ run: | NODE_NVMRC=$(cat .nvmrc) NODE_VERSION=${NODE_NVMRC/v/''} diff --git a/.nvmrc b/.nvmrc index 9a2a0e219c9..53d1c14db37 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20 +v22 diff --git a/apps/cli/.nvmrc b/apps/cli/.nvmrc new file mode 100644 index 00000000000..9a2a0e219c9 --- /dev/null +++ b/apps/cli/.nvmrc @@ -0,0 +1 @@ +v20 diff --git a/apps/cli/package.json b/apps/cli/package.json index 2ec4e6f6970..ea94314c641 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -92,5 +92,9 @@ "semver": "7.7.2", "tldts": "7.0.1", "zxcvbn": "4.4.2" + }, + "engines": { + "node": "~20", + "npm": "~10" } } diff --git a/package.json b/package.json index 629e073c15b..b522113876c 100644 --- a/package.json +++ b/package.json @@ -231,7 +231,7 @@ "*.ts": "eslint --cache --cache-strategy content --fix" }, "engines": { - "node": "~20", + "node": "~22", "npm": "~10" } } From 352787a4984de7eaa6669e0c32b941cd44ac4c4e Mon Sep 17 00:00:00 2001 From: Andy Pixley <3723676+pixman20@users.noreply.github.com> Date: Thu, 26 Jun 2025 20:18:42 -0400 Subject: [PATCH 23/57] [BRE-973] Fixing desktop version to 2025.6.1 (#15358) --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 69c1b04e2d6..4a59d5bbcf0 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.6.3", + "version": "2025.6.1", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 1b8cfed2bf3..128cf94a09d 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.6.3", + "version": "2025.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.6.3", + "version": "2025.6.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 65f765a5c93..9c6d5712b6d 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.6.3", + "version": "2025.6.1", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index 38e0edd0665..c83580dd0d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -286,7 +286,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.6.3", + "version": "2025.6.1", "hasInstallScript": true, "license": "GPL-3.0" }, From eaf8afb4c371cf44b27033b3efb7847d9450501e Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 21:13:49 -0400 Subject: [PATCH 24/57] Autosync the updated translations (#15365) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ar/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/az/messages.json | 39 +++++++++++++++++-- apps/web/src/locales/be/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/bg/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/bn/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/bs/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ca/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/cs/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/cy/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/da/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/de/messages.json | 41 +++++++++++++++++--- apps/web/src/locales/el/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/en_GB/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/en_IN/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/eo/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/es/messages.json | 43 ++++++++++++++++++--- apps/web/src/locales/et/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/eu/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/fa/messages.json | 39 +++++++++++++++++-- apps/web/src/locales/fi/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/fil/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/fr/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/gl/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/he/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/hi/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/hr/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/hu/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/id/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/it/messages.json | 39 +++++++++++++++++-- apps/web/src/locales/ja/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ka/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/km/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/kn/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ko/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/lv/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ml/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/mr/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/my/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/nb/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ne/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/nl/messages.json | 39 +++++++++++++++++-- apps/web/src/locales/nn/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/or/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/pl/messages.json | 43 ++++++++++++++++++--- apps/web/src/locales/pt_BR/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/pt_PT/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ro/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/ru/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/si/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/sk/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/sl/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/sr_CS/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/sr_CY/messages.json | 43 ++++++++++++++++++--- apps/web/src/locales/sv/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/te/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/th/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/tr/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/uk/messages.json | 39 +++++++++++++++++-- apps/web/src/locales/vi/messages.json | 37 ++++++++++++++++-- apps/web/src/locales/zh_CN/messages.json | 49 +++++++++++++++++++----- apps/web/src/locales/zh_TW/messages.json | 37 ++++++++++++++++-- 62 files changed, 2130 insertions(+), 208 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index e1ee9515030..33c8aec11af 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Deaktiveer" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Noodtoegang afgekeur" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Persoonlike eienaarskap" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "hierdie gebruiker" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Een of meer organisasiebeleide stel die volgende eise aan die hoofwagwoord:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index fcbdbb57b4a..c96c0565eed 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "إيقاف" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "إلغاء الوصول" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "هذا المستخدم" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "تمت إعادة تعيين كلمة المرور بنجاح!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index f8c76e729fa..ac1280d6ad0 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "İki addımlı girişi qurmaq, Bitwarden hesabınızı birdəfəlik kilidləyə bilər. Geri qaytarma kodu, normal iki addımlı giriş provayderinizi artıq istifadə edə bilmədiyiniz hallarda (məs. cihazınızı itirəndə) hesabınıza müraciət etməyinizə imkan verir. Hesabınıza müraciəti itirsəniz, Bitwarden dəstəyi sizə kömək edə bilməyəcək. Geri qaytarma kodunuzu bir yerə yazmağınızı və ya çap etməyinizi və onu etibarlı bir yerdə saxlamağınızı məsləhət görürük." }, - "restrictedItemTypesPolicy": { - "message": "Kart element növünü sil" + "restrictedItemTypePolicy": { + "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Üzvlərin kart element növünü yaratmasına icazə verilməsin." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "İki addımlı giriş provayderinizə müraciəti itirdiyiniz halda, iki addımlı girişi söndürmək üçün təkistifadəlik geri qaytarma kodunu istifadə edə bilərsiniz. Bitwarden tövsiyə edir ki, geri qaytarma kodunuzu bir yerə yazıb güvənli bir yerdə saxlayın." @@ -2218,6 +2224,9 @@ "disable": { "message": "Sıradan çıxart" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Müraciəti ləğv et" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Fövqəladə hal müraciəti rədd edildi" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ üçün parol sıfırlandı. Artıq yeni parol ilə giriş edə bilərsiniz.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Fərdi sahiblik" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "bu istifadəçi" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Bir və ya daha çox təşkilat siyasəti, aşağıdakı tələbləri qarşılamaq üçün ana parolu tələb edir:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Parol sıfırlama uğurludur!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 485764d33f1..f0fc9db94c3 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Уключэнне двухэтапнага ўваходу можа цалкам заблакіраваць доступ да ўліковага запісу Bitwarden. Код аднаўлення дае магчымасць атрымаць доступ да вашага ўліковага запісу ў выпадку, калі вы не можаце скарыстацца звычайным спосабам пастаўшчыка двухэтапнага ўваходу (напрыклад, вы згубілі сваю прыладу). Падтрымка Bitwarden не зможа вам дапамагчы, калі вы згубіце доступ да свайго ўліковага запісу. Мы рэкамендуем вам запісаць або раздрукаваць код аднаўлення і захоўваць яго ў надзейным месцы." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Адключыць" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Адклікаць доступ" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Экстранны доступ адхілены" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Пароль скінуты для $USER$. Цяпер вы можаце ўвайсці з дапамогай новага пароля.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Выдаліць асабістае сховішча" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "гэты карыстальнік" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Адна або больш палітык арганізацыі патрабуе, каб асноўны пароль адпавядаў наступным патрабаванням:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Пароль паспяхова скінуты!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 29b076ce2ec..e059002f5ff 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Включването на двустепенна идентификация може завинаги да предотврати вписването ви в абонамента към Битуорден. Кодът за възстановяване ще ви позволи да достъпите абонамента дори и да имате проблем с доставчика на двустепенна идентификация (напр. ако изгубите устройството си). Дори и екипът по поддръжката към няма да ви помогне в такъв случай. Силно препоръчваме да отпечатате или запишете кодовете и да ги пазете на надеждно място." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Премахване на елемента за карти" }, - "restrictedItemTypesPolicyDesc": { - "message": "Да не се позволява на членовете да създават елементи от тип „карта“." + "restrictedItemTypePolicyDesc": { + "message": "Да не се разрешава на членовете да създават картови елементи. Съществуващите карти ще бъдат премахнати автоматично." + }, + "restrictCardTypeImport": { + "message": "Картовите елементи не могат да бъдат внесени" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, зададена от 1 или повече организации, не позволява да внасяте карти в трезорите си." }, "yourSingleUseRecoveryCode": { "message": "Вашият еднократен код за възстановяване може да бъде използван, за да изключите двустепенното удостоверяване, в случай че нямате достъп до доставчика си за двустепенно вписване. Битуорден препоръчва да запишете кода си за възстановяване и да го пазите на сигурно място." @@ -2218,6 +2224,9 @@ "disable": { "message": "Изключване" }, + "orgUserDetailsNotFound": { + "message": "Няма намерени подробности за члена." + }, "revokeAccess": { "message": "Отнемане на достъпа" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Извънредният достъп е отказан." }, + "grantorDetailsNotFound": { + "message": "Няма намерени подробности за Gantor" + }, "passwordResetFor": { "message": "Паролата на потребителя $USER$ е сменена и той може вече да влезе с новата.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Индивидуално притежание" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Действието ще прекрати текущата сесия на $NAME$, след което ще се наложи той/тя отново да се впише. Активните сесии на другите устройства може да останат такива до един час.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "този потребител" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Поне една политика на организация има следните изисквания към главната парола:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Поне една политика на организация има следните изисквания към главната парола:" + }, "resetPasswordSuccess": { "message": "Успешна смяна на паролата!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Адресът за таксуване е задължителен за добавянето на средства.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 8b80336304f..39f883a7be6 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 71f91d57180..4bf2dd3c8c8 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 6603ce8dbf8..5ba0d84fcdb 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Si habiliteu l'inici de sessió en dues passes, pot bloquejar-vos de manera definitiva el compte de Bitwarden. Un codi de recuperació us permet accedir al vostre compte en cas que no pugueu utilitzar el proveïdor d'inici de sessió en dues passes (p. Ex. Perdre el dispositiu). El suport de Bitwarden no podrà ajudar-vos si perdeu l'accés al vostre compte. Us recomanem que escriviu o imprimiu el codi de recuperació i el mantingueu en un lloc segur." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Inhabilita" }, + "orgUserDetailsNotFound": { + "message": "No s'han trobat les dades del membre." + }, "revokeAccess": { "message": "Revoca l'accés" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Accés d’emergència rebutjat" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Restabliment de la contrasenya per a $USER$. Ara podeu iniciar la sessió amb la nova contrasenya.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Suprimeix la caixa forta individual" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "aquest usuari" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Una o més polítiques d’organització requereixen que la vostra contrasenya principal complisca els requisits següents:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "S'ha restablert la contrasenya correctament!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 12731ea382e..5e022396a0e 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Nastavením dvoufázového přihlášení můžete sami sobě znemožnit přihlášení k Vašemu účtu. Obnovovací kód umožňuje přístup do Vašeho účtu i v případě, pokud již nemůžete použít svůj normální způsob dvoufázového přihlášení (např. ztráta zařízení). Pokud ztratíte přístup ke svému účtu, nebude Vám schopna pomoci ani zákaznická podpora Bitwardenu. Doporučujeme si proto kód pro obnovení zapsat nebo vytisknout a uložit jej na bezpečném místě." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Odebrat typ položky karty" }, - "restrictedItemTypesPolicyDesc": { - "message": "Nedovolí členům vytvářet typy položek karet." + "restrictedItemTypePolicyDesc": { + "message": "Nepovolovat členům vytvářet typy položek karty. Existující karty budou automaticky odebrány." + }, + "restrictCardTypeImport": { + "message": "Nelze importovat typy položek karty" + }, + "restrictCardTypeImportDesc": { + "message": "Zásady nastavené 1 nebo více organizací Vám brání v importu karet do Vašeho trezoru." }, "yourSingleUseRecoveryCode": { "message": "Jednorázový kód pro obnovení lze použít k vypnutí dvoufázového přihlašování v případě, že ztratíte přístup ke svému poskytovateli dvoufázového přihlašování. Bitwarden doporučuje, abyste si kód pro obnovení zapsali a uložili na bezpečném místě." @@ -2218,6 +2224,9 @@ "disable": { "message": "Vypnout" }, + "orgUserDetailsNotFound": { + "message": "Podrobnosti o členovi nebyly nalezeny" + }, "revokeAccess": { "message": "Zrušit přístup" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Nouzový přístup byl odmítnut" }, + "grantorDetailsNotFound": { + "message": "Údaje o zadavateli nebyly nalezeny" + }, "passwordResetFor": { "message": "Pro $USER$ bylo obnoveno heslo. Nyní se můžete přihlásit pomocí nového hesla.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Vynutit vlastnictví dat organizace" + }, "personalOwnership": { "message": "Odebrat osobní trezor" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Pokračováním odhlásíte $NAME$ z aktuální relace, což znamená, že se bude muset znovu přihlásit. Aktivní relace na jiných zařízeních mohou zůstat aktivní až po dobu jedné hodiny.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "tento uživatel" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Jedna nebo více zásad organizace vyžaduje, aby hlavní heslo splňovalo následující požadavky:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Jedna nebo více zásad organizace vyžaduje, aby hlavní heslo splňovalo následující požadavky:" + }, "resetPasswordSuccess": { "message": "Heslo bylo úspěšně resetováno!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Pro přidání kreditu je vyžadována fakturační adresa.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index bf319005c5e..9e9521a6869 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index ac5ed3c0a16..7c4f7459886 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Opsætning af totrins-login kan permanent låse en bruger ude af sin Bitwarden-konto. En gendannelseskode muliggør kontoadgang, såfremt den normale totrins-loginudbyder ikke længere kan bruges (f.eks. ved tab af en enhed). Bitwarden-supporten kan ikke hjælpe ved mistet kontoadgang. Det anbefales at nedskrive/udskrive gendannelseskoden samt gemme denne et sikkert sted." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Deaktivér" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Ophæv adgang" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Nødadgang nægtet" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Adgangskode nulstillet for $USER$. Du kan nu logge ind med den nye adgangskode.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Fjern individuel boks" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "denne bruger" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Én eller flere organisationspolitikker kræver, at hovedadgangskoden opfylder flg. krav:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Adgangskode nulstillet!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 41a583b1d87..da3d2bd081f 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Durch die Aktivierung der Zwei-Faktor-Authentifizierung kannst du dich dauerhaft aus deinem Bitwarden-Konto aussperren. Ein Wiederherstellungscode ermöglicht es dir, auf dein Konto zuzugreifen, falls du deinen normalen Zwei-Faktor-Anbieter nicht mehr verwenden kannst (z.B. wenn du dein Gerät verlierst). Der Bitwarden-Support kann dir nicht helfen, wenn du den Zugang zu deinem Konto verlierst. Wir empfehlen dir, den Wiederherstellungscode aufzuschreiben oder auszudrucken und an einem sicheren Ort aufzubewahren." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Karten-Eintragstyp entfernen" }, - "restrictedItemTypesPolicyDesc": { - "message": "Mitgliedern das Erstellen von Karten-Eintragstypen nicht erlauben." + "restrictedItemTypePolicyDesc": { + "message": "Mitgliedern nicht erlauben, Karten-Eintragstypen zu erstellen. Vorhandene Karten werden automatisch entfernt." + }, + "restrictCardTypeImport": { + "message": "Karten-Eintragstypen können nicht importiert werden" + }, + "restrictCardTypeImportDesc": { + "message": "Eine von einer oder mehreren Organisationen festgelegte Richtlinie verhindert, dass du Karten in deinen Tresor importieren kannst." }, "yourSingleUseRecoveryCode": { "message": "Dein einmal benutzbarer Wiederherstellungscode kann benutzt werden, um die Zwei-Faktor-Authentifizierung auszuschalten, wenn du Zugang zu deinen Zwei-Faktor-Anbietern verlierst. Bitwarden empfiehlt dir, den Wiederherstellungscode aufzuschreiben und an einem sicheren Ort aufzubewahren." @@ -2218,6 +2224,9 @@ "disable": { "message": "Deaktivieren" }, + "orgUserDetailsNotFound": { + "message": "Mitgliederdetails nicht gefunden." + }, "revokeAccess": { "message": "Zugriff widerrufen" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Notfallzugriff abgelehnt" }, + "grantorDetailsNotFound": { + "message": "Angaben zur gewährenden Person nicht gefunden" + }, "passwordResetFor": { "message": "Passwort für $USER$ zurückgesetzt. Du kannst dich jetzt mit dem neuen Passwort anmelden.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Eigentumsrechte an Unternehmensdaten erzwingen" + }, "personalOwnership": { "message": "Persönlichen Tresor entfernen" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Wenn du fortfährst, wird $NAME$ aus seiner aktuellen Sitzung abgemeldet und muss sich erneut anmelden. Aktive Sitzungen auf anderen Geräten können bis zu einer Stunde weiterhin aktiv bleiben.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "dieser Benutzer" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Eine oder mehrere Organisationsrichtlinien erfordern, dass dein Master-Passwort die folgenden Anforderungen erfüllt:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Eine oder mehrere Organisationsrichtlinien erfordern, dass dein Master-Passwort die folgenden Anforderungen erfüllt:" + }, "resetPasswordSuccess": { "message": "Passwort erfolgreich zurückgesetzt!" }, @@ -7615,7 +7642,7 @@ "description": "Notifies that a service account has been updated" }, "typeOrSelectProjects": { - "message": "Type or select projects", + "message": "Projektnamen eingeben oder auswählen", "description": "Instructions for selecting projects for a service account" }, "newSaTypeToFilter": { @@ -7702,7 +7729,7 @@ "description": "Title for the section displaying access tokens." }, "createAccessToken": { - "message": "Create access token", + "message": "Zugriffstoken erstellen", "description": "Button label for creating a new access token." }, "expires": { @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Rechnungsadresse erforderlich, um Guthaben hinzuzufügen.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index de75b5c538b..815c58a25ec 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Η ενεργοποίηση σύνδεσης δύο βημάτων μπορεί να κλειδώσει οριστικά το λογαριασμό σας από το Bitwarden. Ένας κωδικός ανάκτησης σάς επιτρέπει να έχετε πρόσβαση στον λογαριασμό σας σε περίπτωση που δεν μπορείτε πλέον να χρησιμοποιήσετε τη σύνδεση δύο βημάτων (π. χ. χάνετε τη συσκευή σας). Η υποστήριξη πελατών του Bitwarden δεν θα είναι σε θέση να σας βοηθήσει αν χάσετε την πρόσβαση στο λογαριασμό σας. Συνιστούμε να γράψετε ή να εκτυπώσετε τον κωδικό ανάκτησης και να τον φυλάξετε σε ασφαλές μέρος." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Απενεργοποίηση" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Ανάκληση πρόσβασης" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Η πρόσβαση έκτακτης ανάγκης απορρίφθηκε" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Επαναφορά κωδικού πρόσβασης για το $USER$. Τώρα μπορείτε να συνδεθείτε χρησιμοποιώντας το νέο κωδικό πρόσβασης.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Προσωπική Ιδιοκτησία" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "αυτός ο χρήστης" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Σε μία ή περισσότερες πολιτικές του οργανισμού απαιτείται ο κύριος κωδικός να πληρεί τις ακόλουθες απαιτήσεις:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Επιτυχία επαναφοράς κωδικού!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index f749cf65f54..e3f279af8f4 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organisation data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organisation policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organisation policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 0be4e496f74..4d5f5ff177d 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (e.g. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Disable" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organisation data ownership" + }, "personalOwnership": { "message": "Personal Ownership" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organisation policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organisation policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 0da9c976af1..1366fc7c492 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Ebligi du-paŝan ensaluton povas konstante elŝlosi vin el via Bitwarden-konto. Rekuperiga kodo permesas vin aliri vian konton, se vi ne plu povas uzi vian normalan du-paŝan ensalutan provizanton (ekz. vi perdas Bitwarden-subteno ne povos helpi vin se vi perdos aliron al via konto. Ni rekomendas al vi skribi aŭ presi la reakiran kodon kaj konservi ĝin en sekura loko. " }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Malŝalti" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Krizaliro malakceptita" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Pravaloriziĝis la pasvorto de $USER$. Vi nun povas saluti per la nova pasvorto.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Persona Posedo" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ĉi tiu uzanto" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 02a4ec0de38..2bc0abb9c00 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -108,7 +108,7 @@ "message": "At-risk passwords" }, "requestPasswordChange": { - "message": "Request password change" + "message": "Solicitar cambio de contraseña" }, "totalPasswords": { "message": "Total de contraseñas" @@ -1495,7 +1495,7 @@ "message": "Usa un Yubikey para acceder a tu cuenta. Funciona con YubiKey 4, 4 Nano, 4C y dispositivos NEO." }, "duoDescV2": { - "message": "Enter a code generated by Duo Security.", + "message": "Introduce un código generado por Duo Security.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Habilitar la autenticación en dos pasos puede impedirte acceder permanentemente a tu cuenta de Bitwarden. Un código de recuperación te permite acceder a la cuenta en caso de que no puedas usar más tu proveedor de autenticación en dos pasos (ej. si pierdes tu dispositivo). El soporte de Bitwarden no será capaz de asistirte si pierdes acceso a tu cuenta. Te recomendamos que escribas o imprimas este código y lo guardes en un lugar seguro." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Desactivar" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revocar el acceso" }, @@ -4125,7 +4134,7 @@ } }, "freeTrialEndPromptTomorrowNoOrgName": { - "message": "Tu prueba gratuita finaliza mañana." + "message": "Tu prueba gratuita termina mañana." }, "freeTrialEndPromptToday": { "message": "$ORGANIZATION$, your free trial ends today.", @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Acceso de emergencia rechazado" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Restablecimiento de contraseña para $USER$. Ahora puede iniciar sesión usando la nueva contraseña.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Propiedad personal" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "este usuario" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Una o más políticas de organización requieren la contraseña maestra para cumplir con los siguientes requisitos:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "¡Contraseña restablecida!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index bf766409fbd..c718b718d15 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Kaheastmelise kinnitamine aktiveerimine võib luua olukorra, kus sul on võimatu oma Bitwardeni kontosse sisse logida. Näiteks kui kaotad oma nutiseadme. Taastamise kood võimaldab aga kontole ligi pääseda ka olukorras, kus kaheastmelist kinnitamist ei ole võimalik läbi viia. Sellistel juhtudel ei saa ka Bitwardeni klienditugi sinu kontole ligipääsu taastada. Selle tõttu soovitame taastekoodi välja printida ja seda turvalises kohas hoida." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Sinu ühekordseid taastamise koode saab kasutada selleks, et lülitada kahe-astmeline sisselogimine välja juhul, kui sa oled kaotanud juurdepääsu oma kahe-astmelise sisselogimise viisidele. Bitwarden soovitab sul kirjutada üles taastamise koodid ja hoiustada neid ohutus kohas." @@ -2218,6 +2224,9 @@ "disable": { "message": "Keela" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Tühistada ligipääsu luba" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Hädaolukorra ligipääsust keelduti" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ parool on lähtestatud. Saad nüüd uue parooliga sisse logida.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Personaalne salvestamine" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "see kasutaja" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Üks või enam organisatsiooni eeskirja nõuavad, et ülemparool vastaks nendele nõudmistele:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Parool on edukalt lähtestatud!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 4c520de1762..1b41987012c 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Bi urratseko saio hasiera gaitzeak betirako blokea dezake Bitwarden kontura sartzea. Berreskuratze-kode baten bidez, zure kontura sar zaitezke, bi urratseko saio hasierako hornitzailea erabili ezin baduzu (adb. gailua galtzen baduzu). Bitwarden-ek ezingo dizu lagundu zure konturako sarbidea galtzen baduzu. Berreskuratze-kodea idatzi edo inprimatzea eta leku seguruan edukitzea gomendatzen dugu." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Desgaitu" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Sarbidea ezeztatu" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Larrialdiko sarbidea ukatua" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$-(r)entzat pasahitza berrezartzea. Orain, saioa abiaraz dezakezu pasahitz berria erabiliz.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Ezabatu kutxa gotor pertsonala" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "erabiltzaile hau" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Erakundeko politika batek edo gehiagok pasahitz nagusia behar dute baldintza hauek betetzeko:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Pasahitza berrezarria!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 2be7366e75e..63ef193879a 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "راه‌اندازی ورود دو مرحله‌ای می‌تواند برای همیشه حساب Bitwarden شما را قفل کند. یک کد بازیابی به شما امکان می‌دهد در صورتی که دیگر نمی‌توانید از ارائه‌دهنده‌ی ورود دو مرحله‌ای معمولی خود استفاده کنید (به عنوان مثال: دستگاه خود را گم می‌کنید) به حساب خود دسترسی پیدا کنید. اگر دسترسی به حساب خود را از دست بدهید، پشتیبانی Bitwarden نمی‌تواند به شما کمک کند. توصیه می‌کنیم کد بازیابی را یادداشت یا چاپ کنید و آن را در مکانی امن نگهداری کنید." }, - "restrictedItemTypesPolicy": { - "message": "حذف نوع مورد کارت" + "restrictedItemTypePolicy": { + "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "اجازه نده اعضا نوع مورد کارت ایجاد کنند." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "کد بازیابی یک‌بار مصرف شما می‌تواند در صورت از دست دادن دسترسی به سرویس ورود دو مرحله‌ای، برای غیرفعال کردن آن استفاده شود. Bitwarden توصیه می‌کند این کد را یادداشت کرده و در جای امنی نگهداری کنید." @@ -2218,6 +2224,9 @@ "disable": { "message": "خاموش کردن" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "لغو دسترسی" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "دسترسی اضطراری رد شد" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "کلمه عبور برای $USER$ بازنشانی شد. اکنون می‌توانید با استفاده از کلمه عبور جدید وارد شوید.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "حذف گاوصندوق شخصی" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "این کاربر" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "یک یا چند سیاست سازمانی برای تأمین شرایط زیر به کلمه عبور اصلی احتیاج دارد:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "بازیابی رمزعبور با موفقیت انجام شد!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 7b948d6cbc8..607b17d7749 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Kaksivaiheisen kirjautumisen käyttöönotto voi lukita sinut ulos Bitwarden-tililtäsi pysyvästi. Palautuskoodi mahdollistaa pääsyn tilillesi myös silloin, kun et voi käyttää normaaleja kaksivaiheisen tunnistautumisen vahvistustapoja (esim. kadotat suojausavaimesi tai se varastetaan). Bitwardenin asiakaspalvelukaan ei voi auttaa sinua, jos menetät pääsyn tillesi. Suosittelemme, että kirjoitat palautuskoodin muistiin tai tulostat sen ja säilytät turvallisessa paikassa (esim. kassakaapissa tai pankin tallelokerossa)." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Kertakäyttöisellä palautuskoodillasi voit poistaa kaksivaiheisen kirjautumisen käytöstä, mikäli et voi käyttää kaksivaiheista todennustapaasi. Bitwarden suosittelee kirjoittamaan palautuskoodin ylös ja säilyttämään sen turvallisessa paikassa." @@ -2218,6 +2224,9 @@ "disable": { "message": "Poista käytöstä" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Mitätöi käyttöoikeudet" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Varmuuskäyttö hylätty." }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Käyttäjän $USER$ salasana on palautettu. Voit nyt kirjautua sisään käyttäen uutta salasanaa.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Poista yksityinen holvi" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "tämä käyttäjä" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Yksi tai useampi organisaatiokäytäntö edellyttää, että pääsalasanasi täyttää seuraavat vaatimukset:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Salasanan palautus onnistui!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 8d8524a070b..c8689d54bbc 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Maaaring permanente kang ma-lock out sa account mo dahil sa dalawang-hakbang na pag-log in. Pwede kang gumamit ng code pang-recover sakaling hindi mo na magamit ang normal mong provider ng dalawang-hakbang na pag-log in (halimbawa: nawala ang device mo). Hindi ka matutulungan ng Bitwarden support kung mawalan ka ng access sa account mo. Mainam na isulat o i-print mo ang mga code pang-recover at itago ito sa ligtas na lugar." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Isara" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Tanggalin ang access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Hindi tinanggap ang emergency access" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Pag-reset ng password para sa $USER$. Maaari ka na ngayong mag login gamit ang bagong password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Alisin ang indibidwal na vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ang user na ito" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Ang isa o higit pang mga patakaran sa organisasyon ay nangangailangan ng master password upang matugunan ang mga sumusunod na kinakailangan:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Tagumpay sa pag-reset ng password!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index adc8f91aaf7..0ebdfaa611c 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "La configuration d'un système d'authentification à deux facteurs peut définitivement vous verrouiller l'accès à votre compte Bitwarden. Un code de récupération vous permet d'accéder à votre compte dans le cas où vous ne pourriez plus utiliser votre fournisseur normal d'authentification à deux facteurs (exemple : vous perdez votre appareil). L'assistance de Bitwarden ne pourra pas vous aider si vous perdez l'accès à votre compte. Nous vous recommandons de noter ou d'imprimer le code de récupération et de le conserver en lieu sûr." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Supprimer le type d'élément de la carte" }, - "restrictedItemTypesPolicyDesc": { - "message": "Ne pas autoriser les membres à créer des types d'objets de carte." + "restrictedItemTypePolicyDesc": { + "message": "Ne pas autoriser les membres à créer des types d'éléments de carte. Les cartes existantes seront automatiquement supprimées." + }, + "restrictCardTypeImport": { + "message": "Impossible d'importer les types de l'élément de la carte" + }, + "restrictCardTypeImportDesc": { + "message": "Une politique de sécurité définie par 1 organisation ou plus vous empêche d'importer des cartes dans vos coffres." }, "yourSingleUseRecoveryCode": { "message": "Votre code de récupération à usage unique peut être utilisé pour désactiver la connexion en deux étapes si vous perdez l'accès à votre fournisseur de connexion en deux étapes. Bitwarden vous recommande d'écrire le code de récupération et de le conserver dans un endroit sûr." @@ -2218,6 +2224,9 @@ "disable": { "message": "Désactiver" }, + "orgUserDetailsNotFound": { + "message": "Informations sur le membre non trouvés." + }, "revokeAccess": { "message": "Révoquer l'Accès" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Accès d'urgence refusé." }, + "grantorDetailsNotFound": { + "message": "Détails de la source non trouvés" + }, "passwordResetFor": { "message": "Mot de passe réinitialisé pour $USER$. Vous pouvez maintenant vous connecter en utilisant le nouveau mot de passe.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Forcer la propriété des données de l'organisation" + }, "personalOwnership": { "message": "Supprimer le coffre individuel" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "En poursuivant, $NAME$ sera déconnecté de sa session actuelle, ce qui l'obligera à se reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives pendant encore une heure.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "cet utilisateur" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Une ou plusieurs politiques de sécurité de l'organisation exigent que votre mot de passe principal réponde aux exigences suivantes :" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Une ou plusieurs politiques de sécurité de l'organisation exigent que votre mot de passe principal réponde aux exigences suivantes :" + }, "resetPasswordSuccess": { "message": "Mot de passe réinitialisé avec succès !" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "L'adresse de facturation est requise pour ajouter du crédit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 0603a7e79f0..ad59838cc77 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 49fa7eda4aa..59f5dbc404b 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "הגדרת כניסה דו־שלבית יכולה לנעול אותך לצמיתות מחוץ לחשבון Bitwarden שלך. קוד שחזור מאפשר לך לגשת לחשבון שלך במקרה שאתה לא יכול להשתמש בספק הכניסה הד־שלבית הרגיל שלך (דוגמה: איבדת את המכשיר שלך). התמיכה של Bitwarden לא תוכל לסייע לך אם תאבד גישה לחשבון שלך. אנו ממליצים שתכתוב או תדפיס את קוד השחזור ותשמור אותו במקום בטוח." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "ניתן להשתמש בקוד השחזור החד־פעמי שלך כדי לכבות כניסה דו־שלבית במקרה שאתה מאבד גישה לספק הכניסה הדו־שלבית שלך. Bitwarden ממליץ לך לרשום את קוד השחזור ולשמור אותו במקום בטוח." @@ -2218,6 +2224,9 @@ "disable": { "message": "כבה" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "בטל גישה" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "גישת חירום נדחתה" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "הסיסמה אופסה עבור $USER$. אתה יכול כעת להיכנס באמצעות הסיסמה החדשה.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "הסר כספת אישית" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "משתמש זה" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "מדיניות ארגון אחת או יותר דורשת שהסיסמה הראשית תעמוד בדרישות הבאות:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "הצלחת איפוס סיסמה!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 2135be63cc2..250d68f1f0a 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 98f24ef51cd..d322231d2ad 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Uključivanje prijave dvostrukom autentifikacijom može ti trajno onemogućiti pristup Bitwarden računu. Kôd za oporavak ti omogućuje pristup računu u slučaju kada više ne možeš koristiti redovnog pružatelja prijave dvostrukom autentifikacijom (npr. izgubiš svoj uređaj). Bitwarden podrška neće ti moći pomoći ako izgubiš pristup svojem računu. Savjetujemo da zapišeš ili ispišeš kôd za oporavak i spremiš ga na sigurno mjesto." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Tvoj jednokratni kôd za oporavak može se koristiti za isključivanje prijave dvostruke autentifikacije u slučaju da izgubiš pristup svom davatelju usluge dvostruke autentifikacije. Bitwarden preporučuje da zapišeš kôd za oporavak i čuvaš ga na sigurnom mjestu." @@ -2218,6 +2224,9 @@ "disable": { "message": "Onemogući" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Opozovi pristup" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Pristup u nuždi odbijen" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Lozinka za $USER$ resetirana. Sada se možeš prijaviti novom lozinkom.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Ukloni osobni trezor" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ovaj korisnik" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Jedno ili više organizacijskih pravila zahtijeva da glavna lozinka ispunjava sljedeće uvjete:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Uspješno ponovno postalvjena lozinka!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 33bdef91d86..211e99d3234 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "A kétlépcsős bejelentkezés engedélyezése véglegesen kizárhatja a felhasználót a Bitwarden fiókból. A helyreállítási kód lehetővé teszi a fiókjához való hozzáférést abban az esetben, ha már nem tudjuk használni a szokásos kétlépcsős bejelentkezési szolgáltatást (pl. készülék elvesztése). A Bitwarden támogatás nem tud segíteni abban az esetben, ha elveszítjük a hozzáférést a fiókhoz. Célszerű leírni vagy kinyomtatni a helyreállítási kódot és azt biztonságos helyen tartani." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Kártyaelem típus eltávolítása" }, - "restrictedItemTypesPolicyDesc": { - "message": "Ne engedjük a felhasználóknak a kártyaelem típusok létrehozását." + "restrictedItemTypePolicyDesc": { + "message": "Ne engedjük meg a tagoknak, hogy kártyaelem típusokat hozzanak létre. A meglévő kártyák automatikusan eltávolításra kerülnek." + }, + "restrictCardTypeImport": { + "message": "A kártya elem típusokat nem lehet importálni." + }, + "restrictCardTypeImportDesc": { + "message": "Egy vagy több szervezet által beállított szabályzat megakadályozza a kártyák importálását a széfekbe." }, "yourSingleUseRecoveryCode": { "message": "Az egyszer használatos helyreállítási kóddal kikapcsolhatjuk a kétlépcsős bejelentkezést abban az esetben, ha elveszítjük a hozzáférést a kétlépcsős bejelentkezési szolgáltatóhoz. A Bitwarden azt javasolja, hogy írjuk le a helyreállítási kódot és tartsuk biztonságos helyen." @@ -2218,6 +2224,9 @@ "disable": { "message": "Letiltás" }, + "orgUserDetailsNotFound": { + "message": "A tag adatai nem találhatók." + }, "revokeAccess": { "message": "Hozzáférés visszavonása" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "A vészhelyzeti hozzáférés elutasításra került." }, + "grantorDetailsNotFound": { + "message": "Az adományozó adatai nem találhatók." + }, "passwordResetFor": { "message": "A jelszó alaphelyzetbe került $USER$ részére. Most az új jelszóval lehet bejelentkezni.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "A szervezet adat tulajdonjogának érvényesítése" + }, "personalOwnership": { "message": "Személyes tulajdon" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "A folyamat során $NAME$ kijelentkezése történik az aktuális munkamenetből, ezért vissza kell jelentkezni. Az aktív munkamenetek más eszközökön akár egy órán keresztül is aktívak maradhatnak.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ez a felhasználó" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Egy vagy több szervezeti rendszabályhoz mesterjelszó szükséges a következő követelmények megfeleléséhez:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Egy vagy több szervezeti rendszabályhoz mesterjelszó szükséges a következő követelmények megfeleléséhez:" + }, "resetPasswordSuccess": { "message": "A jelszó alaphelyzetbe állítása sikeres volt." }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "A jóváírás hozzáadásához szükséges számlázási cím.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 72bb773140d..bb0fbad9361 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Dengan mengaktifkan login dua-langkah, Anda bisa terkunci dari akun Bitwarden secara permanen. Jika Anda tidak bisa menggunakan provider login dua-langkah di kondisi normal (misal karena perangkat Anda hilang), Anda bisa menggunakan kode pemulihan untuk mengakses akun tersebut. Bitwarden sama sekali tidak dapat membantu jika Anda kehilangan akses ke akun Anda. Oleh karena itu, kami menyarankan Anda untuk menulis atau mencetak kode pemulihan dan menyimpannya di tempat yang aman." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Nonaktifkan" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Cabut Akses" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Akses darurat ditolak" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Setel ulang sandi untuk $USER$. Sekarang Anda dapat masuk menggunakan kata sandi baru.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Kepemilikan Pribadi" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "pengguna ini" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Satu atau lebih kebijakan organisasi memerlukan kata sandi utama Anda untuk memenuhi persyaratan berikut:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Ubah kata kunci berhasil!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index c5f665c89e6..30cd8bb6d53 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Impostare la verifica in due passaggi potrebbe bloccarti permanentemente fuori dal tuo account Bitwarden. Un codice di recupero ti permette di accedere al tuo account il caso non potessi più usare il tuo solito metodo di verifica in due passaggi (per esempio se perdi il telefono). L'assistenza clienti di Bitwarden non sarà in grado di aiutarti se perdi l'accesso al tuo account. Scrivi o stampa il tuo codice di recupero e conservalo in un luogo sicuro." }, - "restrictedItemTypesPolicy": { - "message": "Rimuovi tipo di carta" + "restrictedItemTypePolicy": { + "message": "Rimuovi l'elemento carta" }, - "restrictedItemTypesPolicyDesc": { - "message": "Non consentire ai membri di salvare le carte." + "restrictedItemTypePolicyDesc": { + "message": "Non consentire ai membri di creare elementi di tipo carta. Le carte esistenti saranno rimosse automaticamente." + }, + "restrictCardTypeImport": { + "message": "Impossibile importare elementi di tipo carta" + }, + "restrictCardTypeImportDesc": { + "message": "Non puoi importare carte nelle tue casseforti a causa di una politica impostata da una o più organizzazioni." }, "yourSingleUseRecoveryCode": { "message": "Puoi usare il codice di recupero monouso se non hai accesso a nessuno dei metodi impostati per l'accesso in due passaggi. Se accedi con un codice, l'accesso in due passaggi sarà disattivato. Conserva il codice in un luogo sicuro e accessibile solo a te." @@ -2218,6 +2224,9 @@ "disable": { "message": "Disattiva" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoca accesso" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Accesso di emergenza rifiutato" }, + "grantorDetailsNotFound": { + "message": "Dettagli del concedente non trovati" + }, "passwordResetFor": { "message": "Password ripristinata per $USER$. Ora puoi accedere usando la nuova password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Forza la proprietà dei dati dell'organizzazione" + }, "personalOwnership": { "message": "Rimuovi cassaforte individuale" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Se si procede, sarà chiusa la sessione corrente di $NAME$, cui sarà richiesto un nuovo accesso. Le sue sessioni attive su altri dispositivi potrebbero restare attive per un periodo massimo di un'ora.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "questo utente" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Una o più politiche dell'organizzazione richiedono che la tua password principale soddisfi questi requisiti:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Una o più politiche dell'organizzazione richiedono che la tua password principale soddisfi questi requisiti:" + }, "resetPasswordSuccess": { "message": "Password ripristinata!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Indirizzo di fatturazione richiesto per aggiungere credito.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 278110dc8cc..95e2cc71644 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "2段階認証を有効にすると Bitwarden アカウントから永久に閉め出されてしまうことがあります。リカバリーコードがあれば、通常の2段階認証プロバイダを使えなくなったとき (デバイスの紛失等) でもアカウントにアクセスできます。アカウントにアクセスできなくなっても Bitwarden はサポート出来ないため、リカバリーコードを書き出すか印刷し安全な場所で保管しておくことを推奨します。" }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "2段階認証プロバイダーへのアクセスを失った場合は、使い捨てのリカバリーコードを使用して2段階認証をオフにできます。 Bitwarden では、リカバリーコードを書き留めて安全な場所に保管することをお勧めしています。" @@ -2218,6 +2224,9 @@ "disable": { "message": "無効化" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "アクセスを取り消す" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "緊急アクセスが拒否されました" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$のパスワードをリセットしました。新しいパスワードでログインできます。", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "個別の保管庫を削除" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "このユーザー\n" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "組織ポリシーの要件を満たすためにマスターパスワードが必要です:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "パスワードをリセットしました" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 51ae044e856..4ebfca7db2c 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 3661600ce58..bdcdf5ed237 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 92f70868868..9abfaf0329b 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವುದರಿಂದ ನಿಮ್ಮ ಬಿಟ್‌ವಾರ್ಡನ್ ಖಾತೆಯಿಂದ ನಿಮ್ಮನ್ನು ಶಾಶ್ವತವಾಗಿ ಲಾಕ್ ಮಾಡಬಹುದು. ನಿಮ್ಮ ಸಾಮಾನ್ಯ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಪೂರೈಕೆದಾರರನ್ನು ನೀವು ಇನ್ನು ಮುಂದೆ ಬಳಸಲಾಗದಿದ್ದಲ್ಲಿ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಮರುಪಡೆಯುವಿಕೆ ಕೋಡ್ ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ (ಉದಾ. ನಿಮ್ಮ ಸಾಧನವನ್ನು ನೀವು ಕಳೆದುಕೊಳ್ಳುತ್ತೀರಿ). ನಿಮ್ಮ ಖಾತೆಗೆ ನೀವು ಪ್ರವೇಶವನ್ನು ಕಳೆದುಕೊಂಡರೆ ಬಿಟ್‌ವಾರ್ಡನ್ ಬೆಂಬಲವು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಮರುಪಡೆಯುವಿಕೆ ಕೋಡ್ ಅನ್ನು ಬರೆಯಲು ಅಥವಾ ಮುದ್ರಿಸಲು ಮತ್ತು ಅದನ್ನು ಸುರಕ್ಷಿತ ಸ್ಥಳದಲ್ಲಿ ಇರಿಸಲು ನಾವು ಶಿಫಾರಸು ಮಾಡುತ್ತೇವೆ." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "ನಿಷ್‌ಕ್ರಿಯೆಗೊಳಿಸಿ" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "ತುರ್ತು ಪ್ರವೇಶವನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ ಗೆ ಪಾಸ್‌ವರ್ಡ್ ಮರುಹೊಂದಿಸಿ. ನೀವು ಈಗ ಹೊಸ ಪಾಸ್‌ವರ್ಡ್ ಬಳಸಿ ಲಾಗಿನ್ ಮಾಡಬಹುದು.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "ವೈಯಕ್ತಿಕ ಮಾಲೀಕತ್ವ" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ಈ ಬಳಕೆದಾರ" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಸಂಸ್ಥೆಯ ನೀತಿಗಳಿಗೆ ಈ ಕೆಳಗಿನ ಅವಶ್ಯಕತೆಗಳನ್ನು ಪೂರೈಸಲು ಮಾಸ್ಟರ್ ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿದೆ:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "ಪಾಸ್ವರ್ಡ್ ಮರುಹೊಂದಿಸುವ ಯಶಸ್ಸು!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 76b99be0fe8..8ae6e906dea 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "2단계 로그인을 활성화하면 Bitwarden 계정을 영원히 잠글 수 있습니다. 복구 코드를 사용하면 정상적인 2단계 로그인 제공자를 더 이상 사용할 수 없는 경우(예. 장치를 잃어버렸을 때) 계정에 액세스할 수 있습니다. 계정에 접근하지 못한다면 Bitwarden 지원팀은 어떤 도움도 줄 수 없습니다. 복구 코드를 기록하거나 출력하여 안전한 장소에 보관할 것을 권장합니다." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "비활성화" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "긴급 접근 거절됨." }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ 사용자의 비밀번호가 초기화되었습니다. 이제 새로운 비밀번호로 로그인할 수 있습니다.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "개인 소유권" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "이 사용자" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "하나 이상의 단체 정책이 마스터 비밀번호가 다음 사항을 따르도록 요구합니다:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "비밀번호 재설정 성공!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 102057367fd..0c738d66de2 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Divpakāpju pieteikšanās var pastāvīgi liegt piekļuvi Bitwarden kontam. Atkopšanas kods ļauj piekļūt tam gadījumā, kad vairs nav iespējams izmantot ierasto divpakāpju pieteikšanās nodrošinātāju (piemēram, ir pazaudēta ierīce). Bitwarden atbalsts nevarēs palīdzēt, ja tiks pazaudēta piekļuve kontam. Ir ieteicams, ka atkopšanas kods tiek pierakstīts vai izdrukāts un turēts drošā vietā." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Noņemt karšu vienumu veidu" }, - "restrictedItemTypesPolicyDesc": { - "message": "Neļaut dalībniekiem izveidot karšu vienumu veidus." + "restrictedItemTypePolicyDesc": { + "message": "Neļaut dalībniekiem izveidot karšu vienumu veidus. Esošās kartes tiks automātiski noņemtas." + }, + "restrictCardTypeImport": { + "message": "Nevar ievietot karšu vienumu veidus" + }, + "restrictCardTypeImportDesc": { + "message": "Pamatnostādne, ko ir iestatījusi viena vai vairākas apvienības, liedz karšu ievietošanu savās glabātavās." }, "yourSingleUseRecoveryCode": { "message": "Vienreizējas izmantošanas atkopes kodu var izmantot, lai izslēgtu divpakāpju pieteikšanos gadījumā, ja tiek zaudēta piekļuve savam divpakāpju pieteikšanās nodrošinātājam. Bitwarden iesaka pierakstīt atkopes kodu un glabāt to drošā vietā." @@ -2218,6 +2224,9 @@ "disable": { "message": "Atspējot" }, + "orgUserDetailsNotFound": { + "message": "Informācija par dalībnieku netika atrasta." + }, "revokeAccess": { "message": "Atsaukt piekļuvi" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Ārkārtas piekļuve noraidīta" }, + "grantorDetailsNotFound": { + "message": "Informācijas par piešķīrēju netika atrasta" + }, "passwordResetFor": { "message": "Parole atiestatīta lietotājam $USER$. Tagad var pieteikties ar jauno paroli.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Uzspiest apvienības datu īpašumtiesības" + }, "personalOwnership": { "message": "Personīgās īpašumtiesības" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Turpināšana izrakstīs $NAME$ no pašreizējās sesijas, un pēc tam būs nepieciešams vēlreiz pieteikties. Citās ierīcēs darbojošās sesijas var būt spēkā līdz vienai stundai.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "šo lietotāju" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Vienā vai vairākos apvienības nosacījumos ir norādīts, ka galvenajai parolei ir jāatbilst šādām prasībām:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Vienā vai vairākās apvienības pamatnostādnēs ir norādīts, ka galvenajai parolei ir jāatbilst šādām prasībām:" + }, "resetPasswordSuccess": { "message": "Peroles atiestatīšana bija veiksmīga" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Norēķinu adrese ir nepieciešama, lai pievienot kredītu.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 3442762319d..2b93514d5ad 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (ex. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "പ്രവര്‍ത്തന രഹിതമാക്കുക" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "വ്യക്തിഗത ഉടമസ്ഥാവകാശം" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 4483b9ebb61..338c0228fcf 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 3661600ce58..bdcdf5ed237 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 3e704ca47dc..7221018aca0 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Å skru på 2-trinnsinnlogging kan låse deg permanent ut av din Bitwarden-konto. En gjenopprettingskode gir deg tilgang til kontoen din i det tilfellet at du ikke lenger kan bruke din vanlige 2-trinnsinnloggingsleverandør (f.eks. at du mister enheten din). Bitwarden-kundestøtten vil ikke kunne hjelpe deg dersom du mister tilgang til kontoen din. Vi anbefaler at du skriver ned eller skriver ut gjenopprettingskoden og legger den på en trygg plass." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Deaktiver" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Opphev tilgang" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Nødtilgang avvist" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Passord tilbakestille for $USER$. Du kan nå logge inn ved å bruke det nye passordet.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Personlig eierskap" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "denne brukeren" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "En eller flere av organisasjonens vilkår krever hovedpassordet ditt for å oppfylle følgende krav:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Tilbakestilling av passord vellykket!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 7a4c19a0362..4b401cd67a8 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 0e70d503dc5..556ef03ada0 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Door aanmelden in twee stappen in te schakelen, kun je jezelf definitief buitensluiten van je Bitwarden-account. Een herstelcode geeft je toegang tot je account in het geval dat je je normale tweestapsaanmelding niet meer kunt gebruiken (bijv. als je je apparaat verliest). De Bitwarden-klantondersteuning kan je niet helpen als je de toegang tot je account verliest. We raden je met klem aan de herstelcode op te schrijven of af te drukken en op een veilige plaats te bewaren." }, - "restrictedItemTypesPolicy": { - "message": "Verwijder kaart item type" + "restrictedItemTypePolicy": { + "message": "Kaart item type verwijderen" }, - "restrictedItemTypesPolicyDesc": { - "message": "Aanmaken van kaart item types niet toestaan voor leden." + "restrictedItemTypePolicyDesc": { + "message": "Leden niet toestaan om kaartitemtypes te maken. Bestaande kaarten wordek automatisch verwijderd." + }, + "restrictCardTypeImport": { + "message": "Kan kaart item types niet importeren" + }, + "restrictCardTypeImportDesc": { + "message": "Een beleid ingesteld door 1 of meer organisaties voorkomt dat je kaarten naar je kluizen kunt importeren." }, "yourSingleUseRecoveryCode": { "message": "Met je herstelcode voor eenmalig gebruik kun je tweestapsaanmelding uitschakelen in het geval dat je toegang verliest tot je tweestapsaanmeldingsprovider. Bitwarden adviseert de herstelcode op te schrijven en op een veilige plaats te bewaren." @@ -2218,6 +2224,9 @@ "disable": { "message": "Uitschakelen" }, + "orgUserDetailsNotFound": { + "message": "Details van lid niet gevonden." + }, "revokeAccess": { "message": "Toegang intrekken" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Noodtoegang afgewezen" }, + "grantorDetailsNotFound": { + "message": "Details van concessiegever niet gevonden" + }, "passwordResetFor": { "message": "Wachtwoord opnieuw ingesteld voor $USER$. Je kunt nu inloggen met het nieuwe wachtwoord.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Gegevenseigendom van organisatie afdwingen" + }, "personalOwnership": { "message": "Persoonlijk eigendom" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Doorgaan logt de huidige sessie van $NAME$ uit, waarna deze opnieuw moet aanmelden. Actieve sessies op andere apparaten kunnen mogelijk nog één uur actief blijven.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "deze gebruiker" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Een of meer organisatiebeleidseisen stelt de volgende eisen aan je hoofdwachtwoord:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Een of meer organisatiebeleidseisen stelt de volgende eisen aan je hoofdwachtwoord:" + }, "resetPasswordSuccess": { "message": "Wachtwoord opnieuw ingesteld!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Factuuradres vereist voor het toevoegen van krediet.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index dee5e91aa9c..0707b948a38 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Slå av" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 3661600ce58..bdcdf5ed237 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 7199de0f2bb..b70804f2b6f 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Włączenie logowania dwustopniowego można trwale zablokować konto Bitwarden. Kod odzyskiwania pozwala na dostęp do konta w przypadku, gdy nie będziesz mógł skorzystać ze standardowego dostawcy logowania dwustopniowego (np. w przypadku utraty urządzenia). Pomoc techniczna Bitwarden nie będzie w stanie Ci pomóc, jeśli stracisz dostęp do swojego konta. Zalecamy zapisanie lub wydrukowanie kodu odzyskiwania i przechowywanie go w bezpiecznym miejscu." }, - "restrictedItemTypesPolicy": { - "message": "Usuń typ elementu karty" + "restrictedItemTypePolicy": { + "message": "Usuń elementy typu karty" }, - "restrictedItemTypesPolicyDesc": { - "message": "Nie zezwalaj członkom na tworzenie typów elementów karty." + "restrictedItemTypePolicyDesc": { + "message": "Nie zezwalaj członkom na tworzenie elementów typu karty. Istniejące karty zostaną automatycznie usunięte." + }, + "restrictCardTypeImport": { + "message": "Nie można importować elementów typu karty" + }, + "restrictCardTypeImportDesc": { + "message": "Polityka ustawiona przez 1 lub więcej organizacji uniemożliwia importowanie kart do sejfów." }, "yourSingleUseRecoveryCode": { "message": "Jednorazowy kod odzyskiwania może być użyty do wyłączenia dwuetapowego logowania w przypadku utraty dostępu do dostawcy logowania dwuetapowego. Bitwarden zaleca zapisanie kodu odzyskiwania i przechowywanie go w bezpiecznym miejscu." @@ -2218,6 +2224,9 @@ "disable": { "message": "Wyłącz" }, + "orgUserDetailsNotFound": { + "message": "Nie znaleziono szczegółów użytkownika." + }, "revokeAccess": { "message": "Unieważnij dostęp" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Dostęp awaryjny został odrzucony" }, + "grantorDetailsNotFound": { + "message": "Nie znaleziono szczegółów dotacji" + }, "passwordResetFor": { "message": "Zresetowałeś hasło użytkownika $USER$. Możesz zalogować się za pomocą nowego hasła.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Wymuś własność danych organizacji" + }, "personalOwnership": { "message": "Własność osobista" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Kontynuowanie spowoduje wylogowanie użytkownika $NAME$ z obecnej sesji i będzie musiał zalogować się ponownie. Aktywne sesje na innych urządzeniach mogą pozostać aktywne przez maksymalnie godzinę.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "ten użytkownik" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Co najmniej jedna zasada organizacji wymaga, aby hasło główne spełniało następujące wymagania:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Co najmniej jedna zasada organizacji wymaga, aby hasło główne spełniało następujące wymagania:" + }, "resetPasswordSuccess": { "message": "Hasło zostało zresetowane!" }, @@ -7615,7 +7642,7 @@ "description": "Notifies that a service account has been updated" }, "typeOrSelectProjects": { - "message": "Type or select projects", + "message": "Wpisz lub wybierz projekty", "description": "Instructions for selecting projects for a service account" }, "newSaTypeToFilter": { @@ -7702,7 +7729,7 @@ "description": "Title for the section displaying access tokens." }, "createAccessToken": { - "message": "Create access token", + "message": "Utwórz token dostępu", "description": "Button label for creating a new access token." }, "expires": { @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Aby dodać środki, wymagany jest adres rozliczeniowy.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index cfd998f2bfd..dbd11b62095 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "A configuração de login em duas etapas pode bloqueá-lo permanentemente da sua conta no Bitwarden. Um código de recuperação permite que você acesse sua conta no caso de não poder mais usar seu provedor de login em duas etapas normalmente (exemplo: você perde seu dispositivo). O suporte do Bitwarden não será capaz de ajudá-lo se você perder o acesso à sua conta. Recomendamos que você anote ou imprima o código de recuperação e o mantenha em um lugar seguro." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Seu código de recuperação de uso único pode ser usado para desativar o login em duas etapas no caso de você perder acesso ao seu provedor de login em duas etapas. O Bitwarden recomenda que você anote o código de recuperação e o mantenha em um lugar seguro." @@ -2218,6 +2224,9 @@ "disable": { "message": "Desabilitar" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revogar acesso" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Acesso de emergência rejeitado" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Redefinição de senha para $USER$. Agora você pode acessar usando a nova senha.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Propriedade Pessoal" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "este usuário" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Uma ou mais políticas da organização exigem que a senha mestra cumpra aos seguintes requisitos:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Senha redefinida com sucesso!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 4b9343a62ca..33ac3148a1c 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "A configuração da verificação de dois passos pode bloquear permanentemente a sua conta Bitwarden. Um código de recuperação permite-lhe aceder à sua conta no caso de já não poder utilizar o seu fornecedor normal de verificação de dois passos (por exemplo, se perder o seu dispositivo). O suporte Bitwarden não poderá ajudá-lo se perder o acesso à sua conta. Recomendamos que anote ou imprima o código de recuperação e o guarde num local seguro." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remover o tipo de item do cartão" }, - "restrictedItemTypesPolicyDesc": { - "message": "Não permitir que os membros criem tipos de itens de cartão." + "restrictedItemTypePolicyDesc": { + "message": "Não permitir que os membros criem tipos de itens de cartão. Os cartões existentes serão automaticamente removidos." + }, + "restrictCardTypeImport": { + "message": "Não é possível importar tipos de itens de cartão" + }, + "restrictCardTypeImportDesc": { + "message": "Uma política definida por 1 ou mais organizações impede-o de importar cartões para os seus cofres." }, "yourSingleUseRecoveryCode": { "message": "O seu código de recuperação de utilização única pode ser utilizado para desativar a verificação de dois passos no caso de perder o acesso ao seu fornecedor de verificação de dois passos. O Bitwarden recomenda que anote o código de recuperação e o guarde num local seguro." @@ -2218,6 +2224,9 @@ "disable": { "message": "Desativar" }, + "orgUserDetailsNotFound": { + "message": "Detalhes do membro não encontrados." + }, "revokeAccess": { "message": "Revogar o acesso" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Acesso de emergência rejeitado" }, + "grantorDetailsNotFound": { + "message": "Detalhes do concedente não encontrados" + }, "passwordResetFor": { "message": "Palavra-passe de $USER$ redefinida. Pode agora iniciar sessão utilizando a nova palavra-passe.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Reforçar a propriedade dos dados da organização" + }, "personalOwnership": { "message": "Remover cofre pessoal" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Ao prosseguir, terminará a sessão atual de $NAME$ e terá de iniciar sessão novamente. As sessões ativas noutros dispositivos poderão continuar ativas até uma hora.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "este utilizador" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Uma ou mais políticas da organização exigem que a palavra-passe mestra cumpra os seguintes requisitos:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Uma ou mais políticas da organização exigem que a palavra-passe mestra cumpra os seguintes requisitos:" + }, "resetPasswordSuccess": { "message": "Palavra-passe redefinida com sucesso!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Endereço de faturação necessário para adicionar crédito.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index afec83c9395..add0453a747 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Configurarea unei autentificări în două etape vă poate bloca permanent din contul Bitwarden. Un cod de recuperare vă permite să vă accesați contul în cazul în care nu mai puteți utiliza furnizorul normal de autentificare în două etape (exemplu: vă pierdeți dispozitivul). Serviciul de asistență Bitwarden nu vă va putea ajuta dacă pierdeți accesul la cont. Vă recomandăm să notați sau să imprimați codul de recuperare și să-l păstrați într-un loc sigur." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Dezactivare" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revocare acces" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Acces de urgență respins" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "S-a resetat parola pentru $USER$. Vă puteți conecta acum cu noua parolă.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Înlăturați seiful personal" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "acest utilizator" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Una sau mai multe politici ale organizației, necesită ca parola principală să îndeplinească următoarele cerințe:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Parolă resetată cu succes!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 95c100551c3..146c9659ce0 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "При включении двухэтапной аутентификации вы можете навсегда потерять доступ к вашей учетной записи Bitwarden. Код восстановления позволяет получить доступ к вашему аккаунту в случае, если вы больше не можете использовать свой обычный метод двухэтапной аутентификации (например, при потере устройства). Служба поддержки Bitwarden не сможет вам помочь, если вы потеряете доступ к своему аккаунту. Мы рекомендуем вам записать или распечатать код восстановления и хранить его в надежном месте." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Удалить элемент типа карта" }, - "restrictedItemTypesPolicyDesc": { - "message": "Не разрешать пользователям создавать элемент типа карта." + "restrictedItemTypePolicyDesc": { + "message": "Не разрешать пользователям создавать элементы карт. Существующие карты будут автоматически удалены." + }, + "restrictCardTypeImport": { + "message": "Невозможно импортировать элементы карт" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, установленная 1 или более организациями, не позволяет импортировать карты в ваши хранилища." }, "yourSingleUseRecoveryCode": { "message": "Одноразовый код восстановления можно использовать для отключения двухэтапной аутентификации в случае потери доступа к провайдеру двухэтапной аутентификации. Bitwarden рекомендует записать код восстановления и хранить его в надежном месте." @@ -2218,6 +2224,9 @@ "disable": { "message": "Отключить" }, + "orgUserDetailsNotFound": { + "message": "Данные участника не найдены." + }, "revokeAccess": { "message": "Отозвать доступ" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "В экстренном доступе отказано" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Сброшен пароль для $USER$. Теперь вы можете войти, используя новый пароль.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Удалить личное хранилище" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "В случае продолжения сессия $NAME$ будет завершена, что потребует повторной авторизации. Сессии на других устройствах могут оставаться активными до одного часа.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "этот пользователь" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Согласно одной или нескольким политикам организации необходимо, чтобы мастер-пароль отвечал следующим требованиям:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Согласно одной или нескольким политикам организации необходимо, чтобы мастер-пароль отвечал следующим требованиям:" + }, "resetPasswordSuccess": { "message": "Пароль успешно сброшен!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Для пополнения счета необходим платежный адрес.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 09705e19ecd..412960c2453 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index bf2acb5838b..752f088a92c 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Zapnutie dvojstupňového prihlásenia vás môže natrvalo vymknúť z vášho Bitwarden účtu. Záchranný kód umožňuje prístup k vášmu kontu v prípade že už nemôžete použiť svoj normálny dvojstupňový spôsob overenia. (napríklad ak stratíte zariadenie) Zákaznícka podpora nebude schopná pomôcť vám ak stratíte prístup k účtu. Preto vám odporúčame zapísať si, alebo si vytlačiť záchranný kód a uložiť ho na bezpečnom mieste." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Odstrániť typ položky pre kartu" }, - "restrictedItemTypesPolicyDesc": { - "message": "Nedovoliť členom vytvárať typy položiek pre karty." + "restrictedItemTypePolicyDesc": { + "message": "Neumožniť členom vytvárať typ položky pre kartu. Existujúce karty budú automaticky odstránené." + }, + "restrictCardTypeImport": { + "message": "Položky typu karta sa nedajú importovať" + }, + "restrictCardTypeImportDesc": { + "message": "Politika nastavená 1 alebo viacerými organizáciami vám bráni v importovaní kariet do vašich trezorov." }, "yourSingleUseRecoveryCode": { "message": "Váš jednorázový záchranný kód sa dá použiť na vypnutie dvojstupňového prihlasovania ak ste stratili pristúp k jeho poskytovateľovi. Bitwarden odporúča, aby ste si záchranný kód zapísali a odložili na bezpečné miesto." @@ -2218,6 +2224,9 @@ "disable": { "message": "Vypnúť" }, + "orgUserDetailsNotFound": { + "message": "Detaily o členovi nenájdené." + }, "revokeAccess": { "message": "Zrušiť prístup" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Núdzový prístup odmietnutý" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Resetovanie hesla pre $USER$. Teraz sa môžete prihlásiť s novým heslom.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Požadovanie vlastníctva údajov organizácie" + }, "personalOwnership": { "message": "Zakázať osobný trezor" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Pokračovaním sa $NAME$ odhlási z aktuálnej relácie a bude sa musieť znova prihlásiť. Aktívne relácie na iných zariadeniach môžu zostať aktívne až jednu hodinu.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "tento používateľ" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Jedno alebo viac pravidiel organizácie požadujú, aby hlavné heslo spĺňalo nasledujúce požiadavky:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "Jedno alebo viac pravidiel organizácie požadujú, aby hlavné heslo spĺňalo nasledujúce požiadavky:" + }, "resetPasswordSuccess": { "message": "Heslo bolo úspešne obnovené!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Na pridanie kreditu je potrebná fakturačná adresa.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 66930baf451..dca8c4d1161 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Onemogočeno" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Odvzemi dostop" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 380122b4f71..71af7ca2062 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index 6e805557c65..0f84a52f849 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Омогућавање пријаве у два корака може вас трајно закључати са вашег Bitwarden-а налога. Код за опоравак омогућава вам приступ вашем налогу у случају да више не можете да користите свог уобичајеног добављача услуге пријављивања у два корака (нпр. ако изгубите уређај). Подршка Bitwarden-а неће вам моћи помоћи ако изгубите приступ свом налогу. Препоручујемо да запишете или одштампате код за опоравак и сачувате га на сигурном месту." }, - "restrictedItemTypesPolicy": { - "message": "Уклоните тип ставке картице" + "restrictedItemTypePolicy": { + "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Не дозволите члановима да креирају тип ставке картице." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Не могу увозити врсте картица" + }, + "restrictCardTypeImportDesc": { + "message": "Политика која је поставила 1 или више организација спречава вас да се увозе картице у сефу." }, "yourSingleUseRecoveryCode": { "message": "Ваш јединствени кôд за опоравак може се користити за искључивање у два корака у случају да изгубите приступ свом двоструком провајдеру пријаве. Bitwarden препоручује да запишете кôд за опоравак и држите га на сигурном месту." @@ -2218,6 +2224,9 @@ "disable": { "message": "Онемогући" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Опозови Приступ" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Одбијен хитни приступ" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Ресетовање лозинке за $USER$. Сада се можете пријавити помоћу нове лозинке.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Лично власништво" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "овај корисник" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Једна или више смерница организације захтевају главну лозинку да би испуњавали следеће захтеве:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Успешно ресетовање лозинке!" }, @@ -7615,7 +7642,7 @@ "description": "Notifies that a service account has been updated" }, "typeOrSelectProjects": { - "message": "Type or select projects", + "message": "Унети или одабрати пројекте", "description": "Instructions for selecting projects for a service account" }, "newSaTypeToFilter": { @@ -7702,7 +7729,7 @@ "description": "Title for the section displaying access tokens." }, "createAccessToken": { - "message": "Create access token", + "message": "Креирати приступни токен", "description": "Button label for creating a new access token." }, "expires": { @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 1a2c3f0ec5a..a81335284a1 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Att aktivera tvåstegsverifiering kan låsa ute dig från ditt Bitwarden-konto permanent. En återställningskod låter dig komma åt ditt konto om du inte längre kan använda din vanliga metod för tvåstegsverifiering (t.ex. om du förlorar din enhet). Bitwardens kundservice kommer inte att kunna hjälpa dig om du förlorar åtkomst till ditt konto. Vi rekommenderar att du skriver ner eller skriver ut återställningskoden och förvarar den på ett säkert ställe." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Stäng av" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Återkalla åtkomst" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Nödåtkomst nekad" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Lösenordet för $USER$ återställdes. Du kan nu logga in med det nya lösenordet.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Radera individuellt valv" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "denna användare" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "En eller flera organisationspolicyer kräver att huvudlösenordet uppfyller följande krav:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Lösenordsåterställningen lyckades!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 3661600ce58..bdcdf5ed237 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 5913116b462..48cedb1c4d5 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Turn off" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Revoke access" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index b7f8212c05e..af3d51faa3e 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "İki aşamalı girişi etkinleştirmek, Bitwarden hesabınızı kalıcı olarak kilitleyebilir. Kurtarma kodunuz, iki aşamalı giriş sağlayıcınızı kullanamamanız durumunda hesabınıza erişmenize olanak sağlar (ör. cihazınızı kaybedersiniz). Hesabınıza erişiminizi kaybederseniz Bitwarden destek ekibi size yardımcı olamaz. Kurtarma kodunu not almanızı veya yazdırmanızı ve güvenli bir yerde saklamanızı öneririz." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Kart kaydı türünü kaldır" }, - "restrictedItemTypesPolicyDesc": { - "message": "Üyelerin kart kaydı türü oluşturmasına izin verme." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Devre dışı bırak" }, + "orgUserDetailsNotFound": { + "message": "Üye bilgileri bulunamadı." + }, "revokeAccess": { "message": "Erişimi iptal et" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Acil durum erişimi reddedildi" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ için parola sıfırlandı. Artık yeni parola ile giriş yapabilirsiniz.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Kişisel kasayı kaldır" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "bu kullanıcı" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Bir veya daha fazla kuruluş ilkesi gereğince ana parola aşağıdaki gereksinimleri karşılamalıdır:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Parola başarıyla sıfırlandı." }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index b95db61651c..c67b8d62ceb 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Увімкнення двоетапної перевірки може цілком заблокувати доступ до облікового запису Bitwarden. Код відновлення дає вам змогу отримати доступ до свого облікового запису у випадку, якщо ви не можете скористатися провайдером двоетапної перевірки (наприклад, якщо втрачено пристрій). Служба підтримки Bitwarden не зможе допомогти відновити доступ до вашого облікового запису. Ми радимо вам записати чи надрукувати цей код відновлення і зберігати його в надійному місці." }, - "restrictedItemTypesPolicy": { - "message": "Вилучити тип запису \"Картка\"" + "restrictedItemTypePolicy": { + "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Не дозволяти учасникам створювати записи типу \"Картка\"." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Одноразовий код відновлення можна використати для вимкнення двоетапної перевірки у випадку, якщо ви втратите доступ до вашого провайдера двоетапної перевірки. Bitwarden рекомендує вам записати код відновлення і зберігати його в надійному місці." @@ -2218,6 +2224,9 @@ "disable": { "message": "Вимкнути" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Відкликати доступ" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Екстрений доступ відхилено" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Пароль для користувача $USER$ скинуто. Тепер ви можете увійти використовуючи новий пароль.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Вилучити особисте сховище" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "цей користувач" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "Одна або декілька політик організації вимагають дотримання таких вимог для головного пароля:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Пароль успішно скинуто!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 03d8238f9bd..6b9cb8d0ac0 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "Vô hiệu hoá" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "Thu hồi quyền truy cập" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "Emergency access rejected" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "Password reset for $USER$. You can now login using the new password.", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "Remove individual vault" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "this user" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "One or more organization policies require the master password to meet the following requirements:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "Password reset success!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 7cde4854120..55579121b4b 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "启用两步登录可能会将您永久锁定在 Bitwarden 账户之外。当您无法使用常规的两步登录提供程序(例如您丢失了设备)时,可以使用恢复代码访问您的账户。如果您失去对您账户的访问,Bitwarden 支持也无法帮助您。我们建议您写下或打印恢复代码,并将其妥善保管。" }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "禁用支付卡项目类型" }, - "restrictedItemTypesPolicyDesc": { - "message": "不允许成员创建支付卡项目类型。" + "restrictedItemTypePolicyDesc": { + "message": "不允许成员创建支付卡项目类型。现有支付卡将自动被移除。" + }, + "restrictCardTypeImport": { + "message": "无法导入支付卡项目类型" + }, + "restrictCardTypeImportDesc": { + "message": "由 1 个或多个组织设置的策略阻止您将支付卡导入密码库。" }, "yourSingleUseRecoveryCode": { "message": "当您无法访问两步登录提供程序时,您的一次性恢复代码可用于停用两步登录。Bitwarden 建议您写下恢复代码,并将其妥善保管。" @@ -2189,7 +2195,7 @@ "message": "需要高级会员" }, "premiumRequiredDesc": { - "message": "此功能需要高级会员资格。" + "message": "使用此功能需要高级会员资格。" }, "youHavePremiumAccess": { "message": "您拥有高级访问权限" @@ -2218,6 +2224,9 @@ "disable": { "message": "停用" }, + "orgUserDetailsNotFound": { + "message": "未找到成员详细信息。" + }, "revokeAccess": { "message": "撤销访问权限" }, @@ -2608,7 +2617,7 @@ "message": "检查泄漏情况" }, "breachUsernameNotFound": { - "message": "在任何已知数据泄漏中找不到 $USERNAME$。", + "message": "没有在已知的数据泄露中发现 $USERNAME$。", "placeholders": { "username": { "content": "$1", @@ -4345,7 +4354,7 @@ "description": "Upper limit of seats to allow through autoscaling" }, "maxSeatCost": { - "message": "最大潜在席位费用" + "message": "最大潜在的席位费用" }, "addSeats": { "message": "添加席位", @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "紧急访问已拒绝" }, + "grantorDetailsNotFound": { + "message": "未找到授予人详细信息" + }, "passwordResetFor": { "message": "$USER$ 的密码已重置。您现在可以使用新密码登录了。", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "强制组织数据所有权" + }, "personalOwnership": { "message": "禁用个人密码库" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "继续操作会将 $NAME$ 登出当前会话,要求他们重新登录。在其他设备上的活动会话可能继续活动长达一个小时。", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "此用户" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "一个或多个组织策略要求主密码满足以下要求:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "一个或多个组织策略要求主密码满足以下要求:" + }, "resetPasswordSuccess": { "message": "密码重置成功!" }, @@ -6681,7 +6708,7 @@ "message": "上面的 1 个字段需要您注意。" }, "fieldRequiredError": { - "message": "$FIELDNAME$ 必填。", + "message": "必须填写 $FIELDNAME$。", "placeholders": { "fieldname": { "content": "$1", @@ -8436,7 +8463,7 @@ } }, "notFound": { - "message": "$RESOURCE$ 未找到", + "message": "未找到 $RESOURCE$", "placeholders": { "resource": { "content": "$1", @@ -8734,7 +8761,7 @@ "message": "服务账户限制(可选)" }, "maxServiceAccountCost": { - "message": "最大潜在服务账户费用" + "message": "最大潜在的服务账户费用" }, "loggedInExclamation": { "message": "已登录!" @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "添加信用额度需要计费地址。", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index f5bb444aaf1..a44d7eb5f28 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -2153,11 +2153,17 @@ "twoStepLoginRecoveryWarning": { "message": "啟用兩步驟登入可能會將您永久鎖定在您的 Bitwarden 帳戶外。如果您無法正常使用兩步驟登入方式(例如,您遺失了裝置),則可以使用復原碼存取您的帳戶。 如果您失去帳戶的存取權限,Bitwarden 也無法幫助您。所以我們建議您記下或列印復原碼,並將其妥善保存。" }, - "restrictedItemTypesPolicy": { + "restrictedItemTypePolicy": { "message": "Remove card item type" }, - "restrictedItemTypesPolicyDesc": { - "message": "Do not allow members to create card item types." + "restrictedItemTypePolicyDesc": { + "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." @@ -2218,6 +2224,9 @@ "disable": { "message": "停用" }, + "orgUserDetailsNotFound": { + "message": "Member details not found." + }, "revokeAccess": { "message": "撤銷存取權限" }, @@ -5363,6 +5372,9 @@ "emergencyRejected": { "message": "已拒絕緊急存取" }, + "grantorDetailsNotFound": { + "message": "Grantor details not found" + }, "passwordResetFor": { "message": "$USER$ 的密碼已重設。您現在可以使用新密碼登入了。", "placeholders": { @@ -5372,6 +5384,9 @@ } } }, + "organizationDataOwnership": { + "message": "Enforce organization data ownership" + }, "personalOwnership": { "message": "停用個人密碼庫" }, @@ -5763,12 +5778,24 @@ } } }, + "emergencyAccessLoggedOutWarning": { + "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "placeholders": { + "name": { + "content": "$1", + "example": "John Smith" + } + } + }, "thisUser": { "message": "此使用者" }, "resetPasswordMasterPasswordPolicyInEffect": { "message": "一個或多個組織原則要求主密碼須符合下列條件:" }, + "changePasswordDelegationMasterPasswordPolicyInEffect": { + "message": "One or more organization policies require the master password to meet the following requirements:" + }, "resetPasswordSuccess": { "message": "密碼重設成功!" }, @@ -10649,5 +10676,9 @@ "example": "12/31/2024" } } + }, + "billingAddressRequiredToAddCredit": { + "message": "Billing address required to add credit.", + "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } From 06867943866065e6a795537835b41a04aec6d4cb Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 10:52:56 +0200 Subject: [PATCH 25/57] Autosync the updated translations (#15363) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 6 + apps/desktop/src/locales/ar/messages.json | 6 + apps/desktop/src/locales/az/messages.json | 6 + apps/desktop/src/locales/be/messages.json | 6 + apps/desktop/src/locales/bg/messages.json | 6 + apps/desktop/src/locales/bn/messages.json | 6 + apps/desktop/src/locales/bs/messages.json | 6 + apps/desktop/src/locales/ca/messages.json | 6 + apps/desktop/src/locales/cs/messages.json | 6 + apps/desktop/src/locales/cy/messages.json | 6 + apps/desktop/src/locales/da/messages.json | 6 + apps/desktop/src/locales/de/messages.json | 44 ++-- apps/desktop/src/locales/el/messages.json | 26 ++- apps/desktop/src/locales/en_GB/messages.json | 6 + apps/desktop/src/locales/en_IN/messages.json | 6 + apps/desktop/src/locales/eo/messages.json | 6 + apps/desktop/src/locales/es/messages.json | 210 ++++++++++--------- apps/desktop/src/locales/et/messages.json | 6 + apps/desktop/src/locales/eu/messages.json | 6 + apps/desktop/src/locales/fa/messages.json | 6 + apps/desktop/src/locales/fi/messages.json | 6 + apps/desktop/src/locales/fil/messages.json | 6 + apps/desktop/src/locales/fr/messages.json | 6 + apps/desktop/src/locales/gl/messages.json | 6 + apps/desktop/src/locales/he/messages.json | 6 + apps/desktop/src/locales/hi/messages.json | 6 + apps/desktop/src/locales/hr/messages.json | 6 + apps/desktop/src/locales/hu/messages.json | 6 + apps/desktop/src/locales/id/messages.json | 6 + apps/desktop/src/locales/it/messages.json | 6 + apps/desktop/src/locales/ja/messages.json | 6 + apps/desktop/src/locales/ka/messages.json | 6 + apps/desktop/src/locales/km/messages.json | 6 + apps/desktop/src/locales/kn/messages.json | 6 + apps/desktop/src/locales/ko/messages.json | 6 + apps/desktop/src/locales/lt/messages.json | 6 + apps/desktop/src/locales/lv/messages.json | 6 + apps/desktop/src/locales/me/messages.json | 6 + apps/desktop/src/locales/ml/messages.json | 6 + apps/desktop/src/locales/mr/messages.json | 6 + apps/desktop/src/locales/my/messages.json | 6 + apps/desktop/src/locales/nb/messages.json | 6 + apps/desktop/src/locales/ne/messages.json | 6 + apps/desktop/src/locales/nl/messages.json | 6 + apps/desktop/src/locales/nn/messages.json | 6 + apps/desktop/src/locales/or/messages.json | 6 + apps/desktop/src/locales/pl/messages.json | 6 + apps/desktop/src/locales/pt_BR/messages.json | 6 + apps/desktop/src/locales/pt_PT/messages.json | 6 + apps/desktop/src/locales/ro/messages.json | 6 + apps/desktop/src/locales/ru/messages.json | 6 + apps/desktop/src/locales/si/messages.json | 6 + apps/desktop/src/locales/sk/messages.json | 6 + apps/desktop/src/locales/sl/messages.json | 6 + apps/desktop/src/locales/sr/messages.json | 44 ++-- apps/desktop/src/locales/sv/messages.json | 6 + apps/desktop/src/locales/te/messages.json | 6 + apps/desktop/src/locales/th/messages.json | 6 + apps/desktop/src/locales/tr/messages.json | 6 + apps/desktop/src/locales/uk/messages.json | 6 + apps/desktop/src/locales/vi/messages.json | 6 + apps/desktop/src/locales/zh_CN/messages.json | 8 +- apps/desktop/src/locales/zh_TW/messages.json | 6 + 63 files changed, 529 insertions(+), 151 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 36200826832..83842972ece 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 848eb9f21db..546257941e5 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "الحساب مقيد" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"كلمة مرور الملف\" و \"تأكيد كلمة مرور الملف\" غير متطابقين." }, diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index daee85808cf..29022fc9789 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Hesab məhdudlaşdırıldı" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Fayl parolu\" və \"Fayl parolunu təsdiqlə\" uyuşmur." }, diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index befc91faed3..f2eadaa919b 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 926eba32f8f..b8e25d14b8b 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Регистрацията е ограничена" }, + "restrictCardTypeImport": { + "message": "Картовите елементи не могат да бъдат внесени" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, зададена от 1 или повече организации, не позволява да внасяте карти в трезорите си." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Дънните в полетата „Парола на файла“ и „Потвърждаване на паролата на файла“ не съвпадат." }, diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index a9c16109acf..dc413c24bda 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index b60e588a573..8daad2996e7 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 43e58d31781..2ce95c78d47 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Compte restringit" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Contrasenya del fitxer\" i \"Confirma contrasenya del fitxer\" no coincideixen." }, diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 4658b515d13..f48461c05c8 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Účet je omezený" }, + "restrictCardTypeImport": { + "message": "Nelze importovat typy položek karty" + }, + "restrictCardTypeImportDesc": { + "message": "Zásady nastavené 1 nebo více organizací Vám brání v importu karet do Vašeho trezoru." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Heslo souboru\" a \"Potvrzení hesla souboru\" se neshodují." }, diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 875892713c1..42dbc5e42cb 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 81a6afc783a..995588202fa 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Konto begrænset" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“Filadgangskode” og “Bekræft filadgangskode“ matcher ikke." }, diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 70914d4dc2c..9254ca17f41 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -24,7 +24,7 @@ "message": "Identität" }, "typeNote": { - "message": "Note" + "message": "Notiz" }, "typeSecureNote": { "message": "Sichere Notiz" @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Konto eingeschränkt" }, + "restrictCardTypeImport": { + "message": "Karten-Eintragstypen können nicht importiert werden" + }, + "restrictCardTypeImportDesc": { + "message": "Eine von einer oder mehreren Organisationen festgelegte Richtlinie verhindert, dass du Karten in deinen Tresor importieren kannst." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "„Dateipasswort“ und „Dateipasswort bestätigen“ stimmen nicht überein." }, @@ -3817,28 +3823,28 @@ "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "assignToCollections": { - "message": "Assign to collections" + "message": "Sammlungen zuweisen" }, "assignToTheseCollections": { - "message": "Assign to these collections" + "message": "Diesen Sammlungen zuweisen" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Nur Organisationsmitglieder mit Zugriff auf diese Sammlungen können diesen Eintrag sehen." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Nur Organisationsmitglieder mit Zugriff auf diese Sammlungen können die Einträge sehen." }, "noCollectionsAssigned": { - "message": "No collections have been assigned" + "message": "Es wurden keine Sammlungen zugewiesen" }, "assign": { - "message": "Assign" + "message": "Zuweisen" }, "bulkCollectionAssignmentDialogDescription": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Nur Organisationsmitglieder mit Zugriff auf diese Sammlungen können die Einträge sehen." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Du hast $TOTAL_COUNT$ Einträge ausgewählt. Du kannst $READONLY_COUNT$ der Einträge nicht aktualisieren, da du keine Bearbeitungsrechte hast.", "placeholders": { "total_count": { "content": "$1", @@ -3850,10 +3856,10 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "Zu zuweisende Sammlungen auswählen" }, "personalItemsTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ werden dauerhaft an die ausgewählte Organisation übertragen. Du wirst diese Einträge nicht mehr besitzen.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3862,7 +3868,7 @@ } }, "personalItemsWithOrgTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ werden dauerhaft an $ORG$ übertragen. Du wirst diese Einträge nicht mehr besitzen.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3875,10 +3881,10 @@ } }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1 Eintrag wird dauerhaft an die ausgewählte Organisation übertragen. Du wirst diesen Eintrag nicht mehr besitzen." }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "1 Eintrag wird dauerhaft an $ORG$ übertragen. Du wirst diesen Eintrag nicht mehr besitzen.", "placeholders": { "org": { "content": "$1", @@ -3887,13 +3893,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "Sammlungen erfolgreich zugewiesen" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "Du hast nichts ausgewählt." }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Einträge verschoben nach $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3902,7 +3908,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Eintrag verschoben nach $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3911,7 +3917,7 @@ } }, "movedItemsToOrg": { - "message": "Selected items moved to $ORGNAME$", + "message": "Ausgewählte Einträge in $ORGNAME$ verschoben", "placeholders": { "orgname": { "content": "$1", diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index e2f14e3726e..57e0cb0b2f8 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -24,7 +24,7 @@ "message": "Ταυτότητα" }, "typeNote": { - "message": "Note" + "message": "Σημείωση" }, "typeSecureNote": { "message": "Ασφαλής σημείωση" @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Ο λογαριασμός περιορίστηκε" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Το \"Κωδικός πρόσβασης αρχείου\" και το \"Επιβεβαίωση κωδικού πρόσβασης αρχείου\" δεν ταιριάζουν." }, @@ -2628,7 +2634,7 @@ "message": "Δημιουργία διεύθυνσης ηλ. ταχυδρομείου" }, "usernameGenerator": { - "message": "Username generator" + "message": "Γεννήτρια ονόματος χρήστη" }, "generatePassword": { "message": "Γέννηση κωδικού πρόσβασης" @@ -2637,13 +2643,13 @@ "message": "Δημιουργία φράσης πρόσβασης" }, "passwordGenerated": { - "message": "Password generated" + "message": "Ο κωδικός πρόσβασης δημιουργήθηκε" }, "passphraseGenerated": { "message": "Passphrase generated" }, "usernameGenerated": { - "message": "Username generated" + "message": "Το όνομα χρήστη δημιουργήθηκε" }, "emailGenerated": { "message": "Email generated" @@ -3600,7 +3606,7 @@ "message": "Secure password generated! Don't forget to also update your password on the website." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Χρησιμοποιήστε τη γεννήτρια", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { @@ -3734,10 +3740,10 @@ "message": "Μετακίνηση" }, "newFolder": { - "message": "New folder" + "message": "Νέος φάκελος" }, "folderName": { - "message": "Folder Name" + "message": "Όνομα φακέλου" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -3751,7 +3757,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "Γρήγορη δημιουργία κωδικών πρόσβασης" }, "generatorNudgeBodyOne": { "message": "Easily create strong and unique passwords by clicking on", @@ -3820,7 +3826,7 @@ "message": "Assign to collections" }, "assignToTheseCollections": { - "message": "Assign to these collections" + "message": "Ανάθεση σε αυτές τις συλλογές" }, "bulkCollectionAssignmentDialogDescriptionSingular": { "message": "Only organization members with access to these collections will be able to see the item." @@ -3902,7 +3908,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Το στοιχείο μεταφέρθηκε στο $ORGNAME$", "placeholders": { "orgname": { "content": "$1", diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 7fce4462848..70a59a096d0 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index fa66606d267..c1f46961086 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index caf3d656d5d..d63ff473ed1 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Ne akordas la «Pasvorto de la dosiero» kaj «Konfirmu la pasvorton de la dosiero»." }, diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 74e978159b0..a14ea40f0b6 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -24,7 +24,7 @@ "message": "Identidad" }, "typeNote": { - "message": "Note" + "message": "Nota" }, "typeSecureNote": { "message": "Nota segura" @@ -238,16 +238,16 @@ "message": "Activa el agente SSH para firmar peticiones SSH directamente desde tu caja fuerte de Bitwarden." }, "enableSshAgentHelp": { - "message": "The SSH agent is a service targeted at developers that allows you to sign SSH requests directly from your Bitwarden vault." + "message": "El agente SSH es un servicio dirigido a desarrolladores que te permite firmar peticiones SSH directamente desde tu caja fuerte de Bitwarden." }, "sshAgentPromptBehavior": { - "message": "Ask for authorization when using SSH agent" + "message": "Solicitar autorización al usar el agente SSH" }, "sshAgentPromptBehaviorDesc": { "message": "Choose how to handle SSH-agent authorization requests." }, "sshAgentPromptBehaviorHelp": { - "message": "Remember SSH authorizations" + "message": "Recordar autorizaciones SSH" }, "sshAgentPromptBehaviorAlways": { "message": "Siempre" @@ -256,7 +256,7 @@ "message": "Nunca" }, "sshAgentPromptBehaviorRememberUntilLock": { - "message": "Remember until vault is locked" + "message": "Recordar hasta que la caja fuerte se bloquee" }, "premiumRequired": { "message": "Premium requerido" @@ -409,16 +409,16 @@ "message": "Clave de autenticación (TOTP)" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Clave de autenticador" }, "autofillOptions": { - "message": "Autofill options" + "message": "Opciones de autocompletado" }, "websiteUri": { - "message": "Website (URI)" + "message": "Sitio web (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "Sitio web (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -428,7 +428,7 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Página web añadida" }, "addWebsite": { "message": "Añadir página web" @@ -458,10 +458,10 @@ "message": "Añadir" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Usa campos de texto para datos como preguntas de seguridad" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Usa campos ocultos para datos sensibles como una contraseña" }, "checkBoxHelpText": { "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" @@ -563,7 +563,7 @@ "message": "Copiar clave privada SSH" }, "copyPassphrase": { - "message": "Copy passphrase", + "message": "Copiar frase de contraseña", "description": "Copy passphrase to clipboard" }, "copyUri": { @@ -657,7 +657,7 @@ "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "Los requisitos de política empresarial se han aplicado a las opciones de tu generador.", "description": "Indicates that a policy limits the credential generator screen." }, "searchCollection": { @@ -695,7 +695,7 @@ "message": "El tamaño máximo de archivo es de 500MB." }, "legacyEncryptionUnsupported": { - "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + "message": "La encriptación antigua ya no está soportada. Por favor, contacta con soporte para recuperar tu cuenta." }, "editedFolder": { "message": "Carpeta editada" @@ -740,13 +740,13 @@ "message": "Pulsa tu YubiKey para autenticarte" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Iniciar sesión con clave de acceso" }, "loginWithDevice": { - "message": "Log in with device" + "message": "Iniciar sesión con dispositivo" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Usar inicio de sesión único" }, "submit": { "message": "Enviar" @@ -792,7 +792,7 @@ "message": "Pista de la contraseña maestra" }, "passwordStrengthScore": { - "message": "Password strength score $SCORE$", + "message": "Puntuación de seguridad de la contraseña $SCORE$", "placeholders": { "score": { "content": "$1", @@ -998,7 +998,7 @@ "message": "Opciones de la autenticación en dos pasos" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Selecciona un método de inicio de sesión en dos pasos" }, "selfHostedEnvironment": { "message": "Entorno de alojamiento propio" @@ -1465,7 +1465,7 @@ "message": "Comprar Premium" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "Puedes comprar el Premium desde la configuración de tu cuenta en la aplicación web de Bitwarden." }, "premiumCurrentMember": { "message": "¡Eres un miembro Premium!" @@ -1489,13 +1489,13 @@ "message": "Historial de contraseñas" }, "generatorHistory": { - "message": "Generator history" + "message": "Historial del generador" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Limpiar historial del generador" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Si continúas, todas las entradas se eliminarán permanentemente del historial del generador. ¿Estás seguro de que quieres continuar?" }, "clear": { "message": "Limpiar", @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Cuenta restringida" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "Una política establecida en 1 o más organizaciones te impide importar tarjetas a tus cajas fuertes." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Contraseña del archivo\" y \"Confirmar contraseña del archivo\" no coinciden." }, @@ -1794,7 +1800,7 @@ "message": "Ajustes adicionales de Windows Hello" }, "unlockWithPolkit": { - "message": "Unlock with system authentication" + "message": "Desbloquear con la autenticación del sistema" }, "windowsHelloConsentMessage": { "message": "Verificar para Bitwarden." @@ -1812,7 +1818,7 @@ "message": "Solicitar Windows Hello al iniciar" }, "autoPromptPolkit": { - "message": "Ask for system authentication on launch" + "message": "Solicitar autenticación de sistema al iniciar" }, "autoPromptTouchId": { "message": "Solicitar Touch ID al iniciar" @@ -1827,7 +1833,7 @@ "message": "Recomendado por seguridad." }, "lockWithMasterPassOnRestart1": { - "message": "Lock with master password on restart" + "message": "Bloquear con contraseña maestra al reiniciar" }, "deleteAccount": { "message": "Eliminar cuenta" @@ -1842,7 +1848,7 @@ "message": "No se puede eliminar la cuenta" }, "cannotDeleteAccountDesc": { - "message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details." + "message": "Esta acción no se puede completar porque tu cuenta es de propiedad de una organización. Contacta con el administrador de tu organización para más detalles." }, "accountDeleted": { "message": "Cuenta eliminada" @@ -1964,10 +1970,10 @@ } }, "cardDetails": { - "message": "Card details" + "message": "Datos de la tarjeta" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "Datos de $BRAND$", "placeholders": { "brand": { "content": "$1", @@ -1976,26 +1982,26 @@ } }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Más información sobre los autenticadores" }, "copyTOTP": { - "message": "Copy Authenticator key (TOTP)" + "message": "Copiar clave de Autenticador (TOTP)" }, "totpHelperTitle": { "message": "Make 2-step verification seamless" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden puede almacenar y rellenar códigos de verificación en 2 pasos. Copia y pega la clave en este campo." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden puede almacenar y rellenar códigos de verificación en 2 pasos. Selecciona el icono de la cámara para hacer una captura de pantalla del código QR de este sitio web, o copia y pega la clave en este campo." }, "premium": { "message": "Premium", "description": "Premium membership" }, "freeOrgsCannotUseAttachments": { - "message": "Free organizations cannot use attachments" + "message": "Las organizaciones gratuitas no pueden usar adjuntos" }, "singleFieldNeedsAttention": { "message": "1 campo necesita tu atención." @@ -2071,7 +2077,7 @@ "message": "Su nueva contraseña maestra no cumple con los requisitos de la política." }, "receiveMarketingEmailsV2": { - "message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox." + "message": "Obtén consejos, anuncios y oportunidades de investigación de Bitwarden en tu bandeja de entrada." }, "unsubscribe": { "message": "Darse de baja" @@ -2095,7 +2101,7 @@ "message": "Habilitar integración con el navegador" }, "enableBrowserIntegrationDesc1": { - "message": "Used to allow biometric unlock in browsers that are not Safari." + "message": "Usado para permitir el desbloqueo biométrico en navegadores que no son Safari." }, "enableDuckDuckGoBrowserIntegration": { "message": "Permitir integración con el navegador DuckDuckGo" @@ -2444,7 +2450,7 @@ "message": "Minutos" }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "$HOURS$ hora(s) y $MINUTES$ minuto(s) como máximo.", "placeholders": { "hours": { "content": "$1", @@ -2628,28 +2634,28 @@ "message": "Generar correo electrónico" }, "usernameGenerator": { - "message": "Username generator" + "message": "Generador de nombre de usuario" }, "generatePassword": { "message": "Generar contraseña" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Generar frase de contraseña" }, "passwordGenerated": { - "message": "Password generated" + "message": "Contraseña generada" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "Frase de contraseña generada" }, "usernameGenerated": { - "message": "Username generated" + "message": "Nombre de usuario generado" }, "emailGenerated": { - "message": "Email generated" + "message": "Correo electrónico generado" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "El valor debe estar entre $MIN$ y $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2663,7 +2669,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Usa $RECOMMENDED$ caracteres o más para generar una contraseña segura.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2673,7 +2679,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Usa $RECOMMENDED$ palabras o más para generar una frase de contraseña segura.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2705,7 +2711,7 @@ "message": "Usar esta contraseña" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "Usar esta frase de contraseña" }, "useThisUsername": { "message": "Usar este nombre de usuario" @@ -2742,7 +2748,7 @@ "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Elige un dominio que esté soportado por el servicio seleccionado", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -2798,7 +2804,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ ha rechazado tu solicitud. Por favor, contacta con tu proveedor del servicio para obtener asistencia.", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -2808,7 +2814,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ ha rechazado tu solicitud $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -2927,13 +2933,13 @@ "message": "Se ha enviado una notificación a tu dispositivo." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Se ha enviado una notificación a tu dispositivo" }, "notificationSentDevicePart1": { - "message": "Unlock Bitwarden on your device or on the " + "message": "Desbloquea Bitwarden en tu dispositivo o en la " }, "notificationSentDeviceAnchor": { - "message": "web app" + "message": "aplicación web" }, "notificationSentDevicePart2": { "message": "Make sure the Fingerprint phrase matches the one below before approving." @@ -2954,7 +2960,7 @@ "message": "Iniciar sesión con el dispositivo debe estar habilitado en los ajustes de la aplicación móvil Bitwarden. ¿Necesitas otra opción?" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Ver todas las opciones de inicio de sesión" }, "viewAllLoginOptions": { "message": "Ver todas las opciones de inicio de sesión" @@ -2967,7 +2973,7 @@ "description": "'Character count' describes a feature that displays a number next to each character of the password." }, "areYouTryingToAccessYourAccount": { - "message": "Are you trying to access your account?" + "message": "¿Estás intentando acceder a tu cuenta?" }, "accessAttemptBy": { "message": "Intento de acceso de $EMAIL$", @@ -3079,7 +3085,7 @@ "message": "Comprobar filtración de datos conocidos para esta contraseña" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "¡Sesión iniciada!" }, "important": { "message": "Importante:" @@ -3112,7 +3118,7 @@ "message": "Actualización de ajustes recomendados" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Recuerda este dispositivo para hacer los futuros inicios de sesión fluidos" }, "deviceApprovalRequired": { "message": "Se requiere aprobación del dispositivo. Selecciona una opción de aprobación a continuación:" @@ -3176,7 +3182,7 @@ "message": "Dispositivo de confianza" }, "trustOrganization": { - "message": "Trust organization" + "message": "Confiar en la organización" }, "trust": { "message": "Confiar" @@ -3185,7 +3191,7 @@ "message": "No confiar" }, "organizationNotTrusted": { - "message": "Organization is not trusted" + "message": "La organización no es de confianza" }, "emergencyAccessTrustWarning": { "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" @@ -3197,7 +3203,7 @@ "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." }, "trustUser": { - "message": "Trust user" + "message": "Confiar con el usuario" }, "inputRequired": { "message": "La entrada requerida." @@ -3358,7 +3364,7 @@ } }, "duoHealthCheckResultsInNullAuthUrlError": { - "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + "message": "Error al conectarse con el servicio Duo. Utiliza un método de inicio de sesión en dos pasos diferente o ponte en contacto con Duo para obtener ayuda." }, "duoRequiredByOrgForAccount": { "message": "Se requiere el inicio de sesión en dos pasos de Duo para tu cuenta." @@ -3597,14 +3603,14 @@ "message": "No free ports could be found for the sso login." }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "¡Contraseña segura generada! No olvides actualizar tu contraseña en el sitio web." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Usa el generador", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "para crear una contraseña única y segura", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "biometricsStatusHelptextUnlockNeeded": { @@ -3632,10 +3638,10 @@ "message": "Biometric unlock is currently unavailable for an unknown reason." }, "itemDetails": { - "message": "Item details" + "message": "Detalles del elemento" }, "itemName": { - "message": "Item name" + "message": "Nombre del elemento" }, "loginCredentials": { "message": "Login credentials" @@ -3644,13 +3650,13 @@ "message": "Opciones adicionales" }, "itemHistory": { - "message": "Item history" + "message": "Historial del elemento" }, "lastEdited": { - "message": "Last edited" + "message": "Última edición" }, "upload": { - "message": "Upload" + "message": "Subir" }, "authorize": { "message": "Autorizar" @@ -3665,16 +3671,16 @@ "message": "Warning: Agent Forwarding" }, "agentForwardingWarningText": { - "message": "This request comes from a remote device that you are logged into" + "message": "Esta solicitud viene de un dispositivo remoto en el que has iniciado sesión" }, "sshkeyApprovalMessageInfix": { "message": "está solicitando acceso a" }, "sshkeyApprovalMessageSuffix": { - "message": "in order to" + "message": "para" }, "sshActionLogin": { - "message": "authenticate to a server" + "message": "autenticar en un servidor" }, "sshActionSign": { "message": "firmar un mensaje" @@ -3719,10 +3725,10 @@ "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" + "message": "Cambiar contraseña en riesgo" }, "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "message": "No puedes eliminar colecciones con permisos de solo Visualización: $COLLECTIONS$", "placeholders": { "collections": { "content": "$1", @@ -3737,41 +3743,41 @@ "message": "Nueva carpeta" }, "folderName": { - "message": "Folder Name" + "message": "Nombre de la Carpeta" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "Envía información sensible de forma segura", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "Comparte archivos y datos de forma segura con cualquiera, en cualquier plataforma. Tu información permanecerá encriptada de extremo a extremo, limitando su exposición.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "Crear contraseñas rápidamente" }, "generatorNudgeBodyOne": { - "message": "Easily create strong and unique passwords by clicking on", + "message": "Crea fácilmente contraseñas seguras y únicas haciendo clic en", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyTwo": { - "message": "to help you keep your logins secure.", + "message": "para ayudarte a mantener tus inicios de sesión seguros.", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyAria": { - "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.", + "message": "Crea fácilmente contraseñas seguras y únicas haciendo clic en el botón Generar contraseña para ayudarte a mantener tus inicios de sesión seguros.", "description": "Aria label for the body content of the generator nudge" }, "newLoginNudgeTitle": { - "message": "Save time with autofill" + "message": "Ahora tiempo con autocompletado" }, "newLoginNudgeBodyOne": { - "message": "Include a", + "message": "Incluír un", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, @@ -3781,7 +3787,7 @@ "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyTwo": { - "message": "so this login appears as an autofill suggestion.", + "message": "para que este inicio de sesión aparezca como una sugerencia de autocompletado.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, @@ -3789,19 +3795,19 @@ "message": "Seamless online checkout" }, "newCardNudgeBody": { - "message": "With cards, easily autofill payment forms securely and accurately." + "message": "Con las tarjetas, autocompleta fácilmente formularios de pago de forma segura y precisa." }, "newIdentityNudgeTitle": { - "message": "Simplify creating accounts" + "message": "Simplifica la creación de cuentas" }, "newIdentityNudgeBody": { - "message": "With identities, quickly autofill long registration or contact forms." + "message": "Con las identidades, autocompleta rápidamente formularios largos de registro o de contacto." }, "newNoteNudgeTitle": { - "message": "Keep your sensitive data safe" + "message": "Mantén tus datos sensibles seguros" }, "newNoteNudgeBody": { - "message": "With notes, securely store sensitive data like banking or insurance details." + "message": "Con las notas, almacena de forma segura datos sensibles como datos bancarios o de seguros." }, "newSshNudgeTitle": { "message": "Developer-friendly SSH access" @@ -3812,15 +3818,15 @@ "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "Más información sobre el agente SSH", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "assignToCollections": { - "message": "Assign to collections" + "message": "Asignar a colecciones" }, "assignToTheseCollections": { - "message": "Assign to these collections" + "message": "Asignar a estas colecciones" }, "bulkCollectionAssignmentDialogDescriptionSingular": { "message": "Only organization members with access to these collections will be able to see the item." @@ -3829,10 +3835,10 @@ "message": "Only organization members with access to these collections will be able to see the items." }, "noCollectionsAssigned": { - "message": "No collections have been assigned" + "message": "No se han asignado colecciones" }, "assign": { - "message": "Assign" + "message": "Asignar" }, "bulkCollectionAssignmentDialogDescription": { "message": "Only organization members with access to these collections will be able to see the items." @@ -3853,7 +3859,7 @@ "message": "Select collections to assign" }, "personalItemsTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ serán transferidos permanentemente a la organización seleccionada. Ya no serás el propietario de estos elementos.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3862,7 +3868,7 @@ } }, "personalItemsWithOrgTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ serán transferidos permanentemente a $ORG$. Ya no serás el propietario de estos elementos.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3875,10 +3881,10 @@ } }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1 elemento será transferido permanentemente a la organización seleccionada. Ya no serás el propietario de este elemento." }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "1 elemento será transferido permanentemente a $ORG$. Ya no serás el propietario de este elemento.", "placeholders": { "org": { "content": "$1", @@ -3893,7 +3899,7 @@ "message": "You have not selected anything." }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Elementos movidos a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3902,7 +3908,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Elemento movido a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3911,7 +3917,7 @@ } }, "movedItemsToOrg": { - "message": "Selected items moved to $ORGNAME$", + "message": "Elementos seleccionados movidos a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index cbc10972fc5..bfbcabdf815 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Kontosisene" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Faili parool\" ja \"Faili parooli kinnitus\" ei kattu." }, diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 7bcbc880990..8845ec04aff 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 1727def9850..ae227b68e4e 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "حساب کاربری محدود شده است" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "عدم تطابق \"کلمه عبور پرونده\" و \"تأیید کلمه عبور پرونده\" با یکدیگر." }, diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index a5fb514b510..53370012ada 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Tiliä on rajoitettu" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Tiedoston salasana\" ja \"Vahvista tiedoston salasana\" eivät täsmää." }, diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index d66b678d31d..d2823199175 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 4529c4e37ea..7fb2017b131 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Compte restreint" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Le \"Mot de passe du fichier\" et la \"Confirmation du mot de passe du fichier\" ne correspondent pas." }, diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 5c8144a687d..fdedc6a97e2 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index cb65639bb93..8d2ebf8bb98 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "מוגבל חשבון" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"סיסמת קובץ\" ו\"אשר סיסמת קובץ\" אינם תואמים." }, diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 5a67b992f68..35076bcb184 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index adcbbd44960..557695c5b6c 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Račun ograničen" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Lozinka se ne podudara." }, diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 179d55b7be0..f06f4adb99c 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Korlátozott fiók" }, + "restrictCardTypeImport": { + "message": "A kártya elem típusokat nem lehet importálni." + }, + "restrictCardTypeImportDesc": { + "message": "Egy vagy több szervezet által beállított szabályzat megakadályozza a kártyák importálását a széfekbe." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "A “Fájl jelszó” és a “Fájl jelszó megerősítés“ nem egyezik." }, diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 90114e44604..df8993d75f3 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index ca67e4b1b81..b11c83c5de4 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account limitato" }, + "restrictCardTypeImport": { + "message": "Impossibile importare elementi di tipo carta" + }, + "restrictCardTypeImportDesc": { + "message": "Non puoi importare carte nelle tue casseforti a causa di una politica impostata da una o più organizzazioni." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Le due password del file non corrispondono." }, diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 708feba5d0f..f4d2123c197 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "アカウント制限" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "「ファイルパスワード」と「ファイルパスワードの確認」が一致しません。" }, diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 1de20b49b47..6c5e8321126 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 5c8144a687d..fdedc6a97e2 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index bae9f2a773d..254b8ab988d 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 4e6a39f6e7f..95620ce5f1f 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 4212369aa95..1e876467155 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 959c7b27589..e4fe5851e8e 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Konts ir ierobežots" }, + "restrictCardTypeImport": { + "message": "Nevar ievietot karšu vienumu veidus" + }, + "restrictCardTypeImportDesc": { + "message": "Pamatnostādne, ko ir iestatījusi viena vai vairākas apvienības, liedz karšu ievietošanu savās glabātavās." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Datnes parole\" un \"Apstiprināt datnes paroli\" vērtības nesakrīt." }, diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 2663732da0f..142a5e371f9 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 41352017c44..56c6b32ac35 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 5c8144a687d..fdedc6a97e2 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 0809df9790e..95724d9f2d8 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index e9c52fda662..ef96e448e8c 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Konto begrenset" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "«Filpassord» og «Bekreft filpassord» stemmer ikke overens." }, diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index ff5fc1dceec..2b92b43ff03 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 951681c1ce7..35dd7233e50 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account beperkt" }, + "restrictCardTypeImport": { + "message": "Kan kaart item types niet importeren" + }, + "restrictCardTypeImportDesc": { + "message": "Een beleid ingesteld door 1 of meer organisaties voorkomt dat je kaarten naar je kluizen kunt importeren." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Bestandswachtwoord\" en \"Bestandswachtwoord bevestigen\" komen niet overeen." }, diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 23bfdbd64bf..278968c4eb0 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index c79a7d86b4e..5333775eabe 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 91dfab48cf2..0d70760eff8 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Konto ograniczone" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“Hasło pliku” i “Potwierdź hasło pliku“ nie pasują do siebie." }, diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 6279a1c3785..376277ebd7e 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Conta restrita" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Senha do arquivo\" e \"Confirmação de senha\" não correspondem." }, diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index eb7e631c9a9..33e858b7603 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Conta restringida" }, + "restrictCardTypeImport": { + "message": "Não é possível importar tipos de itens de cartão" + }, + "restrictCardTypeImportDesc": { + "message": "Uma política definida por 1 ou mais organizações impede-o de importar cartões para os seus cofres." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Palavra-passe do ficheiro\" e \"Confirmar palavra-passe do ficheiro\" não correspondem." }, diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 0d879beaa8b..b66151a9429 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index c5034596e07..f305b877d0f 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Ограничено аккаунтом" }, + "restrictCardTypeImport": { + "message": "Невозможно импортировать элементы карт" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, установленная 1 или более организациями, не позволяет импортировать карты в ваши хранилища." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Пароль к файлу\" и \"Подтверждение пароля к файлу\" не совпадают." }, diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 6614ff83562..15b5b750cd2 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 1aeeed61bb6..8cbeaf1a934 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Obmedzený účet" }, + "restrictCardTypeImport": { + "message": "Položky typu karta sa nedajú importovať" + }, + "restrictCardTypeImportDesc": { + "message": "Politika nastavená 1 alebo viacerými organizáciami vám bráni v importovaní kariet do vašich trezorov." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Heslo súboru\" a \"Potvrdiť heslo súboru\" sa nezhodujú." }, diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index d38b671ca63..715d08f2e6e 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 86a7acad827..54405b8a2ac 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -24,7 +24,7 @@ "message": "Идентитет" }, "typeNote": { - "message": "Note" + "message": "Белешка" }, "typeSecureNote": { "message": "Сигурносна белешка" @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Налог је ограничен" }, + "restrictCardTypeImport": { + "message": "Не могу увозити врсте картица" + }, + "restrictCardTypeImportDesc": { + "message": "Политика која је поставила 1 или више организација спречава вас да се увозе картице у сефу." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Унете лозинке се не подударају." }, @@ -3817,28 +3823,28 @@ "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "assignToCollections": { - "message": "Assign to collections" + "message": "Додели колекцијама" }, "assignToTheseCollections": { - "message": "Assign to these collections" + "message": "Додели овим колекцијама" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Само чланови организације са приступом овим збиркама ће моћи да виде ставку." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Само чланови организације са приступом овим збиркама ће моћи да виде ставке." }, "noCollectionsAssigned": { - "message": "No collections have been assigned" + "message": "Није додељена ниједна колекција" }, "assign": { - "message": "Assign" + "message": "Додели" }, "bulkCollectionAssignmentDialogDescription": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Само чланови организације са приступом овим збиркама ће моћи да виде ставке." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Одабрали сте $TOTAL_COUNT$ ставки. Не можете да ажурирате $READONLY_COUNT$ од ставки јер немате дозволе за уређивање.", "placeholders": { "total_count": { "content": "$1", @@ -3850,10 +3856,10 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "Изаберите колекције за доделу" }, "personalItemsTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ биће трајно пребачени у изабрану организацију. Више нећете имати ове ставке.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3862,7 +3868,7 @@ } }, "personalItemsWithOrgTransferWarning": { - "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ биће трајно пребачени у $ORG$. Више нећете имати ове ставке.", "placeholders": { "personal_items_count": { "content": "$1", @@ -3875,10 +3881,10 @@ } }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1 ставка биће трајно пребачена у изабрану организацију. Више нећете имати ову ставку." }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "1 ставка биће трајно пребачена у $ORG$. Више нећете имати ову ставку.", "placeholders": { "org": { "content": "$1", @@ -3887,13 +3893,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "Успешно додељене колекције" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "Нисте ништа изабрали." }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Ставке премештене у $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3902,7 +3908,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Ставка премештена у $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -3911,7 +3917,7 @@ } }, "movedItemsToOrg": { - "message": "Selected items moved to $ORGNAME$", + "message": "Одабране ставке премештене у $ORGNAME$", "placeholders": { "orgname": { "content": "$1", diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 41eea807bd2..1ead0c46d60 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 5c8144a687d..fdedc6a97e2 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index fc380ac7f9f..3f37cd197fc 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Account restricted" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index b8645b3db82..a84dd908621 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Hesap kısıtlı" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Dosya parolası\" ile \"Dosya parolasını onaylayın\" eşleşmiyor." }, diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 232795225ef..f7d8520bc30 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Обмежено обліковим записом" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "Пароль файлу та підтвердження пароля відрізняються." }, diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 5480fbad37c..f7b588e356a 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "Tài khoản bị hạn chế" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“Mật khẩu tập tin” và “Nhập lại mật khẩu tập tin” không khớp." }, diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index c8f66245efd..ad3d97f467f 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "账户限制" }, + "restrictCardTypeImport": { + "message": "无法导入支付卡项目类型" + }, + "restrictCardTypeImportDesc": { + "message": "由 1 个或多个组织设置的策略阻止您将支付卡导入密码库。" + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "「文件密码」与「确认文件密码」不一致。" }, @@ -1779,7 +1785,7 @@ "message": "设置用于解锁 Bitwarden 的 PIN 码。您的 PIN 设置将在您完全注销应用程序时被重置。" }, "pinRequired": { - "message": "需要 PIN 码。" + "message": "必须填写 PIN 码。" }, "invalidPin": { "message": "无效的 PIN 码。" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index cf1e811b9e1..ea017a14489 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -1716,6 +1716,12 @@ "accountRestricted": { "message": "帳戶已限制" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "「檔案密碼」與「確認檔案密碼」不一致。" }, From 8ab44dd992a72a46ac9e86cb4bd011b467a9b0af Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 10:55:08 +0200 Subject: [PATCH 26/57] Autosync the updated translations (#15364) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 10 +- apps/browser/src/_locales/az/messages.json | 10 +- apps/browser/src/_locales/be/messages.json | 10 +- apps/browser/src/_locales/bg/messages.json | 10 +- apps/browser/src/_locales/bn/messages.json | 10 +- apps/browser/src/_locales/bs/messages.json | 10 +- apps/browser/src/_locales/ca/messages.json | 10 +- apps/browser/src/_locales/cs/messages.json | 10 +- apps/browser/src/_locales/cy/messages.json | 10 +- apps/browser/src/_locales/da/messages.json | 10 +- apps/browser/src/_locales/de/messages.json | 14 +- apps/browser/src/_locales/el/messages.json | 10 +- apps/browser/src/_locales/en_GB/messages.json | 10 +- apps/browser/src/_locales/en_IN/messages.json | 10 +- apps/browser/src/_locales/es/messages.json | 214 +++++++++--------- apps/browser/src/_locales/et/messages.json | 10 +- apps/browser/src/_locales/eu/messages.json | 10 +- apps/browser/src/_locales/fa/messages.json | 10 +- apps/browser/src/_locales/fi/messages.json | 10 +- apps/browser/src/_locales/fil/messages.json | 10 +- apps/browser/src/_locales/fr/messages.json | 10 +- apps/browser/src/_locales/gl/messages.json | 10 +- apps/browser/src/_locales/he/messages.json | 10 +- apps/browser/src/_locales/hi/messages.json | 10 +- apps/browser/src/_locales/hr/messages.json | 10 +- apps/browser/src/_locales/hu/messages.json | 10 +- apps/browser/src/_locales/id/messages.json | 10 +- apps/browser/src/_locales/it/messages.json | 10 +- apps/browser/src/_locales/ja/messages.json | 10 +- apps/browser/src/_locales/ka/messages.json | 10 +- apps/browser/src/_locales/km/messages.json | 10 +- apps/browser/src/_locales/kn/messages.json | 10 +- apps/browser/src/_locales/ko/messages.json | 10 +- apps/browser/src/_locales/lt/messages.json | 10 +- apps/browser/src/_locales/lv/messages.json | 10 +- apps/browser/src/_locales/ml/messages.json | 10 +- apps/browser/src/_locales/mr/messages.json | 10 +- apps/browser/src/_locales/my/messages.json | 10 +- apps/browser/src/_locales/nb/messages.json | 10 +- apps/browser/src/_locales/ne/messages.json | 10 +- apps/browser/src/_locales/nl/messages.json | 10 +- apps/browser/src/_locales/nn/messages.json | 10 +- apps/browser/src/_locales/or/messages.json | 10 +- apps/browser/src/_locales/pl/messages.json | 14 +- apps/browser/src/_locales/pt_BR/messages.json | 10 +- apps/browser/src/_locales/pt_PT/messages.json | 10 +- apps/browser/src/_locales/ro/messages.json | 10 +- apps/browser/src/_locales/ru/messages.json | 10 +- apps/browser/src/_locales/si/messages.json | 10 +- apps/browser/src/_locales/sk/messages.json | 8 +- apps/browser/src/_locales/sl/messages.json | 10 +- apps/browser/src/_locales/sr/messages.json | 24 +- apps/browser/src/_locales/sv/messages.json | 10 +- apps/browser/src/_locales/te/messages.json | 10 +- apps/browser/src/_locales/th/messages.json | 10 +- apps/browser/src/_locales/tr/messages.json | 10 +- apps/browser/src/_locales/uk/messages.json | 10 +- apps/browser/src/_locales/vi/messages.json | 10 +- apps/browser/src/_locales/zh_CN/messages.json | 14 +- apps/browser/src/_locales/zh_TW/messages.json | 10 +- 60 files changed, 594 insertions(+), 234 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 85a535f2476..6c7f7f0fccd 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "لقد حالت سياسة المؤسسة دون استيراد العناصر إلى خزانتك الشخصية." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "النطاقات", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 5da9db4359c..e189b3ba292 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Bir təşkilat siyasəti, elementlərin fərdi seyfinizə köçürülməsini əngəllədi." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domenlər", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "PIN ilə kilid açma təyini" }, - "unlockBiometricSet": { - "message": "Biometrik ilə kilidi aç ayarı" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Kimlik doğrulama" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 7ec00e7432d..3944569df94 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Дамены", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 32c566db779..53e896e6a72 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Политика на организацията забранява да внасяте елементи в личния си трезор." }, + "restrictCardTypeImport": { + "message": "Картовите елементи не могат да бъдат внесени" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, зададена от 1 или повече организации, не позволява да внасяте карти в трезорите си." + }, "domainsTitle": { "message": "Домейни", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Зададен е ПИН код за отключване" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Отключване с биометричен набор" }, "authenticating": { "message": "Удостоверяване" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 4eb485f1861..29e0e3800c8 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index b1a2cfc3f6d..7c575cdb72b 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index c67496fef54..ff3226255e3 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Una política d'organització ha bloquejat la importació d'elements a la vostra caixa forta individual." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Dominis", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Desbloqueja amb conjunt biomètric" }, "authenticating": { "message": "S'està autenticant" diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 2679ab063af..52c548a93b0 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Zásady organizace zablokovaly importování položek do Vašeho osobního trezoru." }, + "restrictCardTypeImport": { + "message": "Nelze importovat typy položek karty" + }, + "restrictCardTypeImportDesc": { + "message": "Zásady nastavené 1 nebo více organizací Vám brání v importu karet do Vašeho trezoru." + }, "domainsTitle": { "message": "Domény", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "PIN pro odemknutí byl nastaven" }, - "unlockBiometricSet": { - "message": "Odemknout sadu biometriky" + "unlockWithBiometricSet": { + "message": "Odemknout pomocí biometrie" }, "authenticating": { "message": "Ověřování" diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 9940ed173ed..c060d81bcfc 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index d6680f4190a..1b79fc0ecf9 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "En organisationspolitik hindrer import af emner til den individuelle boks." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domæner", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Godkender" diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 8449eb7b966..418c15dc3a0 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1923,7 +1923,7 @@ "message": "SSH-Schlüssel" }, "typeNote": { - "message": "Note" + "message": "Notiz" }, "newItemHeader": { "message": "Neue $TYPE$", @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Eine Organisationsrichtlinie hat das Importieren von Einträgen in deinen persönlichen Tresor deaktiviert." }, + "restrictCardTypeImport": { + "message": "Karten-Eintragstypen können nicht importiert werden" + }, + "restrictCardTypeImportDesc": { + "message": "Eine von einer oder mehreren Organisationen festgelegte Richtlinie verhindert, dass du Karten in deinen Tresor importieren kannst." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Entsperr-PIN festgelegt" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Mit Biometrie entsperren eingerichtet" }, "authenticating": { "message": "Authentifizierung" @@ -5411,7 +5417,7 @@ "message": "Du hast keine Berechtigung, diese Seite anzuzeigen. Versuche dich mit einem anderen Konto anzumelden." }, "wasmNotSupported": { - "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.", + "message": "WebAssembly wird von deinem Browser nicht unterstützt oder ist nicht aktiviert. WebAssembly wird benötigt, um die Bitwarden-App nutzen zu können.", "description": "'WebAssembly' is a technical term and should not be translated." } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 9d023585108..acd7cac0c9b 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Μια οργανωτική πολιτική έχει αποτρέψει την εισαγωγή στοιχείων στο προσωπικό θησαυ/κιο σας." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Τομείς", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Ταυτοποίηση" diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 00488aaf275..ffca2486ff4 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organisation policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 796a51f8bba..02d2ba35060 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organisation policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organisations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 40e20fa2d5e..89d5b928f25 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -887,7 +887,7 @@ "message": "Sigue los pasos de abajo para terminar de iniciar sesión." }, "followTheStepsBelowToFinishLoggingInWithSecurityKey": { - "message": "Follow the steps below to finish logging in with your security key." + "message": "Sigue los siguientes pasos de abajo para terminar de iniciar sesión con tu clave de seguridad." }, "restartRegistration": { "message": "Reiniciar registro" @@ -1076,7 +1076,7 @@ "description": "Aria label for the new item button in notification bar confirmation message when error is prompted" }, "notificationEditTooltip": { - "message": "Edit before saving", + "message": "Editar antes de guardar", "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { @@ -1093,15 +1093,15 @@ } }, "notificationLoginSaveConfirmation": { - "message": "saved to Bitwarden.", + "message": "guardado en Bitwarden.", "description": "Shown to user after item is saved." }, "notificationLoginUpdatedConfirmation": { - "message": "updated in Bitwarden.", + "message": "actualizado en Bitwarden.", "description": "Shown to user after item is updated." }, "selectItemAriaLabel": { - "message": "Select $ITEMTYPE$, $ITEMNAME$", + "message": "Seleccionar $ITEMTYPE$, $ITEMNAME$", "description": "Used by screen readers. $1 is the item type (like vault or folder), $2 is the selected item name.", "placeholders": { "itemType": { @@ -1113,11 +1113,11 @@ } }, "saveAsNewLoginAction": { - "message": "Save as new login", + "message": "Guardar como nuevo inicio de sesión", "description": "Button text for saving login details as a new entry." }, "updateLoginAction": { - "message": "Update login", + "message": "Actualizar inicio de sesión", "description": "Button text for updating an existing login entry." }, "unlockToSave": { @@ -1125,15 +1125,15 @@ "description": "User prompt to take action in order to save the login they just entered." }, "saveLogin": { - "message": "Save login", + "message": "Guardar inicio de sesión", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "Actualizar inicio de sesión existente", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { - "message": "Login saved", + "message": "Inicio de sesión guardado", "description": "Message displayed when login details are successfully saved." }, "loginUpdateSuccess": { @@ -1477,11 +1477,11 @@ "message": "Don't ask again on this device for 30 days" }, "selectAnotherMethod": { - "message": "Select another method", + "message": "Selecciona otro método", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "Usa tu código de recuperación" }, "insertU2f": { "message": "Inserta tu llave de seguridad en el puerto USB de tu equipo. Si tiene un botón, púlsalo." @@ -1493,10 +1493,10 @@ "message": "Autenticar WebAuthn" }, "readSecurityKey": { - "message": "Read security key" + "message": "Leer clave de seguridad" }, "awaitingSecurityKeyInteraction": { - "message": "Awaiting security key interaction..." + "message": "Esperando interacción de la clave de seguridad..." }, "loginUnavailable": { "message": "Entrada no disponible" @@ -1923,7 +1923,7 @@ "message": "Llave SSH" }, "typeNote": { - "message": "Note" + "message": "Nota" }, "newItemHeader": { "message": "Nuevo $TYPE$", @@ -1962,7 +1962,7 @@ "message": "Borrar historial del generador" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Si continúas, todas las entradas se eliminarán permanentemente del historial del generador. ¿Estás seguro de que quieres continuar?" }, "back": { "message": "Atrás" @@ -2027,7 +2027,7 @@ "description": "Domain name. Ex. website.com" }, "baseDomainOptionRecommended": { - "message": "Base domain (recommended)", + "message": "Dominio base (recomendado)", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -2157,7 +2157,7 @@ "message": "Establece tu código PIN para desbloquear Bitwarden. Tus ajustes de PIN se reiniciarán si alguna vez cierras tu sesión completamente de la aplicación." }, "setPinCode": { - "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application." + "message": "Puedes usar este PIN para desbloquear Bitwarden. Tu PIN se reiniciará si alguna vez cierras la sesión de la aplicación por completo." }, "pinRequired": { "message": "Código PIN requerido." @@ -2208,7 +2208,7 @@ "message": "Usar esta contraseña" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "Usar esta frase de contraseña" }, "useThisUsername": { "message": "Usar este nombre de usuario" @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Una política organizacional ha bloqueado la importación de elementos a su caja fuerte personal." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Dominios", "description": "A category title describing the concept of web domains" @@ -2532,7 +2538,7 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "Contraseña en riesgo" }, "atRiskPasswords": { "message": "Contraseñas de riesgo" @@ -2560,7 +2566,7 @@ } }, "atRiskPasswordsDescMultiOrgPlural": { - "message": "Your organizations are requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "Tus organizaciones te están solicitando que cambies las $COUNT$ contraseñas porque están en riesgo.", "placeholders": { "count": { "content": "$1", @@ -2569,7 +2575,7 @@ } }, "atRiskChangePrompt": { - "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.", + "message": "La contraseña para este sitio está en riesgo. $ORGANIZATION$ ha solicitado que la cambies.", "placeholders": { "organization": { "content": "$1", @@ -2579,7 +2585,7 @@ "description": "Notification body when a login triggers an at-risk password change request and the change password domain is known." }, "atRiskNavigatePrompt": { - "message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.", + "message": "$ORGANIZATION$ quiere que cambies esta contraseña porque está en riesgo. Navega a los ajustes de tu cuenta para cambiar la contraseña.", "placeholders": { "organization": { "content": "$1", @@ -2589,10 +2595,10 @@ "description": "Notification body when a login triggers an at-risk password change request and no change password domain is provided." }, "reviewAndChangeAtRiskPassword": { - "message": "Review and change one at-risk password" + "message": "Revisa y cambia una contraseña en riesgo" }, "reviewAndChangeAtRiskPasswordsPlural": { - "message": "Review and change $COUNT$ at-risk passwords", + "message": "Revisa y cambia $COUNT$ contraseñas en riesgo", "placeholders": { "count": { "content": "$1", @@ -2607,7 +2613,7 @@ "message": "Update your settings so you can quickly autofill your passwords and generate new ones" }, "reviewAtRiskLogins": { - "message": "Review at-risk logins" + "message": "Revisar inicios de sesión en riesgo" }, "reviewAtRiskPasswords": { "message": "Revisar contraseñas de riesgo" @@ -2620,7 +2626,7 @@ "message": "Illustration of a list of logins that are at-risk." }, "generatePasswordSlideDesc": { - "message": "Quickly generate a strong, unique password with the Bitwarden autofill menu on the at-risk site.", + "message": "Genera rápidamente una contraseña segura y única con el menú de autocompletado de Bitwarden en el sitio en riesgo.", "description": "Description of the generate password slide on the at-risk password page carousel" }, "generatePasswordSlideImgAltPeriod": { @@ -2677,7 +2683,7 @@ "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ visualizaciones restantes", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2708,11 +2714,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { - "message": "Max access count reached", + "message": "Número máximo de accesos alcanzado", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { - "message": "Hide text by default" + "message": "Ocultar texto por defecto" }, "expired": { "message": "Caducado" @@ -2759,7 +2765,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "¿Estás seguro de que quieres eliminar permanentemente este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -2770,7 +2776,7 @@ "message": "Fecha de eliminación" }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "El Send se borrará permanentemente en esta fecha.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -2792,7 +2798,7 @@ "message": "Personalizado" }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Añade una contraseña opcional para que los destinatarios accedan a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { @@ -2815,15 +2821,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { - "message": "Send created successfully!", + "message": "¡Send creado con éxito!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHoursSingle": { - "message": "The Send will be available to anyone with the link for the next 1 hour.", + "message": "El Send estará disponible a cualquiera que tenga el enlace durante la próxima hora.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHours": { - "message": "The Send will be available to anyone with the link for the next $HOURS$ hours.", + "message": "El Send estará disponible a cualquier que tenga el enlace durante las próximas $HOURS$ horas.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "hours": { @@ -2833,11 +2839,11 @@ } }, "sendExpiresInDaysSingle": { - "message": "The Send will be available to anyone with the link for the next 1 day.", + "message": "El Send estará disponible a cualquier que tenga el enlace durante el próximo día.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInDays": { - "message": "The Send will be available to anyone with the link for the next $DAYS$ days.", + "message": "El Send estará disponible a cualquier que tenga el enlace durante los próximos $DAYS$ días.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "days": { @@ -2847,7 +2853,7 @@ } }, "sendLinkCopied": { - "message": "Send link copied", + "message": "Enlace del Send copiado", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { @@ -3162,7 +3168,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Usa $RECOMMENDED$ palabras o más para generar una frase de contraseña segura.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3207,7 +3213,7 @@ "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Elige un dominio que esté soportado por el servicio seleccionado", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3263,7 +3269,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ ha rechazado tu solicitud. Por favor, contacta con tu proveedor del servicio para obtener asistencia.", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -3273,7 +3279,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ ha rechazado tu solicitud $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3542,10 +3548,10 @@ "message": "Se requiere aprobación del dispositivo. Seleccione una opción de aprobación a continuación:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Aprobación del dispositivo requerida" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Selecciona una opción de aprobación abajo" }, "rememberThisDevice": { "message": "Recordar este dispositivo" @@ -3621,16 +3627,16 @@ "message": "Dispositivo de confianza" }, "trustOrganization": { - "message": "Trust organization" + "message": "Confiar en la organización" }, "trust": { - "message": "Trust" + "message": "Confiar" }, "doNotTrust": { - "message": "Do not trust" + "message": "No confiar" }, "organizationNotTrusted": { - "message": "Organization is not trusted" + "message": "La organización no es de confianza" }, "emergencyAccessTrustWarning": { "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" @@ -3642,7 +3648,7 @@ "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." }, "trustUser": { - "message": "Trust user" + "message": "Confiar con el usuario" }, "sendsTitleNoItems": { "message": "Send sensitive information safely", @@ -3846,7 +3852,7 @@ "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { - "message": "New login", + "message": "Nuevo inicio de sesión", "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { @@ -4045,10 +4051,10 @@ "message": "Clave de acceso" }, "accessing": { - "message": "Accessing" + "message": "Accediendo" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "¡Sesión iniciada!" }, "passkeyNotCopied": { "message": "La clave de acceso no se copiará" @@ -4060,7 +4066,7 @@ "message": "Verificación requerida por el sitio inicial. Esta característica aún no está implementada para cuentas sin contraseña maestra." }, "logInWithPasskeyQuestion": { - "message": "Log in with passkey?" + "message": "¿Iniciar sesión con clave de acceso?" }, "passkeyAlreadyExists": { "message": "Ya existe una clave de acceso para esta aplicación." @@ -4072,7 +4078,7 @@ "message": "No tiene un inicio de sesión que coincida para este sitio." }, "noMatchingLoginsForSite": { - "message": "No matching logins for this site" + "message": "No hay inicios de sesión coincidentes para este sitio" }, "searchSavePasskeyNewLogin": { "message": "Search or save passkey as new login" @@ -4243,7 +4249,7 @@ "description": "Title for dialog which asks if the user wants to proceed to a relevant browser settings page" }, "confirmContinueToHelpCenter": { - "message": "Continue to Help Center?", + "message": "¿Continuar al Centro de Ayuda?", "description": "Title for dialog which asks if the user wants to proceed to a relevant Help Center page" }, "confirmContinueToHelpCenterPasswordManagementContent": { @@ -4384,7 +4390,7 @@ } }, "viewItemTitleWithField": { - "message": "View item - $ITEMNAME$ - $FIELD$", + "message": "Ver elemento - $ITEMNAME$ - $FIELD$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4408,7 +4414,7 @@ } }, "autofillTitleWithField": { - "message": "Autofill - $ITEMNAME$ - $FIELD$", + "message": "Autocompletar - $ITEMNAME$ - $FIELD$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4422,7 +4428,7 @@ } }, "copyFieldValue": { - "message": "Copy $FIELD$, $VALUE$", + "message": "Copiar $FIELD$, $VALUE$", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4439,7 +4445,7 @@ "message": "No hay valores para copiar" }, "assignToCollections": { - "message": "Assign to collections" + "message": "Asignar a colecciones" }, "copyEmail": { "message": "Copiar correo electrónico" @@ -4569,25 +4575,25 @@ } }, "downloadBitwarden": { - "message": "Download Bitwarden" + "message": "Descargar Bitwarden" }, "downloadBitwardenOnAllDevices": { - "message": "Download Bitwarden on all devices" + "message": "Descargar Bitwarden en todos los dispositivos" }, "getTheMobileApp": { - "message": "Get the mobile app" + "message": "Obtén la aplicación móvil" }, "getTheMobileAppDesc": { "message": "Access your passwords on the go with the Bitwarden mobile app." }, "getTheDesktopApp": { - "message": "Get the desktop app" + "message": "Obtén la aplicación de escritorio" }, "getTheDesktopAppDesc": { "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension." }, "downloadFromBitwardenNow": { - "message": "Download from bitwarden.com now" + "message": "Descarga desde bitwarden.com ahora" }, "getItOnGooglePlay": { "message": "Consíguela en Google Play" @@ -4611,10 +4617,10 @@ "message": "Filter vault" }, "filterApplied": { - "message": "One filter applied" + "message": "Un filtro aplicado" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtros aplicados", "placeholders": { "count": { "content": "$1", @@ -4648,10 +4654,10 @@ "message": "Credenciales de inicio de sesión" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Clave de autenticador" }, "autofillOptions": { - "message": "Autofill options" + "message": "Opciones de autocompletado" }, "websiteUri": { "message": "Página web (URI)" @@ -4667,7 +4673,7 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Sitio web añadido" }, "addWebsite": { "message": "Añadir página web" @@ -4710,7 +4716,7 @@ "message": "Tarjeta caducada" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Si la has renovado, actualiza la información de la tarjeta" }, "cardDetails": { "message": "Datos de la tarjeta" @@ -4740,7 +4746,7 @@ "message": "Datos" }, "passkeys": { - "message": "Passkeys", + "message": "Claves de acceso", "description": "A section header for a list of passkeys." }, "passwords": { @@ -4755,10 +4761,10 @@ "message": "Asignar" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Solo los miembros de la organización con acceso a estas colecciones podrán ver el elemento." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Solo los miembros de la organización con acceso a estas colecciones podrán ver los elementos." }, "bulkCollectionAssignmentWarning": { "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", @@ -4785,10 +4791,10 @@ "message": "Etiqueta de campo" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Usa campos de texto para datos como preguntas de seguridad" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Usa campos ocultos para datos sensibles como una contraseña" }, "checkBoxHelpText": { "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" @@ -5030,10 +5036,10 @@ "message": "No tiene permiso de editar este elemento" }, "biometricsStatusHelptextUnlockNeeded": { - "message": "Biometric unlock is unavailable because PIN or password unlock is required first." + "message": "El desbloqueo biométrico no está disponible porque primero es necesario desbloquear con PIN o contraseña." }, "biometricsStatusHelptextHardwareUnavailable": { - "message": "Biometric unlock is currently unavailable." + "message": "El desbloqueo biométrico no está disponible actualmente." }, "biometricsStatusHelptextAutoSetupNeeded": { "message": "Biometric unlock is unavailable due to misconfigured system files." @@ -5042,10 +5048,10 @@ "message": "Biometric unlock is unavailable due to misconfigured system files." }, "biometricsStatusHelptextDesktopDisconnected": { - "message": "Biometric unlock is unavailable because the Bitwarden desktop app is closed." + "message": "El desbloqueo biométrico no está disponible porque la aplicación de escritorio de Bitwarden está cerrada." }, "biometricsStatusHelptextNotEnabledInDesktop": { - "message": "Biometric unlock is unavailable because it is not enabled for $EMAIL$ in the Bitwarden desktop app.", + "message": "El desbloqueo biométrico no está disponible porque no está habilitado para $EMAIL$ en la aplicación de escritorio Bitwarden.", "placeholders": { "email": { "content": "$1", @@ -5054,10 +5060,10 @@ } }, "biometricsStatusHelptextUnavailableReasonUnknown": { - "message": "Biometric unlock is currently unavailable for an unknown reason." + "message": "El desbloqueo biométrico no está disponible actualmente por una razón desconocida." }, "unlockVault": { - "message": "Unlock your vault in seconds" + "message": "Desbloquea tu caja fuete en segundos" }, "unlockVaultDesc": { "message": "You can customize your unlock and timeout settings to more quickly access your vault." @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Autenticando" @@ -5255,16 +5261,16 @@ "message": "Introduzca la contraseña" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "La clave SSH es inválida" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "El tipo de clave SSH no está soportado" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "Importar clave desde el portapapeles" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "Clave SSH importada con éxito" }, "cannotRemoveViewOnlyCollections": { "message": "No puedes eliminar colecciones con permisos de solo visualización: $COLLECTIONS$", @@ -5285,16 +5291,16 @@ "message": "Cambiar contraseña de riesgo" }, "settingsVaultOptions": { - "message": "Vault options" + "message": "Opciones de la caja fuerte" }, "emptyVaultDescription": { "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." }, "introCarouselLabel": { - "message": "Welcome to Bitwarden" + "message": "Bienvenido a Bitwarden" }, "securityPrioritized": { - "message": "Security, prioritized" + "message": "Seguridad, priorizada" }, "securityPrioritizedBody": { "message": "Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you." @@ -5318,19 +5324,19 @@ "message": "Guarda contraseñas ilimitadas a través de dispositivos ilimitados con aplicaciones móviles, de navegador y de escritorio de Bitwarden." }, "nudgeBadgeAria": { - "message": "1 notification" + "message": "1 notificación" }, "emptyVaultNudgeTitle": { - "message": "Import existing passwords" + "message": "Importar contraseñas existentes" }, "emptyVaultNudgeBody": { - "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + "message": "Usa el importador para transferir rápidamente inicios de sesión a Bitwarden sin añadirlos manualmente." }, "emptyVaultNudgeButton": { - "message": "Import now" + "message": "Importar ahora" }, "hasItemsVaultNudgeTitle": { - "message": "Welcome to your vault!" + "message": "¡Bienvenido a tu caja fuerte!" }, "hasItemsVaultNudgeBodyOne": { "message": "Autofill items for the current page" @@ -5350,7 +5356,7 @@ "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "Sitio web", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, @@ -5372,10 +5378,10 @@ "message": "With identities, quickly autofill long registration or contact forms." }, "newNoteNudgeTitle": { - "message": "Keep your sensitive data safe" + "message": "Mantén tus datos sensibles seguros" }, "newNoteNudgeBody": { - "message": "With notes, securely store sensitive data like banking or insurance details." + "message": "Con las notas, almacena de forma segura datos sensibles como datos bancarios o de seguros." }, "newSshNudgeTitle": { "message": "Developer-friendly SSH access" @@ -5386,20 +5392,20 @@ "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "Más información sobre el agente SSH", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "Crea contraseñas rápidamente" }, "generatorNudgeBodyOne": { - "message": "Easily create strong and unique passwords by clicking on", + "message": "Crea fácilmente contraseñas seguras y únicas haciendo clic en", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyTwo": { - "message": "to help you keep your logins secure.", + "message": "para ayudarte a mantener tus inicios de sesión seguros.", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, @@ -5408,7 +5414,7 @@ "description": "Aria label for the body content of the generator nudge" }, "noPermissionsViewPage": { - "message": "You do not have permissions to view this page. Try logging in with a different account." + "message": "No tienes permisos para ver esta página. Intenta iniciar sesión con otra cuenta." }, "wasmNotSupported": { "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.", diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 4046451a567..6893dec3f4f 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 1b04d15f36f..49e87abcf83 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index f07352ca159..a1979d703bf 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "یک سیاست سازمانی، درون ریزی موارد به گاوصندوق فردی شما را مسدود کرده است." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "دامنه‌ها", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "بازکردن قفل کد پین تنظیم شد" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "در حال احراز هویت" diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 5723b9b29c5..098e361223a 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Organisaatiokäytäntö estää kohteiden tuonnin yksityiseen holviisi." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Verkkotunnukset", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Todennetaan" diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index d52eae1b43e..de17e87a57b 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Hinarang ng isang patakaran ng organisasyon ang pag-import ng mga item sa iyong vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index f913e2ab4b3..e1397980675 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Une politique d'organisation a bloqué l'import d'éléments dans votre coffre personel." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domaines", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authentification" diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 41766204775..152f4d7236c 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Unha directiva da empresa impide importar entradas á túa caixa forte individual." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Dominios", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Autenticando" diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 7549d77cac0..215aa17988d 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "מדיניות ארגון חסמה ייבוא פריטים אל תוך הכספת האישית שלך." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "דומיינים", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "מאמת" diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 4d766d1090d..64a98de313b 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 972448319b5..69a68e921ae 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Organizacijsko pravilo onemogućuje uvoz stavki u tvoj osobni trezor." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domene", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Autentifikacija" diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 13d5996469f..c85de0ab542 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "A szervezeti politika blokkolta az elemek importálását az egyedi széfbe." }, + "restrictCardTypeImport": { + "message": "A kártya elem típusokat nem lehet importálni." + }, + "restrictCardTypeImportDesc": { + "message": "Egy vagy több szervezet által beállított szabályzat megakadályozza a kártyák importálását a széfekbe." + }, "domainsTitle": { "message": "Tartomány", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "PIN beállítás feloldása" }, - "unlockBiometricSet": { - "message": "Biometriai beállítások feloldása" + "unlockWithBiometricSet": { + "message": "Feloldás biometrikusan" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 995540036d6..125cd7ceeab 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Sebuah kebijakan organisasi telah menghalangi mengimpor benda-benda ke brankas pribadi Anda." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domain", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "PIN untuk membuka telah diatur" }, - "unlockBiometricSet": { - "message": "Biometrik untuk membuka telah diatur" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Sedang memeriksa keaslian" diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 09b92304287..f1c2cd09ca2 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Una politica dell'organizzazione ti impedisce di importare elementi nella tua cassaforte individuale." }, + "restrictCardTypeImport": { + "message": "Impossibile importare elementi di tipo carta" + }, + "restrictCardTypeImportDesc": { + "message": "Non puoi importare carte nelle tue casseforti a causa di una politica impostata da una o più organizzazioni." + }, "domainsTitle": { "message": "Domini", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Sblocca PIN impostato" }, - "unlockBiometricSet": { - "message": "Sblocco biometrico" + "unlockWithBiometricSet": { + "message": "Sblocca con i dati biometrici" }, "authenticating": { "message": "Autenticazione" diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 9b96507cb2c..fd256961ba6 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "組織のポリシーにより、個々の保管庫へのアイテムのインポートがブロックされました。" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "ドメイン", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "認証中" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 00fcf43aa67..68a6a877e50 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "დომენები", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "ავთენტიკაცია" diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index da9a4637444..987a2ce79cb 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index a49ca045dd4..41c01431ac2 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "조직 정책으로 인해 개별 보관함으로 항목을 가져오는 것이 차단되었습니다." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "도메인", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "인증 중" diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 6b672696c2f..3c2751d5fbe 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Organizacijos politika blokavo elementų importavimą į Jūsų individualią saugyklą." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domenai", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index c0690140cc2..7108fe15a93 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Apvienības nosacījums neļauj ievietot ārējos vienumus savā personīgajā glabātavā." }, + "restrictCardTypeImport": { + "message": "Nevar ievietot karšu vienumu veidus" + }, + "restrictCardTypeImportDesc": { + "message": "Pamatnostādne, ko ir iestatījusi viena vai vairākas apvienības, liedz karšu ievietošanu savās glabātavās." + }, "domainsTitle": { "message": "Domēna vārdi", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Atslēgšanas PIN iestatīts" }, - "unlockBiometricSet": { - "message": "Atslēgt biometrijas kopu" + "unlockWithBiometricSet": { + "message": "Atslēgt ar biometriju" }, "authenticating": { "message": "Autentificē" diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 3bbb7e28da6..7708ea4b940 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index fdbd6a9895a..36fdc74f521 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 0ec9268c915..747e37aadd6 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "En organisasjonsretningslinje har blokkert import av gjenstander til ditt individuelle hvelv." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domener", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Autentiserer" diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index a4e49771077..af63059ebd3 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Organisatiebeleid heeft het importeren van items in je persoonlijke kluis geblokkeerd." }, + "restrictCardTypeImport": { + "message": "Kan kaart item types niet importeren" + }, + "restrictCardTypeImportDesc": { + "message": "Een beleid ingesteld door 1 of meer organisaties voorkomt dat je kaarten naar je kluizen kunt importeren." + }, "domainsTitle": { "message": "Domeinen", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "PIN-code ontgrendelen instellen" }, - "unlockBiometricSet": { - "message": "Biometrische set ontgrendelen" + "unlockWithBiometricSet": { + "message": "Met biometrische set ontgrendelen" }, "authenticating": { "message": "Aan het inloggen" diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index de6dfa2d7ff..9bb4d992b37 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1923,7 +1923,7 @@ "message": "Klucz SSH" }, "typeNote": { - "message": "Note" + "message": "Notatka" }, "newItemHeader": { "message": "Nowy $TYPE$", @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Polityka organizacji zablokowała importowanie elementów do Twojego sejfu." }, + "restrictCardTypeImport": { + "message": "Nie można importować elementów typu karty" + }, + "restrictCardTypeImportDesc": { + "message": "Polityka ustawiona przez 1 lub więcej organizacji uniemożliwia importowanie kart do sejfów." + }, "domainsTitle": { "message": "Domeny", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Ustaw kod PIN odblokowujący" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Odblokuj za pomocą danych biometrycznych" }, "authenticating": { "message": "Uwierzytelnianie" @@ -5411,7 +5417,7 @@ "message": "Nie masz uprawnień do przeglądania tej strony. Spróbuj zalogować się na inne konto." }, "wasmNotSupported": { - "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.", + "message": "Zestaw WebAssembly nie jest obsługiwany w przeglądarce lub nie jest włączony. Do korzystania z aplikacji Bitwarden wymagany jest zestaw WebAssembre.", "description": "'WebAssembly' is a technical term and should not be translated." } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 542c2be0a0b..4405d1c59df 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "A política da organização bloqueou a importação de itens para o seu cofre." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domínios", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Autenticando" diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 103dc0351da..ba791785b0e 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Uma política da organização bloqueou a importação de itens para o seu cofre individual." }, + "restrictCardTypeImport": { + "message": "Não é possível importar tipos de itens de cartão" + }, + "restrictCardTypeImportDesc": { + "message": "Uma política definida por 1 ou mais organizações impede-o de importar cartões para os seus cofres." + }, "domainsTitle": { "message": "Domínios", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Definição do PIN de desbloqueio" }, - "unlockBiometricSet": { - "message": "Desbloquear conjunto de biometria" + "unlockWithBiometricSet": { + "message": "Desbloquear com conjunto biométrico" }, "authenticating": { "message": "A autenticar" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 5a21d0886a4..b27a1cbc519 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 130ded8ef0a..b9ffa0afbdc 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Импорт элементов в ваше личное хранилище отключен политикой организации." }, + "restrictCardTypeImport": { + "message": "Невозможно импортировать элементы карт" + }, + "restrictCardTypeImportDesc": { + "message": "Политика, установленная 1 или более организациями, не позволяет импортировать карты в ваши хранилища." + }, "domainsTitle": { "message": "Домены", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Установить PIN--код разблокировки" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Разблокировать с помощью биометрии" }, "authenticating": { "message": "Аутентификация" diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 70019afd17c..9f60625ce4c 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 600ab1817b8..8a10ad901e8 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Zásady organizácie zablokovali importovanie položiek do vášho osobného trezoru." }, + "restrictCardTypeImport": { + "message": "Položky typu karta sa nedajú importovať" + }, + "restrictCardTypeImportDesc": { + "message": "Politika nastavená 1 alebo viacerými organizáciami vám bráni v importovaní kariet do vašich trezorov." + }, "domainsTitle": { "message": "Domény", "description": "A category title describing the concept of web domains" @@ -5065,7 +5071,7 @@ "unlockPinSet": { "message": "PIN na odomknutie nastavený" }, - "unlockBiometricSet": { + "unlockWithBiometricSet": { "message": "Odomknutie biometrickými údajmi nastavené" }, "authenticating": { diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 25bbabfc0c6..2d73e99023c 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 0479276441b..870338e3563 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1923,7 +1923,7 @@ "message": "SSH кључ" }, "typeNote": { - "message": "Note" + "message": "Белешка" }, "newItemHeader": { "message": "Нови $TYPE$", @@ -2157,7 +2157,7 @@ "message": "Поставите свој ПИН код за откључавање Bitwarden-а. Поставке ПИН-а ће се ресетовати ако се икада потпуно одјавите из апликације." }, "setPinCode": { - "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application." + "message": "Можете употребити овај ПИН да би деблокирали Bitwarden. Ваш ПИН ће се ресетовати ако се икада у потпуности одјавите из апликације." }, "pinRequired": { "message": "ПИН је обавезан." @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Политика организације је блокирала увоз ставки у ваш појединачни сеф." }, + "restrictCardTypeImport": { + "message": "Не могу увозити врсте картица" + }, + "restrictCardTypeImportDesc": { + "message": "Политика која је поставила 1 или више организација спречава вас да се увозе картице у сефу." + }, "domainsTitle": { "message": "Домени", "description": "A category title describing the concept of web domains" @@ -2519,7 +2525,7 @@ "message": "Промени" }, "changePassword": { - "message": "Change password", + "message": "Промени лозинку", "description": "Change password button for browser at risk notification on login." }, "changeButtonTitle": { @@ -2532,7 +2538,7 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "Лозинка под ризиком" }, "atRiskPasswords": { "message": "Лозинке под ризиком" @@ -2569,7 +2575,7 @@ } }, "atRiskChangePrompt": { - "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.", + "message": "Ваша лозинка за ову страницу је ризична. $ORGANIZATION$ је затражио да је промените.", "placeholders": { "organization": { "content": "$1", @@ -2579,7 +2585,7 @@ "description": "Notification body when a login triggers an at-risk password change request and the change password domain is known." }, "atRiskNavigatePrompt": { - "message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.", + "message": "$ORGANIZATION$ жели да промените ову лозинку јер је ризична. Идите до поставки вашег налога да бисте променили лозинку.", "placeholders": { "organization": { "content": "$1", @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Постављен ПИН деблокирања" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Откључај биометријом" }, "authenticating": { "message": "Аутентификација" @@ -5411,7 +5417,7 @@ "message": "Немате дозволе за преглед ове странице. Покушајте да се пријавите са другим налогом." }, "wasmNotSupported": { - "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.", + "message": "WebAssembly није подржано или није уапљено на вашем прегледачу. WebAssembly је потребно да би се користила апликација Bitwarden.", "description": "'WebAssembly' is a technical term and should not be translated." } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 5af58bc3419..c4b72cc5ea1 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domäner", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 2d29efcc89e..b6a8d1834b4 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 7d291b911be..f7895c8866d 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "An organization policy has blocked importing items into your individual vault." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Domains", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index abde20c43e0..aae4fdd2486 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Bir kuruluş ilkesi, kayıtları kişisel kasanıza içe aktarmayı engelledi." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Alan adları", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Kimlik doğrulanıyor" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 8f9e8e21f36..ab4fe87a2be 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Політика організації заблокувала імпортування записів до вашого особистого сховища." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Домени", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Розблокування PIN-кодом встановлено" }, - "unlockBiometricSet": { - "message": "Біометричне розблокування налаштовано" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Аутентифікація" diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index b27e419eb30..d02658f9e26 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "Chính sách của tổ chức đã chặn việc nhập các mục vào kho cá nhân của bạn." }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "Các tên miền", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "Authenticating" diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index de945e94110..fe70f8abe57 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -300,7 +300,7 @@ "message": "前往帮助中心吗?" }, "continueToHelpCenterDesc": { - "message": "在帮助中心进一步了解如何使用 Bitwarden。" + "message": "访问帮助中心进一步了解如何使用 Bitwarden。" }, "continueToBrowserExtensionStore": { "message": "前往浏览器扩展商店吗?" @@ -2160,7 +2160,7 @@ "message": "您可以使用此 PIN 码解锁 Bitwarden。您的 PIN 码将在您完全注销此应用程序时被重置。" }, "pinRequired": { - "message": "需要 PIN 码。" + "message": "必须填写 PIN 码。" }, "invalidPin": { "message": "无效 PIN 码。" @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "某个组织策略已阻止将项目导入您的个人密码库。" }, + "restrictCardTypeImport": { + "message": "无法导入支付卡项目类型" + }, + "restrictCardTypeImportDesc": { + "message": "由 1 个或多个组织设置的策略阻止您将支付卡导入密码库。" + }, "domainsTitle": { "message": "域名", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "解锁 PIN 设置" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "生物识别解锁设置" }, "authenticating": { "message": "正在验证" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 1614cf3c9ff..e50117419b2 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -2487,6 +2487,12 @@ "personalOwnershipPolicyInEffectImports": { "message": "某個組織原則已禁止您將項目匯入至您的個人密碼庫。" }, + "restrictCardTypeImport": { + "message": "Cannot import card item types" + }, + "restrictCardTypeImportDesc": { + "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + }, "domainsTitle": { "message": "網域", "description": "A category title describing the concept of web domains" @@ -5065,8 +5071,8 @@ "unlockPinSet": { "message": "Unlock PIN set" }, - "unlockBiometricSet": { - "message": "Unlock biometrics set" + "unlockWithBiometricSet": { + "message": "Unlock with biometrics set" }, "authenticating": { "message": "驗證中" From bfb71a3405eeefa483ae5fd3f07813f9e312ac48 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Fri, 27 Jun 2025 09:59:38 -0400 Subject: [PATCH 27/57] [PM-22996] Failed to decrypt ciphers: TypeError: this.uriChecksum is null (#15355) --- libs/common/src/vault/models/domain/login-uri.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/src/vault/models/domain/login-uri.ts b/libs/common/src/vault/models/domain/login-uri.ts index b3e6fad70dd..5874d99c99d 100644 --- a/libs/common/src/vault/models/domain/login-uri.ts +++ b/libs/common/src/vault/models/domain/login-uri.ts @@ -97,8 +97,8 @@ export class LoginUri extends Domain { */ toSdkLoginUri(): SdkLoginUri { return { - uri: this.uri.toJSON(), - uriChecksum: this.uriChecksum.toJSON(), + uri: this.uri?.toJSON(), + uriChecksum: this.uriChecksum?.toJSON(), match: this.match, }; } From f7ca5b78189d17253ae4446d338fbfc2873e7339 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Fri, 27 Jun 2025 10:28:35 -0400 Subject: [PATCH 28/57] Small Typo & Lint Fix (#15313) * Small typo and lint fix * Removes extra line --- apps/desktop/src/platform/services/desktop-settings.service.ts | 2 +- .../components/access-selector/access-selector.component.html | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/desktop/src/platform/services/desktop-settings.service.ts b/apps/desktop/src/platform/services/desktop-settings.service.ts index 37a1fe73829..e8f311e56f6 100644 --- a/apps/desktop/src/platform/services/desktop-settings.service.ts +++ b/apps/desktop/src/platform/services/desktop-settings.service.ts @@ -108,7 +108,7 @@ export class DesktopSettingsService { private readonly closeToTrayState = this.stateProvider.getGlobal(CLOSE_TO_TRAY_KEY); /** - * Tha applications setting for whether or not to close the application into the system tray. + * The applications setting for whether or not to close the application into the system tray. */ closeToTray$ = this.closeToTrayState.state$.pipe(map(Boolean)); diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html index 088b5051fb1..e9b7ba39aa5 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.html @@ -1,5 +1,3 @@ - -
{{ "permission" | i18n }} From 62750a06ec2739033e0a4f597afdd99af80945ea Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 27 Jun 2025 08:16:59 -0700 Subject: [PATCH 29/57] [PM-36] - [Tech Debt] Move SearchService to libs/common/vault (#15251) * wip - migrate search service to vault * fix import --- .../autofill/popup/fido2/fido2.component.ts | 2 +- .../browser/src/background/main.background.ts | 4 ++-- .../popup/send-v2/send-v2.component.spec.ts | 2 +- .../vault-header-v2.component.spec.ts | 2 +- .../vault-popup-items.service.spec.ts | 2 +- .../services/vault-popup-items.service.ts | 2 +- apps/cli/src/commands/get.command.ts | 2 +- apps/cli/src/commands/list.command.ts | 2 +- .../service-container/service-container.ts | 2 +- .../src/tools/send/commands/get.command.ts | 2 +- .../src/tools/send/commands/list.command.ts | 2 +- apps/desktop/src/app/app.component.ts | 2 +- .../src/app/tools/send/send.component.ts | 2 +- .../app/vault/vault-items-v2.component.ts | 2 +- .../vault/app/vault/vault-items.component.ts | 2 +- .../collections/vault.component.ts | 2 +- apps/web/src/app/app.component.ts | 2 +- apps/web/src/app/tools/send/send.component.ts | 2 +- .../vault/individual-vault/vault.component.ts | 2 +- .../src/services/jslib-services.module.ts | 4 ++-- libs/angular/src/tools/send/send.component.ts | 2 +- .../vault/components/vault-items.component.ts | 2 +- .../services/vault-timeout.service.spec.ts | 2 +- .../services/vault-timeout.service.ts | 2 +- .../abstractions/search.service.ts | 6 +++--- .../src/vault/services/cipher.service.spec.ts | 2 +- .../src/vault/services/cipher.service.ts | 2 +- .../{ => vault}/services/search.service.ts | 20 +++++++++---------- .../src/services/send-items.service.spec.ts | 2 +- .../src/services/send-items.service.ts | 2 +- 30 files changed, 43 insertions(+), 43 deletions(-) rename libs/common/src/{ => vault}/abstractions/search.service.ts (82%) rename libs/common/src/{ => vault}/services/search.service.ts (96%) diff --git a/apps/browser/src/autofill/popup/fido2/fido2.component.ts b/apps/browser/src/autofill/popup/fido2/fido2.component.ts index 3107b60f475..ac38fe2f894 100644 --- a/apps/browser/src/autofill/popup/fido2/fido2.component.ts +++ b/apps/browser/src/autofill/popup/fido2/fido2.component.ts @@ -18,13 +18,13 @@ import { } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { SecureNoteType, CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CardView } from "@bitwarden/common/vault/models/view/card.view"; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 2e4818a8b0c..2f423895f9f 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -22,7 +22,6 @@ import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstracti import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; -import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -174,7 +173,6 @@ import { ApiService } from "@bitwarden/common/services/api.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/services/search.service"; import { PasswordStrengthService, PasswordStrengthServiceAbstraction, @@ -190,6 +188,7 @@ import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vau import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -207,6 +206,7 @@ import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-u import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { SearchService } from "@bitwarden/common/vault/services/search.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service"; import { DefaultTaskService, TaskService } from "@bitwarden/common/vault/tasks"; diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts index 6fc4793f5c0..63ede7ba357 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts @@ -6,7 +6,6 @@ import { MockProxy, mock } from "jest-mock-extended"; import { of, BehaviorSubject } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -21,6 +20,7 @@ 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 { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { ButtonModule, NoItemsModule } from "@bitwarden/components"; import { NewSendDropdownComponent, diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts index cda055176e8..9564aeadc09 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts @@ -7,7 +7,6 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, Subject } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -19,6 +18,7 @@ import { StateProvider } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { PasswordRepromptService } from "@bitwarden/vault"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts index a573f99d3c1..63cd0d90d05 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts @@ -4,7 +4,6 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom, timeout } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -14,6 +13,7 @@ import { SyncService } from "@bitwarden/common/platform/sync"; import { ObservableTracker, mockAccountServiceWith } from "@bitwarden/common/spec"; import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index c1dd9b30c68..20bdbd2eefe 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -20,7 +20,6 @@ import { } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -28,6 +27,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 28a5680da77..3b1e0de4f15 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -6,7 +6,6 @@ import { CollectionService, CollectionView } from "@bitwarden/admin-console/comm import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -30,6 +29,7 @@ import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index 49ec7689b20..517050728c0 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -10,7 +10,6 @@ import { } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -19,6 +18,7 @@ import { ListResponse as ApiListResponse } from "@bitwarden/common/models/respon import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CollectionResponse } from "../admin-console/models/response/collection.response"; diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 099ce503fac..df019520250 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -132,7 +132,6 @@ import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal"; import { AuditService } from "@bitwarden/common/services/audit.service"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/services/search.service"; import { PasswordStrengthService, PasswordStrengthServiceAbstraction, @@ -153,6 +152,7 @@ import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-u import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { SearchService } from "@bitwarden/common/vault/services/search.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { legacyPasswordGenerationServiceFactory, diff --git a/apps/cli/src/tools/send/commands/get.command.ts b/apps/cli/src/tools/send/commands/get.command.ts index 1d651c50bf0..1b3a8f6c500 100644 --- a/apps/cli/src/tools/send/commands/get.command.ts +++ b/apps/cli/src/tools/send/commands/get.command.ts @@ -4,12 +4,12 @@ import { OptionValues } from "commander"; import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { DownloadCommand } from "../../../commands/download.command"; import { Response } from "../../../models/response"; diff --git a/apps/cli/src/tools/send/commands/list.command.ts b/apps/cli/src/tools/send/commands/list.command.ts index ab8a4dcb1c5..f611cb3f5dc 100644 --- a/apps/cli/src/tools/send/commands/list.command.ts +++ b/apps/cli/src/tools/send/commands/list.command.ts @@ -1,8 +1,8 @@ import { firstValueFrom } from "rxjs"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { Response } from "../../../models/response"; import { ListResponse } from "../../../models/response/list.response"; diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index dc1621210de..b5c34cc95a3 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -35,7 +35,6 @@ import { UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -67,6 +66,7 @@ import { SyncService } from "@bitwarden/common/platform/sync"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { DialogRef, DialogService, ToastOptions, ToastService } from "@bitwarden/components"; import { CredentialGeneratorHistoryDialogComponent } from "@bitwarden/generator-components"; diff --git a/apps/desktop/src/app/tools/send/send.component.ts b/apps/desktop/src/app/tools/send/send.component.ts index 3ca26780853..0146a5e62ea 100644 --- a/apps/desktop/src/app/tools/send/send.component.ts +++ b/apps/desktop/src/app/tools/send/send.component.ts @@ -6,7 +6,6 @@ import { FormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; @@ -17,6 +16,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 { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { invokeMenu, RendererMenuItem } from "../../../utils"; diff --git a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts index 1256c9e52e8..21b857b551a 100644 --- a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts @@ -6,9 +6,9 @@ import { distinctUntilChanged } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { VaultItemsComponent as BaseVaultItemsComponent } from "@bitwarden/angular/vault/components/vault-items.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { MenuModule } from "@bitwarden/components"; diff --git a/apps/desktop/src/vault/app/vault/vault-items.component.ts b/apps/desktop/src/vault/app/vault/vault-items.component.ts index 8bf4955343d..c37a29833d9 100644 --- a/apps/desktop/src/vault/app/vault/vault-items.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-items.component.ts @@ -4,9 +4,9 @@ import { Component } from "@angular/core"; import { distinctUntilChanged } from "rxjs"; import { VaultItemsComponent as BaseVaultItemsComponent } from "@bitwarden/angular/vault/components/vault-items.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index bc0f517d1fb..8ad0f6cf499 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -35,7 +35,6 @@ import { import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -55,6 +54,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index b9f3b8c05b7..ada73dd0059 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -9,7 +9,6 @@ import { CollectionService } from "@bitwarden/admin-console/common"; import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction"; import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -26,6 +25,7 @@ import { NotificationsService } from "@bitwarden/common/platform/notifications"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService, BiometricStateService } from "@bitwarden/key-management"; diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 3d42b3182f8..b74a3b80ee3 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -4,7 +4,6 @@ import { Component, NgZone, OnInit, OnDestroy } from "@angular/core"; import { lastValueFrom } from "rxjs"; import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; @@ -16,6 +15,7 @@ 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 { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { DialogRef, DialogService, diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 29ad0ead621..51d59b54369 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -36,7 +36,6 @@ import { import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { getOrganizationById, @@ -60,6 +59,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index bec32ac1157..780604f048d 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -59,7 +59,6 @@ import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstracti import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; -import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { InternalOrganizationServiceAbstraction, @@ -257,7 +256,6 @@ import { ApiService } from "@bitwarden/common/services/api.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; -import { SearchService } from "@bitwarden/common/services/search.service"; import { PasswordStrengthService, PasswordStrengthServiceAbstraction, @@ -279,6 +277,7 @@ import { FolderService as FolderServiceAbstraction, InternalFolderService, } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { @@ -295,6 +294,7 @@ import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-u import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { SearchService } from "@bitwarden/common/vault/services/search.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service"; import { DefaultTaskService, TaskService } from "@bitwarden/common/vault/tasks"; diff --git a/libs/angular/src/tools/send/send.component.ts b/libs/angular/src/tools/send/send.component.ts index 5dbf3686b7d..e96bdd8e31a 100644 --- a/libs/angular/src/tools/send/send.component.ts +++ b/libs/angular/src/tools/send/send.component.ts @@ -12,7 +12,6 @@ import { combineLatest, } from "rxjs"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -25,6 +24,7 @@ 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 { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { DialogService, ToastService } from "@bitwarden/components"; @Directive() diff --git a/libs/angular/src/vault/components/vault-items.component.ts b/libs/angular/src/vault/components/vault-items.component.ts index 424243fe118..cf017899774 100644 --- a/libs/angular/src/vault/components/vault-items.component.ts +++ b/libs/angular/src/vault/components/vault-items.component.ts @@ -15,11 +15,11 @@ import { takeUntil, } from "rxjs"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; diff --git a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts index 5ce7e37778d..9963e7d24f8 100644 --- a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts +++ b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts @@ -14,7 +14,6 @@ import { LogoutReason } from "@bitwarden/auth/common"; import { BiometricsService } from "@bitwarden/key-management"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec"; -import { SearchService } from "../../../abstractions/search.service"; import { AccountInfo } from "../../../auth/abstractions/account.service"; import { AuthService } from "../../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; @@ -28,6 +27,7 @@ import { StateEventRunnerService } from "../../../platform/state"; import { UserId } from "../../../types/guid"; import { CipherService } from "../../../vault/abstractions/cipher.service"; import { FolderService } from "../../../vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "../../../vault/abstractions/search.service"; import { FakeMasterPasswordService } from "../../master-password/services/fake-master-password.service"; import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum"; import { VaultTimeout, VaultTimeoutStringType } from "../types/vault-timeout.type"; diff --git a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts index 04769567db2..b5ee6a1fc0f 100644 --- a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts +++ b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts @@ -12,7 +12,6 @@ import { LogoutReason } from "@bitwarden/auth/common"; // eslint-disable-next-line no-restricted-imports import { BiometricsService } from "@bitwarden/key-management"; -import { SearchService } from "../../../abstractions/search.service"; import { AccountService } from "../../../auth/abstractions/account.service"; import { AuthService } from "../../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; @@ -25,6 +24,7 @@ import { StateEventRunnerService } from "../../../platform/state"; import { UserId } from "../../../types/guid"; import { CipherService } from "../../../vault/abstractions/cipher.service"; import { FolderService } from "../../../vault/abstractions/folder/folder.service.abstraction"; +import { SearchService } from "../../../vault/abstractions/search.service"; import { InternalMasterPasswordServiceAbstraction } from "../../master-password/abstractions/master-password.service.abstraction"; import { VaultTimeoutSettingsService } from "../abstractions/vault-timeout-settings.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../abstractions/vault-timeout.service"; diff --git a/libs/common/src/abstractions/search.service.ts b/libs/common/src/vault/abstractions/search.service.ts similarity index 82% rename from libs/common/src/abstractions/search.service.ts rename to libs/common/src/vault/abstractions/search.service.ts index 2bff33bf2db..c981aa748a4 100644 --- a/libs/common/src/abstractions/search.service.ts +++ b/libs/common/src/vault/abstractions/search.service.ts @@ -2,9 +2,9 @@ // @ts-strict-ignore import { Observable } from "rxjs"; -import { SendView } from "../tools/send/models/view/send.view"; -import { IndexedEntityId, UserId } from "../types/guid"; -import { CipherView } from "../vault/models/view/cipher.view"; +import { SendView } from "../../tools/send/models/view/send.view"; +import { IndexedEntityId, UserId } from "../../types/guid"; +import { CipherView } from "../models/view/cipher.view"; export abstract class SearchService { indexedEntityId$: (userId: UserId) => Observable; diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 1a0b1568775..2fd9b03a37e 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -10,7 +10,6 @@ import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-a import { FakeStateProvider } from "../../../spec/fake-state-provider"; import { makeStaticByteArray, makeSymmetricCryptoKey } from "../../../spec/utils"; import { ApiService } from "../../abstractions/api.service"; -import { SearchService } from "../../abstractions/search.service"; import { AutofillSettingsService } from "../../autofill/services/autofill-settings.service"; import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; import { BulkEncryptService } from "../../key-management/crypto/abstractions/bulk-encrypt.service"; @@ -29,6 +28,7 @@ import { CipherKey, OrgKey, UserKey } from "../../types/key"; import { CipherEncryptionService } from "../abstractions/cipher-encryption.service"; import { EncryptionContext } from "../abstractions/cipher.service"; import { CipherFileUploadService } from "../abstractions/file-upload/cipher-file-upload.service"; +import { SearchService } from "../abstractions/search.service"; import { FieldType } from "../enums"; import { CipherRepromptType } from "../enums/cipher-reprompt-type"; import { CipherType } from "../enums/cipher-type"; diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index d8d180a7c3e..b4f79b2467e 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -9,7 +9,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { KeyService } from "@bitwarden/key-management"; import { ApiService } from "../../abstractions/api.service"; -import { SearchService } from "../../abstractions/search.service"; import { AccountService } from "../../auth/abstractions/account.service"; import { AutofillSettingsServiceAbstraction } from "../../autofill/services/autofill-settings.service"; import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; @@ -38,6 +37,7 @@ import { EncryptionContext, } from "../abstractions/cipher.service"; import { CipherFileUploadService } from "../abstractions/file-upload/cipher-file-upload.service"; +import { SearchService } from "../abstractions/search.service"; import { FieldType } from "../enums"; import { CipherType } from "../enums/cipher-type"; import { CipherData } from "../models/data/cipher.data"; diff --git a/libs/common/src/services/search.service.ts b/libs/common/src/vault/services/search.service.ts similarity index 96% rename from libs/common/src/services/search.service.ts rename to libs/common/src/vault/services/search.service.ts index 3e6a070195a..4b7a26b6a31 100644 --- a/libs/common/src/services/search.service.ts +++ b/libs/common/src/vault/services/search.service.ts @@ -4,21 +4,21 @@ import * as lunr from "lunr"; import { Observable, firstValueFrom, map } from "rxjs"; import { Jsonify } from "type-fest"; -import { SearchService as SearchServiceAbstraction } from "../abstractions/search.service"; -import { UriMatchStrategy } from "../models/domain/domain-service"; -import { I18nService } from "../platform/abstractions/i18n.service"; -import { LogService } from "../platform/abstractions/log.service"; +import { UriMatchStrategy } from "../../models/domain/domain-service"; +import { I18nService } from "../../platform/abstractions/i18n.service"; +import { LogService } from "../../platform/abstractions/log.service"; import { SingleUserState, StateProvider, UserKeyDefinition, VAULT_SEARCH_MEMORY, -} from "../platform/state"; -import { SendView } from "../tools/send/models/view/send.view"; -import { IndexedEntityId, UserId } from "../types/guid"; -import { FieldType } from "../vault/enums"; -import { CipherType } from "../vault/enums/cipher-type"; -import { CipherView } from "../vault/models/view/cipher.view"; +} from "../../platform/state"; +import { SendView } from "../../tools/send/models/view/send.view"; +import { IndexedEntityId, UserId } from "../../types/guid"; +import { SearchService as SearchServiceAbstraction } from "../abstractions/search.service"; +import { FieldType } from "../enums"; +import { CipherType } from "../enums/cipher-type"; +import { CipherView } from "../models/view/cipher.view"; export type SerializedLunrIndex = { version: string; diff --git a/libs/tools/send/send-ui/src/services/send-items.service.spec.ts b/libs/tools/send/send-ui/src/services/send-items.service.spec.ts index 77e3725e813..cf46c909da5 100644 --- a/libs/tools/send/send-ui/src/services/send-items.service.spec.ts +++ b/libs/tools/send/send-ui/src/services/send-items.service.spec.ts @@ -2,11 +2,11 @@ import { TestBed } from "@angular/core/testing"; import { mock } from "jest-mock-extended"; import { BehaviorSubject, first, Subject } from "rxjs"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { UserId } from "@bitwarden/common/types/guid"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { mockAccountServiceWith } from "../../../../../common/spec"; diff --git a/libs/tools/send/send-ui/src/services/send-items.service.ts b/libs/tools/send/send-ui/src/services/send-items.service.ts index 1ade6f37f71..52e1e3d669e 100644 --- a/libs/tools/send/send-ui/src/services/send-items.service.ts +++ b/libs/tools/send/send-ui/src/services/send-items.service.ts @@ -14,11 +14,11 @@ import { tap, } from "rxjs"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { SendListFiltersService } from "./send-list-filters.service"; From 652f673a3c14f1c5ba451e0afed3bf2c29cb019f Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Fri, 27 Jun 2025 11:49:58 -0400 Subject: [PATCH 30/57] [PM-23086] fix My Items collection name (#15366) * fix My Items collection name * clean up --- .../services/organization-user/organization-user.service.ts | 2 +- apps/web/src/locales/en/messages.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts index 31dfa865005..79efeebca2a 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user/organization-user.service.ts @@ -43,7 +43,7 @@ export class OrganizationUserService { ): Observable { const encryptedCollectionName$ = this.orgKey$(organization).pipe( switchMap((orgKey) => - this.encryptService.encryptString(this.i18nService.t("My Itmes"), orgKey), + this.encryptService.encryptString(this.i18nService.t("myItems"), orgKey), ), ); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 79197f1eb06..5c9b02e5287 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -3276,6 +3276,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, From 4e3d83147e3e4dbdaa5b78a44d47e95426bf4c60 Mon Sep 17 00:00:00 2001 From: Ketan Mehta <45426198+ketanMehtaa@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:26:14 +0530 Subject: [PATCH 31/57] fixed ui name collection overlap (#15241) Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- .../components/vault-items/vault-items.component.html | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index 992c9c26bf3..ef928903a72 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -22,16 +22,12 @@ bitCell bitSortable="name" [fn]="sortByName" - [class]="showExtraColumn ? 'lg:tw-w-3/5' : 'tw-w-full'" + [class]="showExtraColumn ? 'tw-w-3/5' : 'tw-w-full'" > {{ "name" | i18n }} - + {{ "name" | i18n }} From cb36b96855bb8ad05e9fc7837224336086e5ed1a Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:55:20 -0500 Subject: [PATCH 32/57] [PM-22178] Add `WebBrowserInteractionService` (#15261) * add `WebBrowserInteractionService` and check for the extension observable * update checkForExtension to use observables rather than window timeouts * add open extension to WebBrowserInteractionService * add at-risk-passwords to `PopupPageUrls` * refactor `PopupPageUrls` to `ExtensionPageUrls` * add test for passing a page * refactor `Default` to `Index` * clean up complete/next issue using `race` * refactor page to url * continue listening for messages from the extension after subscribed * mark risk passwords a deprecated * remove takeUntilDestroyed * add back `takeUntilDestroyed` for internal `messages` * removed null filter - unneeded * add tap to send message for extension installation * add check for accepted urls to prevent any bad actors from opening the extension --- .../abstractions/content-message-handler.ts | 3 + .../content/content-message-handler.ts | 8 ++ .../browser/src/background/main.background.ts | 35 +++++- .../src/background/runtime.background.ts | 4 + .../web-browser-interaction.service.spec.ts | 111 ++++++++++++++++++ .../web-browser-interaction.service.ts | 76 ++++++++++++ .../vault/enums/extension-page-urls.enum.ts | 12 ++ libs/common/src/vault/enums/index.ts | 1 + .../src/vault/enums/vault-messages.enum.ts | 2 + 9 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/app/vault/services/web-browser-interaction.service.spec.ts create mode 100644 apps/web/src/app/vault/services/web-browser-interaction.service.ts create mode 100644 libs/common/src/vault/enums/extension-page-urls.enum.ts diff --git a/apps/browser/src/autofill/content/abstractions/content-message-handler.ts b/apps/browser/src/autofill/content/abstractions/content-message-handler.ts index 8231bd688c9..f413ace9432 100644 --- a/apps/browser/src/autofill/content/abstractions/content-message-handler.ts +++ b/apps/browser/src/autofill/content/abstractions/content-message-handler.ts @@ -1,3 +1,5 @@ +import { ExtensionPageUrls } from "@bitwarden/common/vault/enums"; + type ContentMessageWindowData = { command: string; lastpass?: boolean; @@ -5,6 +7,7 @@ type ContentMessageWindowData = { state?: string; data?: string; remember?: boolean; + url?: ExtensionPageUrls; }; type ContentMessageWindowEventParams = { data: ContentMessageWindowData; diff --git a/apps/browser/src/autofill/content/content-message-handler.ts b/apps/browser/src/autofill/content/content-message-handler.ts index 60f093f8c10..c57b2d959f3 100644 --- a/apps/browser/src/autofill/content/content-message-handler.ts +++ b/apps/browser/src/autofill/content/content-message-handler.ts @@ -1,3 +1,4 @@ +import { ExtensionPageUrls } from "@bitwarden/common/vault/enums"; import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; import { @@ -18,6 +19,8 @@ const windowMessageHandlers: ContentMessageWindowEventHandlers = { duoResult: ({ data, referrer }: { data: any; referrer: string }) => handleDuoResultMessage(data, referrer), [VaultMessages.OpenAtRiskPasswords]: () => handleOpenAtRiskPasswordsMessage(), + [VaultMessages.OpenBrowserExtensionToUrl]: ({ data }) => + handleOpenBrowserExtensionToUrlMessage(data), }; /** @@ -73,10 +76,15 @@ function handleWebAuthnResultMessage(data: ContentMessageWindowData, referrer: s sendExtensionRuntimeMessage({ command, data: data.data, remember, referrer }); } +/** @deprecated use {@link handleOpenBrowserExtensionToUrlMessage} */ function handleOpenAtRiskPasswordsMessage() { sendExtensionRuntimeMessage({ command: VaultMessages.OpenAtRiskPasswords }); } +function handleOpenBrowserExtensionToUrlMessage({ url }: { url?: ExtensionPageUrls }) { + sendExtensionRuntimeMessage({ command: VaultMessages.OpenBrowserExtensionToUrl, url }); +} + /** * Handles the window message event. * diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 2f423895f9f..c6d68a9f047 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -191,6 +191,7 @@ import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitw import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/vault/abstractions/search.service"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; +import { ExtensionPageUrls } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DefaultEndUserNotificationService, @@ -1694,14 +1695,44 @@ export default class MainBackground { // Set route of the popup before attempting to open it. // If the vault is locked, this won't have an effect as the auth guards will // redirect the user to the login page. - await browserAction.setPopup({ popup: "popup/index.html#/at-risk-passwords" }); + await browserAction.setPopup({ popup: ExtensionPageUrls.AtRiskPasswords }); await this.openPopup(); } finally { // Reset the popup route to the default route so any subsequent // popup openings will not open to the at-risk-passwords page. await browserAction.setPopup({ - popup: "popup/index.html#/", + popup: ExtensionPageUrls.Index, + }); + } + } + + /** + * Opens the popup to the given page + * @default ExtensionPageUrls.Index + */ + async openTheExtensionToPage(url: ExtensionPageUrls = ExtensionPageUrls.Index) { + const isValidUrl = Object.values(ExtensionPageUrls).includes(url); + + // If a non-defined URL is provided, return early. + if (!isValidUrl) { + return; + } + + const browserAction = BrowserApi.getBrowserAction(); + + try { + // Set route of the popup before attempting to open it. + // If the vault is locked, this won't have an effect as the auth guards will + // redirect the user to the login page. + await browserAction.setPopup({ popup: url }); + + await this.openPopup(); + } finally { + // Reset the popup route to the default route so any subsequent + // popup openings will not open to the at-risk-passwords page. + await browserAction.setPopup({ + popup: ExtensionPageUrls.Index, }); } } diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index cca17730a22..54fb8326cfb 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -296,6 +296,10 @@ export default class RuntimeBackground { await this.main.openAtRisksPasswordsPage(); this.announcePopupOpen(); break; + case VaultMessages.OpenBrowserExtensionToUrl: + await this.main.openTheExtensionToPage(msg.url); + this.announcePopupOpen(); + break; case "bgUpdateContextMenu": case "editedCipher": case "addedCipher": diff --git a/apps/web/src/app/vault/services/web-browser-interaction.service.spec.ts b/apps/web/src/app/vault/services/web-browser-interaction.service.spec.ts new file mode 100644 index 00000000000..68a9ca6d099 --- /dev/null +++ b/apps/web/src/app/vault/services/web-browser-interaction.service.spec.ts @@ -0,0 +1,111 @@ +import { fakeAsync, TestBed, tick } from "@angular/core/testing"; + +import { ExtensionPageUrls } from "@bitwarden/common/vault/enums"; +import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; + +import { WebBrowserInteractionService } from "./web-browser-interaction.service"; + +describe("WebBrowserInteractionService", () => { + let service: WebBrowserInteractionService; + const postMessage = jest.fn(); + window.postMessage = postMessage; + + const dispatchEvent = (command: string) => { + window.dispatchEvent(new MessageEvent("message", { data: { command } })); + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [WebBrowserInteractionService], + }); + + postMessage.mockClear(); + + service = TestBed.inject(WebBrowserInteractionService); + }); + + describe("extensionInstalled$", () => { + it("posts a message to check for the extension", () => { + service.extensionInstalled$.subscribe(); + + expect(postMessage).toHaveBeenCalledWith({ + command: VaultMessages.checkBwInstalled, + }); + }); + + it("returns false after the timeout", fakeAsync(() => { + service.extensionInstalled$.subscribe((installed) => { + expect(installed).toBe(false); + }); + + tick(1500); + })); + + it("returns true when the extension is installed", (done) => { + service.extensionInstalled$.subscribe((installed) => { + expect(installed).toBe(true); + done(); + }); + + dispatchEvent(VaultMessages.HasBwInstalled); + }); + + it("continues to listen for extension state changes after the first response", fakeAsync(() => { + const results: boolean[] = []; + + service.extensionInstalled$.subscribe((installed) => { + results.push(installed); + }); + + // initial timeout, should emit false + tick(1500); + expect(results[0]).toBe(false); + + // then emit `HasBwInstalled` + dispatchEvent(VaultMessages.HasBwInstalled); + tick(); + expect(results[1]).toBe(true); + })); + }); + + describe("openExtension", () => { + it("posts a message to open the extension", fakeAsync(() => { + service.openExtension().catch(() => {}); + + expect(postMessage).toHaveBeenCalledWith({ + command: VaultMessages.OpenBrowserExtensionToUrl, + }); + + tick(1500); + })); + + it("posts a message with the passed page", fakeAsync(() => { + service.openExtension(ExtensionPageUrls.Index).catch(() => {}); + + expect(postMessage).toHaveBeenCalledWith({ + command: VaultMessages.OpenBrowserExtensionToUrl, + url: ExtensionPageUrls.Index, + }); + + tick(1500); + })); + + it("resolves when the extension opens", async () => { + const openExtensionPromise = service.openExtension().catch(() => { + fail(); + }); + + dispatchEvent(VaultMessages.PopupOpened); + + await openExtensionPromise; + }); + + it("rejects if the extension does not open within the timeout", fakeAsync(() => { + service.openExtension().catch((error) => { + expect(error).toBe("Failed to open the extension"); + }); + + tick(1500); + })); + }); +}); diff --git a/apps/web/src/app/vault/services/web-browser-interaction.service.ts b/apps/web/src/app/vault/services/web-browser-interaction.service.ts new file mode 100644 index 00000000000..46c566140e4 --- /dev/null +++ b/apps/web/src/app/vault/services/web-browser-interaction.service.ts @@ -0,0 +1,76 @@ +import { DestroyRef, inject, Injectable } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { concatWith, filter, fromEvent, map, Observable, race, take, tap, timer } from "rxjs"; + +import { ExtensionPageUrls } from "@bitwarden/common/vault/enums"; +import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; + +/** + * The amount of time in milliseconds to wait for a response from the browser extension. + * NOTE: This value isn't computed by any means, it is just a reasonable timeout for the extension to respond. + */ +const MESSAGE_RESPONSE_TIMEOUT_MS = 1500; + +@Injectable({ + providedIn: "root", +}) +export class WebBrowserInteractionService { + destroyRef = inject(DestroyRef); + + private messages$ = fromEvent(window, "message").pipe( + takeUntilDestroyed(this.destroyRef), + ); + + /** Emits the installation status of the extension. */ + extensionInstalled$ = this.checkForExtension().pipe( + concatWith( + this.messages$.pipe( + filter((event) => event.data.command === VaultMessages.HasBwInstalled), + map(() => true), + ), + ), + ); + + /** Attempts to open the extension, rejects if the extension is not installed or it fails to open. */ + openExtension = (url?: ExtensionPageUrls) => { + return new Promise((resolve, reject) => { + race( + this.messages$.pipe( + filter((event) => event.data.command === VaultMessages.PopupOpened), + map(() => true), + ), + timer(MESSAGE_RESPONSE_TIMEOUT_MS).pipe(map(() => false)), + ) + .pipe(take(1)) + .subscribe((didOpen) => { + if (!didOpen) { + return reject("Failed to open the extension"); + } + + resolve(); + }); + + window.postMessage({ command: VaultMessages.OpenBrowserExtensionToUrl, url }); + }); + }; + + /** Sends a message via the window object to check if the extension is installed */ + private checkForExtension(): Observable { + const checkForExtension$ = race( + this.messages$.pipe( + filter((event) => event.data.command === VaultMessages.HasBwInstalled), + map(() => true), + ), + timer(MESSAGE_RESPONSE_TIMEOUT_MS).pipe(map(() => false)), + ).pipe( + tap({ + subscribe: () => { + window.postMessage({ command: VaultMessages.checkBwInstalled }); + }, + }), + take(1), + ); + + return checkForExtension$; + } +} diff --git a/libs/common/src/vault/enums/extension-page-urls.enum.ts b/libs/common/src/vault/enums/extension-page-urls.enum.ts new file mode 100644 index 00000000000..95f9e0a21df --- /dev/null +++ b/libs/common/src/vault/enums/extension-page-urls.enum.ts @@ -0,0 +1,12 @@ +import { UnionOfValues } from "../types/union-of-values"; + +/** + * Available pages within the extension by their URL. + * Useful when opening a specific page within the popup. + */ +export const ExtensionPageUrls: Record = { + Index: "popup/index.html#/", + AtRiskPasswords: "popup/index.html#/at-risk-passwords", +} as const; + +export type ExtensionPageUrls = UnionOfValues; diff --git a/libs/common/src/vault/enums/index.ts b/libs/common/src/vault/enums/index.ts index d7d1d06d2b9..c996a14a81a 100644 --- a/libs/common/src/vault/enums/index.ts +++ b/libs/common/src/vault/enums/index.ts @@ -3,3 +3,4 @@ export * from "./cipher-type"; export * from "./field-type.enum"; export * from "./linked-id-type.enum"; export * from "./secure-note-type.enum"; +export * from "./extension-page-urls.enum"; diff --git a/libs/common/src/vault/enums/vault-messages.enum.ts b/libs/common/src/vault/enums/vault-messages.enum.ts index 73272564432..fe76cd72427 100644 --- a/libs/common/src/vault/enums/vault-messages.enum.ts +++ b/libs/common/src/vault/enums/vault-messages.enum.ts @@ -1,7 +1,9 @@ const VaultMessages = { HasBwInstalled: "hasBwInstalled", checkBwInstalled: "checkIfBWExtensionInstalled", + /** @deprecated use {@link OpenBrowserExtensionToUrl} */ OpenAtRiskPasswords: "openAtRiskPasswords", + OpenBrowserExtensionToUrl: "openBrowserExtensionToUrl", PopupOpened: "popupOpened", } as const; From 64f8073fdf443147b728454ea8beaddace27b8de Mon Sep 17 00:00:00 2001 From: Jared McCannon Date: Fri, 27 Jun 2025 14:13:21 -0400 Subject: [PATCH 33/57] Removed feature flag for OptimizeNestedTraverseTypescript and vnext methods related to it. (#15314) --- .../collections/utils/collection-utils.ts | 25 -------- .../collections/vault.component.ts | 19 ++---- .../vault/individual-vault/vault.component.ts | 13 +--- libs/common/src/enums/feature-flag.enum.ts | 2 - libs/common/src/vault/service-utils.spec.ts | 18 ------ libs/common/src/vault/service-utils.ts | 64 +------------------ 6 files changed, 9 insertions(+), 132 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts b/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts index f19c3f64530..95ae911bbf6 100644 --- a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts +++ b/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts @@ -37,31 +37,6 @@ export function getNestedCollectionTree( return nodes; } -export function getNestedCollectionTree_vNext( - collections: (CollectionView | CollectionAdminView)[], -): TreeNode[] { - if (!collections) { - return []; - } - - // Collections need to be cloned because ServiceUtils.nestedTraverse actively - // modifies the names of collections. - // These changes risk affecting collections store in StateService. - const clonedCollections = collections - .sort((a, b) => a.name.localeCompare(b.name)) - .map(cloneCollection); - - const nodes: TreeNode[] = []; - clonedCollections.forEach((collection) => { - const parts = - collection.name != null - ? collection.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) - : []; - ServiceUtils.nestedTraverse_vNext(nodes, 0, parts, collection, null, NestingDelimiter); - }); - return nodes; -} - export function getFlatCollectionTree( nodes: TreeNode[], ): CollectionAdminView[]; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index 8ad0f6cf499..5846209e4c6 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -125,11 +125,7 @@ import { BulkCollectionsDialogResult, } from "./bulk-collections-dialog"; import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component"; -import { - getNestedCollectionTree, - getFlatCollectionTree, - getNestedCollectionTree_vNext, -} from "./utils"; +import { getNestedCollectionTree, getFlatCollectionTree } from "./utils"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; import { VaultHeaderComponent } from "./vault-header/vault-header.component"; @@ -423,16 +419,9 @@ export class VaultComponent implements OnInit, OnDestroy { }), ); - const nestedCollections$ = combineLatest([ - allCollections$, - this.configService.getFeatureFlag$(FeatureFlag.OptimizeNestedTraverseTypescript), - ]).pipe( - map( - ([collections, shouldOptimize]) => - (shouldOptimize - ? getNestedCollectionTree_vNext(collections) - : getNestedCollectionTree(collections)) as TreeNode[], - ), + const nestedCollections$ = allCollections$.pipe( + map((collections) => getNestedCollectionTree(collections)), + shareReplay({ refCount: true, bufferSize: 1 }), ); const collections$ = combineLatest([ diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 51d59b54369..52c4bcef01b 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -48,7 +48,6 @@ import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; import { EventType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -85,7 +84,6 @@ import { import { getNestedCollectionTree, getFlatCollectionTree, - getNestedCollectionTree_vNext, } from "../../admin-console/organizations/collections"; import { CollectionDialogAction, @@ -331,15 +329,8 @@ export class VaultComponent implements OnInit, OnDestroy { const filter$ = this.routedVaultFilterService.filter$; const allCollections$ = this.collectionService.decryptedCollections$; - const nestedCollections$ = combineLatest([ - allCollections$, - this.configService.getFeatureFlag$(FeatureFlag.OptimizeNestedTraverseTypescript), - ]).pipe( - map(([collections, shouldOptimize]) => - shouldOptimize - ? getNestedCollectionTree_vNext(collections) - : getNestedCollectionTree(collections), - ), + const nestedCollections$ = allCollections$.pipe( + map((collections) => getNestedCollectionTree(collections)), ); this.searchText$ diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 8322dba03c6..55c96c2334c 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -12,7 +12,6 @@ import { ServerConfig } from "../platform/abstractions/config/server-config"; export enum FeatureFlag { /* Admin Console Team */ SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions", - OptimizeNestedTraverseTypescript = "pm-21695-optimize-nested-traverse-typescript", CreateDefaultLocation = "pm-19467-create-default-location", /* Auth */ @@ -77,7 +76,6 @@ const FALSE = false as boolean; export const DefaultFeatureFlagValue = { /* Admin Console Team */ [FeatureFlag.SeparateCustomRolePermissions]: FALSE, - [FeatureFlag.OptimizeNestedTraverseTypescript]: FALSE, [FeatureFlag.CreateDefaultLocation]: FALSE, /* Autofill */ diff --git a/libs/common/src/vault/service-utils.spec.ts b/libs/common/src/vault/service-utils.spec.ts index 619d3d72ee6..db414da76d7 100644 --- a/libs/common/src/vault/service-utils.spec.ts +++ b/libs/common/src/vault/service-utils.spec.ts @@ -36,24 +36,6 @@ describe("serviceUtils", () => { }); }); - describe("nestedTraverse_vNext", () => { - it("should traverse a tree and add a node at the correct position given a valid path", () => { - const nodeToBeAdded: FakeObject = { id: "1.2.1", name: "1.2.1" }; - const path = ["1", "1.2", "1.2.1"]; - - ServiceUtils.nestedTraverse_vNext(nodeTree, 0, path, nodeToBeAdded, null, "/"); - expect(nodeTree[0].children[1].children[0].node).toEqual(nodeToBeAdded); - }); - - it("should combine the path for missing nodes and use as the added node name given an invalid path", () => { - const nodeToBeAdded: FakeObject = { id: "blank", name: "blank" }; - const path = ["3", "3.1", "3.1.1"]; - - ServiceUtils.nestedTraverse_vNext(nodeTree, 0, path, nodeToBeAdded, null, "/"); - expect(nodeTree[2].children[0].node.name).toEqual("3.1/3.1.1"); - }); - }); - describe("getTreeNodeObject", () => { it("should return a matching node given a single tree branch and a valid id", () => { const id = "1.1.1"; diff --git a/libs/common/src/vault/service-utils.ts b/libs/common/src/vault/service-utils.ts index 9595434223f..0d863e6ad0b 100644 --- a/libs/common/src/vault/service-utils.ts +++ b/libs/common/src/vault/service-utils.ts @@ -4,64 +4,6 @@ import { ITreeNodeObject, TreeNode } from "./models/domain/tree-node"; export class ServiceUtils { - static nestedTraverse( - nodeTree: TreeNode[], - partIndex: number, - parts: string[], - obj: ITreeNodeObject, - parent: TreeNode | undefined, - delimiter: string, - ) { - if (parts.length <= partIndex) { - return; - } - - const end: boolean = partIndex === parts.length - 1; - const partName: string = parts[partIndex]; - - for (let i = 0; i < nodeTree.length; i++) { - if (nodeTree[i].node.name !== partName) { - continue; - } - if (end && nodeTree[i].node.id !== obj.id) { - // Another node exists with the same name as the node being added - nodeTree.push(new TreeNode(obj, parent, partName)); - return; - } - // Move down the tree to the next level - ServiceUtils.nestedTraverse( - nodeTree[i].children, - partIndex + 1, - parts, - obj, - nodeTree[i], - delimiter, - ); - return; - } - - // If there's no node here with the same name... - if (nodeTree.filter((n) => n.node.name === partName).length === 0) { - // And we're at the end of the path given, add the node - if (end) { - nodeTree.push(new TreeNode(obj, parent, partName)); - return; - } - // And we're not at the end of the path, combine the current name with the next name - // 1, *1.2, 1.2.1 becomes - // 1, *1.2/1.2.1 - const newPartName = partName + delimiter + parts[partIndex + 1]; - ServiceUtils.nestedTraverse( - nodeTree, - 0, - [newPartName, ...parts.slice(partIndex + 2)], - obj, - parent, - delimiter, - ); - } - } - /** * Recursively adds a node to nodeTree * @param {TreeNode[]} nodeTree - An array of TreeNodes that the node will be added to @@ -71,7 +13,7 @@ export class ServiceUtils { * @param {ITreeNodeObject} parent - The parent node of the `obj` node * @param {string} delimiter - The delimiter used to split the path string, will be used to combine the path for missing nodes */ - static nestedTraverse_vNext( + static nestedTraverse( nodeTree: TreeNode[], partIndex: number, parts: string[], @@ -104,7 +46,7 @@ export class ServiceUtils { // 1, *1.2, 1.2.1 becomes // 1, *1.2/1.2.1 const newPartName = partName + delimiter + parts[partIndex + 1]; - ServiceUtils.nestedTraverse_vNext( + ServiceUtils.nestedTraverse( nodeTree, 0, [newPartName, ...parts.slice(partIndex + 2)], @@ -114,7 +56,7 @@ export class ServiceUtils { ); } else { // There is a node here with the same name, descend into it - ServiceUtils.nestedTraverse_vNext( + ServiceUtils.nestedTraverse( matchingNodes[0].children, partIndex + 1, parts, From 7500fe32bbcfaf7d1155b626c0dadf1f61b53a83 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:19:13 -0700 Subject: [PATCH 34/57] fix(auth-guard): [PM-22822] fix infinite redirect loop (#15371) --- libs/angular/src/auth/guards/auth.guard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/angular/src/auth/guards/auth.guard.ts b/libs/angular/src/auth/guards/auth.guard.ts index a172c45d6f9..cf4f9dc2034 100644 --- a/libs/angular/src/auth/guards/auth.guard.ts +++ b/libs/angular/src/auth/guards/auth.guard.ts @@ -100,10 +100,10 @@ export const authGuard: CanActivateFn = async ( // Post- Account Recovery or Weak Password on login if ( - forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset || - (forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword && - !routerState.url.includes("update-temp-password") && - !routerState.url.includes("change-password")) + (forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset || + forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword) && + !routerState.url.includes("update-temp-password") && + !routerState.url.includes("change-password") ) { const route = isChangePasswordFlagOn ? "/change-password" : "/update-temp-password"; return router.createUrlTree([route]); From 780ce6a762e5d90a4282354e07222a712086d6c9 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Fri, 27 Jun 2025 14:45:39 -0400 Subject: [PATCH 35/57] Add comment to desktop-settings.service.ts based on direction from platform (#15373) --- .../src/platform/services/desktop-settings.service.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/desktop/src/platform/services/desktop-settings.service.ts b/apps/desktop/src/platform/services/desktop-settings.service.ts index e8f311e56f6..c11f10646d7 100644 --- a/apps/desktop/src/platform/services/desktop-settings.service.ts +++ b/apps/desktop/src/platform/services/desktop-settings.service.ts @@ -1,3 +1,13 @@ +/* + -- Note -- + + As of June 2025, settings should only be added here if they are owned + by the platform team. Other settings should be added to the relevant service + owned by the team that owns the setting. + + More info: https://bitwarden.atlassian.net/browse/PM-23126 +*/ + import { Observable, map } from "rxjs"; import { From 029713fe28f2825c8f85e300c7661f16d2c1c3ea Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:22:49 -0700 Subject: [PATCH 36/57] allow disabling hardware acceleration with env var (#14768) --- apps/desktop/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 7d97805e9be..4821b018148 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -394,7 +394,7 @@ export class Main { this.desktopSettingsService.hardwareAcceleration$, ); - if (!hardwareAcceleration) { + if (!hardwareAcceleration || process.env.ELECTRON_DISABLE_GPU) { this.logService.warning("Hardware acceleration is disabled"); app.disableHardwareAcceleration(); } else if (isMacAppStore()) { From 7a1bb81c5f562094d589d09267e589d397199a74 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 27 Jun 2025 15:49:49 -0400 Subject: [PATCH 37/57] [PM-21719] update desktop to address personal items who cant assign to collections (#15369) --- apps/desktop/src/vault/app/vault/vault-v2.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 849899bfe66..1248f32d1ac 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -482,7 +482,9 @@ export class VaultV2Component implements OnInit, OnDestroy, CopyClickListener { }); } - if (cipher.canAssignToCollections) { + const hasEditableCollections = this.allCollections.some((collection) => !collection.readOnly); + + if (cipher.canAssignToCollections && hasEditableCollections) { menu.push({ label: this.i18nService.t("assignToCollections"), click: () => From 700f54357c1212aac620916f6578e8838a08dfa6 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:04:51 -0500 Subject: [PATCH 38/57] [PM-20041] Marking Task as complete (#14980) * When saving a cipher, mark any associated security tasks as complete * fix test error from encryption refactor * hide security tasks that are associated with deleted ciphers (#15247) * account for deleted ciphers for atRiskPasswordDescriptions --- .../at-risk-password-callout.component.ts | 23 ++- .../at-risk-passwords.component.spec.ts | 27 +++ .../at-risk-passwords.component.ts | 17 +- .../default-cipher-form.service.spec.ts | 161 ++++++++++++++++++ .../services/default-cipher-form.service.ts | 51 +++++- 5 files changed, 268 insertions(+), 11 deletions(-) create mode 100644 libs/vault/src/cipher-form/services/default-cipher-form.service.spec.ts diff --git a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts index 18482706272..3c3270e557c 100644 --- a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts @@ -1,10 +1,11 @@ import { CommonModule } from "@angular/common"; import { Component, inject } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { map, switchMap } from "rxjs"; +import { combineLatest, map, switchMap } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks"; import { AnchorLinkDirective, CalloutModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; @@ -16,10 +17,26 @@ import { I18nPipe } from "@bitwarden/ui-common"; }) export class AtRiskPasswordCalloutComponent { private taskService = inject(TaskService); + private cipherService = inject(CipherService); private activeAccount$ = inject(AccountService).activeAccount$.pipe(getUserId); protected pendingTasks$ = this.activeAccount$.pipe( - switchMap((userId) => this.taskService.pendingTasks$(userId)), - map((tasks) => tasks.filter((t) => t.type === SecurityTaskType.UpdateAtRiskCredential)), + switchMap((userId) => + combineLatest([ + this.taskService.pendingTasks$(userId), + this.cipherService.cipherViews$(userId), + ]), + ), + map(([tasks, ciphers]) => + tasks.filter((t) => { + const associatedCipher = ciphers.find((c) => c.id === t.cipherId); + + return ( + t.type === SecurityTaskType.UpdateAtRiskCredential && + associatedCipher && + !associatedCipher.isDeleted + ); + }), + ), ); } diff --git a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts index dae00ba6c2b..eaa10aba624 100644 --- a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts +++ b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts @@ -203,6 +203,20 @@ describe("AtRiskPasswordsComponent", () => { expect(items).toHaveLength(1); expect(items[0].name).toBe("Item 1"); }); + + it("should not show tasks associated with deleted ciphers", async () => { + mockCiphers$.next([ + { + id: "cipher", + organizationId: "org", + name: "Item 1", + isDeleted: true, + } as CipherView, + ]); + + const items = await firstValueFrom(component["atRiskItems$"]); + expect(items).toHaveLength(0); + }); }); describe("pageDescription$", () => { @@ -245,6 +259,19 @@ describe("AtRiskPasswordsComponent", () => { type: SecurityTaskType.UpdateAtRiskCredential, } as SecurityTask, ]); + mockCiphers$.next([ + { + id: "cipher", + organizationId: "org", + name: "Item 1", + } as CipherView, + { + id: "cipher2", + organizationId: "org2", + name: "Item 2", + } as CipherView, + ]); + const description = await firstValueFrom(component["pageDescription$"]); expect(description).toBe("atRiskPasswordsDescMultiOrgPlural"); }); diff --git a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts index dc6712aa23f..1bfb65a15cc 100644 --- a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts @@ -155,32 +155,35 @@ export class AtRiskPasswordsComponent implements OnInit { (t) => t.type === SecurityTaskType.UpdateAtRiskCredential && t.cipherId != null && - ciphers[t.cipherId] != null, + ciphers[t.cipherId] != null && + !ciphers[t.cipherId].isDeleted, ) .map((t) => ciphers[t.cipherId!]), ), ); - protected pageDescription$ = this.activeUserData$.pipe( - switchMap(({ tasks, userId }) => { - const orgIds = new Set(tasks.map((t) => t.organizationId)); + protected pageDescription$ = combineLatest([this.activeUserData$, this.atRiskItems$]).pipe( + switchMap(([{ userId }, atRiskCiphers]) => { + const orgIds = new Set( + atRiskCiphers.filter((c) => c.organizationId).map((c) => c.organizationId), + ) as Set; if (orgIds.size === 1) { const [orgId] = orgIds; return this.organizationService.organizations$(userId).pipe( getOrganizationById(orgId), map((org) => this.i18nService.t( - tasks.length === 1 + atRiskCiphers.length === 1 ? "atRiskPasswordDescSingleOrg" : "atRiskPasswordsDescSingleOrgPlural", org?.name, - tasks.length, + atRiskCiphers.length, ), ), ); } - return of(this.i18nService.t("atRiskPasswordsDescMultiOrgPlural", tasks.length)); + return of(this.i18nService.t("atRiskPasswordsDescMultiOrgPlural", atRiskCiphers.length)); }), ); diff --git a/libs/vault/src/cipher-form/services/default-cipher-form.service.spec.ts b/libs/vault/src/cipher-form/services/default-cipher-form.service.spec.ts new file mode 100644 index 00000000000..3b2573b8f94 --- /dev/null +++ b/libs/vault/src/cipher-form/services/default-cipher-form.service.spec.ts @@ -0,0 +1,161 @@ +import { TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + CipherService, + EncryptionContext, +} from "@bitwarden/common/vault/abstractions/cipher.service"; +import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; +import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks"; +import { CipherType } from "@bitwarden/sdk-internal"; + +import { CipherFormConfig } from "../abstractions/cipher-form-config.service"; + +import { DefaultCipherFormService } from "./default-cipher-form.service"; + +describe("DefaultCipherFormService", () => { + let service: DefaultCipherFormService; + let testBed: TestBed; + const cipherServiceMock = mock(); + + let markAsCompleteMock: jest.Mock; + let pendingTasks$: jest.Mock; + + beforeEach(() => { + markAsCompleteMock = jest.fn().mockResolvedValue(undefined); + pendingTasks$ = jest.fn().mockReturnValue(of([])); + cipherServiceMock.encrypt.mockResolvedValue({} as EncryptionContext); + + testBed = TestBed.configureTestingModule({ + providers: [ + { provide: CipherService, useValue: cipherServiceMock }, + { provide: TaskService, useValue: { markAsComplete: markAsCompleteMock, pendingTasks$ } }, + { + provide: AccountService, + useValue: { activeAccount$: of({ id: "user-1" as UserId } as Account) }, + }, + DefaultCipherFormService, + ], + }); + + service = testBed.inject(DefaultCipherFormService); + }); + + describe("markAssociatedTaskAsComplete", () => { + it("does not call markAsComplete when the cipher is not a login", async () => { + pendingTasks$.mockReturnValueOnce( + of([ + { + type: SecurityTaskType.UpdateAtRiskCredential, + cipherId: "cipher-1", + userId: "user-1" as UserId, + }, + ]), + ); + + const cardCipher = new CipherView(); + cardCipher.type = CipherType.Card; + cardCipher.id = "cipher-1"; + + await service.saveCipher(cardCipher, { + originalCipher: new Cipher(), + admin: false, + } as CipherFormConfig); + + expect(markAsCompleteMock).not.toHaveBeenCalled(); + }); + + it("does not call markAsComplete when there is no associated credential tasks", async () => { + pendingTasks$.mockReturnValueOnce(of([])); + + const originalCipher = new Cipher(); + originalCipher.type = CipherType.Login; + + const cipher = new CipherView(); + cipher.type = CipherType.Login; + cipher.id = "cipher-1"; + cipher.login = new LoginView(); + cipher.login.password = "password123"; + + cipherServiceMock.decrypt.mockResolvedValue({ + ...cipher, + login: { + ...cipher.login, + password: "newPassword123", + }, + } as CipherView); + + await service.saveCipher(cipher, { + originalCipher: originalCipher, + admin: false, + } as CipherFormConfig); + + expect(markAsCompleteMock).not.toHaveBeenCalled(); + }); + + it("does not call markAsComplete when the password has not changed", async () => { + pendingTasks$.mockReturnValueOnce( + of([ + { + type: SecurityTaskType.UpdateAtRiskCredential, + cipherId: "cipher-1", + userId: "user-1" as UserId, + }, + ]), + ); + + const cipher = new CipherView(); + cipher.type = CipherType.Login; + cipher.id = "cipher-1"; + cipher.login = new LoginView(); + cipher.login.password = "password123"; + + cipherServiceMock.decrypt.mockResolvedValue(cipher); + + await service.saveCipher(cipher, { + originalCipher: new Cipher(), + admin: false, + } as CipherFormConfig); + + expect(markAsCompleteMock).not.toHaveBeenCalled(); + }); + + it("calls markAsComplete when the cipher password has changed and there is an associated credential task", async () => { + pendingTasks$.mockReturnValueOnce( + of([ + { + type: SecurityTaskType.UpdateAtRiskCredential, + cipherId: "cipher-1", + userId: "user-1" as UserId, + }, + ]), + ); + + const cipher = new CipherView(); + cipher.type = CipherType.Login; + cipher.id = "cipher-1"; + cipher.login = new LoginView(); + cipher.login.password = "password123"; + + cipherServiceMock.decrypt.mockResolvedValue({ + ...cipher, + login: { + ...cipher.login, + password: "newPassword123", + }, + } as CipherView); + + await service.saveCipher(cipher, { + originalCipher: new Cipher(), + admin: false, + } as CipherFormConfig); + + expect(markAsCompleteMock).toHaveBeenCalled(); + }); + }); +}); diff --git a/libs/vault/src/cipher-form/services/default-cipher-form.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form.service.ts index 99f853d4c86..5228c85c3f7 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form.service.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form.service.ts @@ -1,13 +1,16 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { inject, Injectable } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks"; import { CipherFormConfig } from "../abstractions/cipher-form-config.service"; import { CipherFormService } from "../abstractions/cipher-form.service"; @@ -20,6 +23,7 @@ function isSetEqual(a: Set, b: Set) { export class DefaultCipherFormService implements CipherFormService { private cipherService: CipherService = inject(CipherService); private accountService: AccountService = inject(AccountService); + private taskService: TaskService = inject(TaskService); async decryptCipher(cipher: Cipher): Promise { const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); @@ -89,6 +93,8 @@ export class DefaultCipherFormService implements CipherFormService { } } + await this.markAssociatedTaskAsComplete(activeUserId, cipher, config); + // Its possible the cipher was made no longer available due to collection assignment changes // e.g. The cipher was moved to a collection that the user no longer has access to if (savedCipher == null) { @@ -97,4 +103,47 @@ export class DefaultCipherFormService implements CipherFormService { return await this.cipherService.decrypt(savedCipher, activeUserId); } + + /** + * When a cipher has an associated pending `UpdateAtRiskCredential` task + * and the password has changed, mark the task as complete. + */ + private async markAssociatedTaskAsComplete( + userId: UserId, + updatedCipher: CipherView, + config: CipherFormConfig, + ) { + const decryptedOriginalCipherCipher = await this.cipherService.decrypt( + config.originalCipher, + userId, + ); + + const associatedPendingTask = await firstValueFrom( + this.taskService + .pendingTasks$(userId) + .pipe( + map((tasks) => + tasks.find( + (task) => + task.type === SecurityTaskType.UpdateAtRiskCredential && + task.cipherId === updatedCipher.id, + ), + ), + ), + ); + + const passwordHasChanged = + updatedCipher.type === CipherType.Login && + updatedCipher.login.password && + updatedCipher.login.password !== decryptedOriginalCipherCipher?.login?.password; + + // When there is not an associated pending task or the password has not changed, + // no action needed-return early. + if (!associatedPendingTask || !passwordHasChanged) { + return; + } + + // If the cipher is a login and the password has changed, mark the associated task as complete + await this.taskService.markAsComplete(associatedPendingTask.id, userId); + } } From 031c9bc947672847b6507ac5da0ed7c8009f735b Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 27 Jun 2025 15:24:12 -0700 Subject: [PATCH 39/57] fix(auth-guard): [PM-22822] remove SsoNewJitProvisionedUser case (#15376) --- libs/angular/src/auth/guards/auth.guard.spec.ts | 5 ----- libs/angular/src/auth/guards/auth.guard.ts | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/libs/angular/src/auth/guards/auth.guard.spec.ts b/libs/angular/src/auth/guards/auth.guard.spec.ts index f64d6cf769d..a2e1613c6c1 100644 --- a/libs/angular/src/auth/guards/auth.guard.spec.ts +++ b/libs/angular/src/auth/guards/auth.guard.spec.ts @@ -127,7 +127,6 @@ describe("AuthGuard", () => { describe("given user is Unlocked", () => { describe("given the PM16117_SetInitialPasswordRefactor feature flag is ON", () => { const tests = [ - ForceSetPasswordReason.SsoNewJitProvisionedUser, ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission, ForceSetPasswordReason.TdeOffboarding, ]; @@ -167,10 +166,6 @@ describe("AuthGuard", () => { describe("given the PM16117_SetInitialPasswordRefactor feature flag is OFF", () => { const tests = [ - { - reason: ForceSetPasswordReason.SsoNewJitProvisionedUser, - url: "/set-password-jit", - }, { reason: ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission, url: "/set-password", diff --git a/libs/angular/src/auth/guards/auth.guard.ts b/libs/angular/src/auth/guards/auth.guard.ts index cf4f9dc2034..f99a91fda34 100644 --- a/libs/angular/src/auth/guards/auth.guard.ts +++ b/libs/angular/src/auth/guards/auth.guard.ts @@ -67,16 +67,6 @@ export const authGuard: CanActivateFn = async ( FeatureFlag.PM16117_ChangeExistingPasswordRefactor, ); - // User JIT provisioned into a master-password-encryption org - if ( - forceSetPasswordReason === ForceSetPasswordReason.SsoNewJitProvisionedUser && - !routerState.url.includes("set-password-jit") && - !routerState.url.includes("set-initial-password") - ) { - const route = isSetInitialPasswordFlagOn ? "/set-initial-password" : "/set-password-jit"; - return router.createUrlTree([route]); - } - // TDE org user has "manage account recovery" permission if ( forceSetPasswordReason === From 7646f3e1e7edb54f8bd0dc8a3c6de12287b9df27 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:57:52 +0000 Subject: [PATCH 40/57] Autosync the updated translations (#15391) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/az/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 2 +- apps/desktop/src/locales/es/messages.json | 36 +++++++-------- apps/desktop/src/locales/ja/messages.json | 56 +++++++++++------------ apps/desktop/src/locales/pl/messages.json | 2 +- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 29022fc9789..e1b46ef3170 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1717,10 +1717,10 @@ "message": "Hesab məhdudlaşdırıldı" }, "restrictCardTypeImport": { - "message": "Cannot import card item types" + "message": "Kart element növləri daxilə köçürülə bilmir" }, "restrictCardTypeImportDesc": { - "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + "message": "1 və ya daha çox təşkilat tərəfindən təyin edilən bir siyasət, kartların seyfinizə köçürülməsini əngəlləyir." }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Fayl parolu\" və \"Fayl parolunu təsdiqlə\" uyuşmur." diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 2ce95c78d47..fda4e073bd0 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -3746,7 +3746,7 @@ "message": "Nom de la carpeta" }, "folderHintText": { - "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + "message": "Imbriqueu una carpeta afegint el nom de la carpeta principal seguit d'una \"/\". Exemple: Social/Fòrums" }, "sendsTitleNoItems": { "message": "Send sensitive information safely", diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index a14ea40f0b6..78b6502bb29 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -226,7 +226,7 @@ "message": "Introducir la contraseña" }, "sshAgentUnlockRequired": { - "message": "Please unlock your vault to approve the SSH key request." + "message": "Por favor, desbloquea tu caja fuerte para aprobar la solicitud de clave SSH." }, "sshAgentUnlockTimeout": { "message": "SSH key request timed out." @@ -1019,7 +1019,7 @@ "message": "URL del servidor" }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Tiempo de autenticación agotado" }, "authenticationSessionTimedOut": { "message": "The authentication session timed out. Please restart the login process." @@ -1215,7 +1215,7 @@ "message": "Timeout" }, "vaultTimeoutDesc": { - "message": "Elije cuando se agotará el tiempo de espera de tu caja fuerte y se ejecutará la acción seleccionada." + "message": "Elige cuando se agotará el tiempo de espera de tu caja fuerte y se ejecutará la acción seleccionada." }, "immediately": { "message": "Inmediatamente" @@ -1465,7 +1465,7 @@ "message": "Comprar Premium" }, "premiumPurchaseAlertV2": { - "message": "Puedes comprar el Premium desde la configuración de tu cuenta en la aplicación web de Bitwarden." + "message": "Puedes comprar el Premium desde los ajustes de tu cuenta en la aplicación web de Bitwarden." }, "premiumCurrentMember": { "message": "¡Eres un miembro Premium!" @@ -1588,7 +1588,7 @@ } }, "copySuccessful": { - "message": "Copy Successful" + "message": "Copia Exitosa" }, "errorRefreshingAccessToken": { "message": "Error de actualización del token de acceso" @@ -3176,7 +3176,7 @@ "message": "Falta el correo electrónico del usuario" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Correo electrónico del usuario activo no encontrado. Cerrando sesión." }, "deviceTrusted": { "message": "Dispositivo de confianza" @@ -3600,7 +3600,7 @@ "message": "Envío de texto" }, "ssoError": { - "message": "No free ports could be found for the sso login." + "message": "No se encontraron puertos libres para el inicio de sesión sso." }, "securePasswordGenerated": { "message": "¡Contraseña segura generada! No olvides actualizar tu contraseña en el sitio web." @@ -3614,19 +3614,19 @@ "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "biometricsStatusHelptextUnlockNeeded": { - "message": "Biometric unlock is unavailable because PIN or password unlock is required first." + "message": "El desbloqueo biométrico no está disponible porque primero es necesario desbloquear con PIN o contraseña." }, "biometricsStatusHelptextHardwareUnavailable": { - "message": "Biometric unlock is currently unavailable." + "message": "El desbloqueo biométrico no está disponible actualmente." }, "biometricsStatusHelptextAutoSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "El desbloqueo biométrico no está disponible por archivos del sistema mal configurados." }, "biometricsStatusHelptextManualSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "El desbloqueo biométrico no está disponible por archivos del sistema mal configurados." }, "biometricsStatusHelptextNotEnabledLocally": { - "message": "Biometric unlock is unavailable because it is not enabled for $EMAIL$ in the Bitwarden desktop app.", + "message": "El desbloqueo biométrico no está disponible porque no está habilitado para $EMAIL$ en la aplicación de escritorio Bitwarden.", "placeholders": { "email": { "content": "$1", @@ -3635,7 +3635,7 @@ } }, "biometricsStatusHelptextUnavailableReasonUnknown": { - "message": "Biometric unlock is currently unavailable for an unknown reason." + "message": "El desbloqueo biométrico no está disponible actualmente por una razón desconocida." }, "itemDetails": { "message": "Detalles del elemento" @@ -3644,7 +3644,7 @@ "message": "Nombre del elemento" }, "loginCredentials": { - "message": "Login credentials" + "message": "Credenciales de inicio de sesión" }, "additionalOptions": { "message": "Opciones adicionales" @@ -3774,7 +3774,7 @@ "description": "Aria label for the body content of the generator nudge" }, "newLoginNudgeTitle": { - "message": "Ahora tiempo con autocompletado" + "message": "Ahorra tiempo con el autocompletado" }, "newLoginNudgeBodyOne": { "message": "Incluír un", @@ -3856,7 +3856,7 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "Selecciona colecciones para asignar" }, "personalItemsTransferWarning": { "message": "$PERSONAL_ITEMS_COUNT$ serán transferidos permanentemente a la organización seleccionada. Ya no serás el propietario de estos elementos.", @@ -3893,10 +3893,10 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "Colecciones asignadas correctamente" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "No has seleccionado nada." }, "itemsMovedToOrg": { "message": "Elementos movidos a $ORGNAME$", diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index f4d2123c197..ca07f36eb9e 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -24,7 +24,7 @@ "message": "ID" }, "typeNote": { - "message": "Note" + "message": "メモ" }, "typeSecureNote": { "message": "セキュアメモ" @@ -241,22 +241,22 @@ "message": "SSH エージェントとは、Bitwarden 保管庫から直接 SSH リクエストに署名できる、開発者を対象としたサービスです。" }, "sshAgentPromptBehavior": { - "message": "Ask for authorization when using SSH agent" + "message": "SSHエージェントを使用する際に認証を要求する" }, "sshAgentPromptBehaviorDesc": { - "message": "Choose how to handle SSH-agent authorization requests." + "message": "SSHエージェント認可リクエストの処理方法を選択します。" }, "sshAgentPromptBehaviorHelp": { - "message": "Remember SSH authorizations" + "message": "SSH認可を記憶する" }, "sshAgentPromptBehaviorAlways": { - "message": "Always" + "message": "常に表示する" }, "sshAgentPromptBehaviorNever": { - "message": "Never" + "message": "表示しない" }, "sshAgentPromptBehaviorRememberUntilLock": { - "message": "Remember until vault is locked" + "message": "保管庫がロックされるまで記憶する" }, "premiumRequired": { "message": "プレミアム会員専用" @@ -409,16 +409,16 @@ "message": "認証キー (TOTP)" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "認証キー" }, "autofillOptions": { - "message": "Autofill options" + "message": "自動入力のオプション" }, "websiteUri": { - "message": "Website (URI)" + "message": "ウェブサイト (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "ウェブサイト (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -428,49 +428,49 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "ウェブサイトを追加しました" }, "addWebsite": { - "message": "Add website" + "message": "ウェブサイトを追加" }, "deleteWebsite": { - "message": "Delete website" + "message": "ウェブサイトを削除" }, "owner": { - "message": "Owner" + "message": "所有者" }, "addField": { - "message": "Add field" + "message": "フィールドを追加" }, "editField": { - "message": "Edit field" + "message": "フィールドを編集" }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "この添付ファイルを完全に削除してもよろしいですか?" }, "fieldType": { - "message": "Field type" + "message": "フィールドの種類" }, "fieldLabel": { - "message": "Field label" + "message": "フィールドのラベル" }, "add": { - "message": "Add" + "message": "追加" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "秘密の質問などのデータには、テキストフィールドを使用します" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "パスワードのような機密データには、非表示フィールドを使用します" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "「メールアドレスを記憶する」などのフォームのチェックボックスを自動入力する場合は、チェックボックスを使用します" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "特定のウェブサイトで自動入力の問題が発生している場合は、リンクされたフィールドを使用します。" }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "フィールドの HTML の id、name、aria-label、placeholder を入力します。" }, "folder": { "message": "フォルダー" @@ -498,7 +498,7 @@ "description": "This describes a field that is 'linked' (related) to another field." }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "チェックボックス" }, "linkedValue": { "message": "リンクされた値", @@ -695,7 +695,7 @@ "message": "最大ファイルサイズは500MBです。" }, "legacyEncryptionUnsupported": { - "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + "message": "従来の暗号化はサポートされていません。アカウントを復元するにはサポートにお問い合わせください。" }, "editedFolder": { "message": "フォルダーを編集しました" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 0d70760eff8..df90ab92af7 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -27,7 +27,7 @@ "message": "Note" }, "typeSecureNote": { - "message": "Bezpieczna notatka" + "message": "Notatka" }, "typeSshKey": { "message": "Klucz SSH" From f0a7592036a986c97f1e02af3b0506f66e240dfd Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:58:02 +0000 Subject: [PATCH 41/57] Autosync the updated translations (#15390) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/az/messages.json | 6 +- apps/browser/src/_locales/es/messages.json | 102 ++++++++++----------- apps/browser/src/_locales/pl/messages.json | 2 +- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index e189b3ba292..ddf82f37d2f 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -2488,10 +2488,10 @@ "message": "Bir təşkilat siyasəti, elementlərin fərdi seyfinizə köçürülməsini əngəllədi." }, "restrictCardTypeImport": { - "message": "Cannot import card item types" + "message": "Kart element növləri daxilə köçürülə bilmir" }, "restrictCardTypeImportDesc": { - "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + "message": "1 və ya daha çox təşkilat tərəfindən təyin edilən bir siyasət, kartların seyfinizə köçürülməsini əngəlləyir." }, "domainsTitle": { "message": "Domenlər", @@ -5072,7 +5072,7 @@ "message": "PIN ilə kilid açma təyini" }, "unlockWithBiometricSet": { - "message": "Unlock with biometrics set" + "message": "Kilidi biometriklə aç ayarı" }, "authenticating": { "message": "Kimlik doğrulama" diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 89d5b928f25..250aa3430e0 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1083,7 +1083,7 @@ "message": "Nueva notificación" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: Nueva notificación", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1121,7 +1121,7 @@ "description": "Button text for updating an existing login entry." }, "unlockToSave": { - "message": "Unlock to save this login", + "message": "Desbloquea para guardar este inicio de sesión", "description": "User prompt to take action in order to save the login they just entered." }, "saveLogin": { @@ -1141,7 +1141,7 @@ "description": "Message displayed when login details are successfully updated." }, "loginUpdateTaskSuccess": { - "message": "Great job! You took the steps to make you and $ORGANIZATION$ more secure.", + "message": "¡Buen trabajo! Has dado los pasos para que tú y $ORGANIZATION$ seáis más seguros.", "placeholders": { "organization": { "content": "$1" @@ -1150,7 +1150,7 @@ "description": "Shown to user after login is updated." }, "loginUpdateTaskSuccessAdditional": { - "message": "Thank you for making $ORGANIZATION$ more secure. You have $TASK_COUNT$ more passwords to update.", + "message": "Gracias por hacer $ORGANIZATION$ más seguro. Tienes $TASK_COUNT$ contraseñas más que actualizar.", "placeholders": { "organization": { "content": "$1" @@ -1162,7 +1162,7 @@ "description": "Shown to user after login is updated." }, "nextSecurityTaskAction": { - "message": "Change next password", + "message": "Cambiar siguiente contraseña", "description": "Message prompting user to undertake completion of another security task." }, "saveFailure": { @@ -1170,7 +1170,7 @@ "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { - "message": "Oh no! We couldn't save this. Try entering the details manually.", + "message": "¡Oh no! No pudimos guardar esto. Intenta introducir los datos manualmente.", "description": "Detailed error message shown when saving login details fails." }, "enableChangedPasswordNotification": { @@ -1366,7 +1366,7 @@ "message": "Característica no disponible" }, "legacyEncryptionUnsupported": { - "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + "message": "La encriptación antigua ya no está soportada. Por favor, contacta con soporte para recuperar tu cuenta." }, "premiumMembership": { "message": "Membresía Premium" @@ -1411,7 +1411,7 @@ "message": "Comprar Premium" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "Puedes comprar el Premium desde los ajustes de tu cuenta en la aplicación web de Bitwarden." }, "premiumCurrentMember": { "message": "¡Eres un miembro Premium!" @@ -1474,7 +1474,7 @@ } }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "No volver a preguntar en este dispositivo durante 30 días" }, "selectAnotherMethod": { "message": "Selecciona otro método", @@ -1511,7 +1511,7 @@ "message": "Opciones de la autenticación en dos pasos" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Selecciona un método de inicio de sesión en dos pasos" }, "recoveryCodeDesc": { "message": "¿Has perdido el acceso a todos tus métodos de autenticación en dos pasos? Utiliza tu código de recuperación para deshabilitar todos los métodos de autenticación en dos pasos de tu cuenta." @@ -1600,13 +1600,13 @@ "message": "Sugerencias de autocompletar" }, "autofillSpotlightTitle": { - "message": "Easily find autofill suggestions" + "message": "Encuentra fácilmente sugerencias de autocompletado" }, "autofillSpotlightDesc": { - "message": "Turn off your browser's autofill settings, so they don't conflict with Bitwarden." + "message": "Desactiva los ajustes de autocompletado de tu navegador para que no entren en conflicto con Bitwarden." }, "turnOffBrowserAutofill": { - "message": "Turn off $BROWSER$ autofill", + "message": "Desactivar autocompletado de $BROWSER$", "placeholders": { "browser": { "content": "$1", @@ -1615,22 +1615,22 @@ } }, "turnOffAutofill": { - "message": "Turn off autofill" + "message": "Desactivar autocompletado" }, "showInlineMenuLabel": { - "message": "Show autofill suggestions on form fields" + "message": "Mostrar sugerencias de autocompletado en campos de formulario" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "Mostrar identidades como sugerencias" }, "showInlineMenuCardsLabel": { "message": "Mostrar tarjetas como sugerencias" }, "showInlineMenuOnIconSelectionLabel": { - "message": "Display suggestions when icon is selected" + "message": "Mostrar sugerencias cuando el icono esté seleccionado" }, "showInlineMenuOnFormFieldsDescAlt": { - "message": "Applies to all logged in accounts." + "message": "Se aplica a todas las cuentas a las que se haya iniciado sesión." }, "turnOffBrowserBuiltInPasswordManagerSettings": { "message": "Desactive la configuración del gestor de contraseñas del navegador para evitar conflictos." @@ -1693,13 +1693,13 @@ "message": "Abrir caja fuerte en la barra lateral" }, "commandAutofillLoginDesc": { - "message": "Autofill the last used login for the current website" + "message": "Autocompletar el último inicio de sesión usado para el sitio web actual" }, "commandAutofillCardDesc": { - "message": "Autofill the last used card for the current website" + "message": "Autocompletar la última tarjeta usada para el sitio web actual" }, "commandAutofillIdentityDesc": { - "message": "Autofill the last used identity for the current website" + "message": "Autocompletar la última identidad usada para el sitio web actual" }, "commandGeneratePasswordDesc": { "message": "Generar y copiar una nueva contraseña aleatoria al portapapeles." @@ -2491,7 +2491,7 @@ "message": "Cannot import card item types" }, "restrictCardTypeImportDesc": { - "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + "message": "Una política establecida en 1 o más organizaciones te impide importar tarjetas a tus cajas fuertes." }, "domainsTitle": { "message": "Dominios", @@ -2991,7 +2991,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "$HOURS$ hora(s) y $MINUTES$ minuto(s) como máximo.", "placeholders": { "hours": { "content": "$1", @@ -3621,7 +3621,7 @@ "message": "Falta el correo electrónico del usuario" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Correo electrónico del usuario activo no encontrado. Cerrando sesión." }, "deviceTrusted": { "message": "Dispositivo de confianza" @@ -3651,11 +3651,11 @@ "message": "Confiar con el usuario" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "Envía información sensible de forma segura", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "Comparte archivos y datos de forma segura con cualquiera, en cualquier plataforma. Tu información permanecerá encriptada de extremo a extremo, limitando su exposición.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { @@ -3820,7 +3820,7 @@ "description": "Button text to display in overlay when the account is locked." }, "unlockAccountAria": { - "message": "Unlock your account, opens in a new window", + "message": "Desbloquea tu cuenta, se abre en una nueva ventana", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { @@ -3828,7 +3828,7 @@ "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Tiempo restante antes de que el TOTP actual expire", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -3856,7 +3856,7 @@ "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Add new vault login item, opens in a new window", + "message": "Añadir nuevo elemento de inicio de sesión a la caja fuerte, se abre en una nueva ventana", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { @@ -4081,7 +4081,7 @@ "message": "No hay inicios de sesión coincidentes para este sitio" }, "searchSavePasskeyNewLogin": { - "message": "Search or save passkey as new login" + "message": "Busca o guarda la clave de acceso como nuevo inicio de sesión" }, "confirm": { "message": "Confirmar" @@ -4093,10 +4093,10 @@ "message": "Guardar clave de acceso como nuevo inicio de sesión" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "Elige un inicio de sesión al que guardar esta clave de acceso" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "Elige una clave de acceso para iniciar sesión en" }, "passkeyItem": { "message": "Elemento de clave de acceso" @@ -4245,7 +4245,7 @@ "description": "Label indicating the most common import formats" }, "confirmContinueToBrowserSettingsTitle": { - "message": "Continue to browser settings?", + "message": "¿Continuar a los ajustes del navegador?", "description": "Title for dialog which asks if the user wants to proceed to a relevant browser settings page" }, "confirmContinueToHelpCenter": { @@ -4325,7 +4325,7 @@ "message": "Sugerencias de autocompletado" }, "itemSuggestions": { - "message": "Suggested items" + "message": "Elementos sugeridos" }, "autofillSuggestionsTip": { "message": "Guarda un elemento de inicio de sesión para este sitio para autocompletar" @@ -4767,7 +4767,7 @@ "message": "Solo los miembros de la organización con acceso a estas colecciones podrán ver los elementos." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Has seleccionado $TOTAL_COUNT$ elementos. No puedes actualizar $READONLY_COUNT$ de los elementos porque no tienes permisos de edición.", "placeholders": { "total_count": { "content": "$1", @@ -4865,10 +4865,10 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "Selecciona colecciones para asignar" }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1 elemento será transferido permanentemente a la organización seleccionada. Ya no serás el propietario de este elemento." }, "personalItemsTransferWarningPlural": { "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to the selected organization. You will no longer own these items.", @@ -4902,13 +4902,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "Colecciones asignadas correctamente" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "No has seleccionado nada." }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Elementos movidos a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -4917,7 +4917,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Elemento movido a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -4961,13 +4961,13 @@ "message": "Acciones de cuenta" }, "showNumberOfAutofillSuggestions": { - "message": "Show number of login autofill suggestions on extension icon" + "message": "Mostrar número de sugerencias de autocompletado de inicios de sesión en el icono de la extensión" }, "showQuickCopyActions": { "message": "Show quick copy actions on Vault" }, "systemDefault": { - "message": "System default" + "message": "Predeterminado del sistema" }, "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" @@ -5009,10 +5009,10 @@ "message": "File saved to device. Manage from your device downloads." }, "showCharacterCount": { - "message": "Show character count" + "message": "Mostrar número de caracteres" }, "hideCharacterCount": { - "message": "Hide character count" + "message": "Ocultar número de caracteres" }, "itemsInTrash": { "message": "Elementos en la papelera" @@ -5069,10 +5069,10 @@ "message": "You can customize your unlock and timeout settings to more quickly access your vault." }, "unlockPinSet": { - "message": "Unlock PIN set" + "message": "Desbloqueo con PIN establecido" }, "unlockWithBiometricSet": { - "message": "Unlock with biometrics set" + "message": "Desbloqueo con biometría establecido" }, "authenticating": { "message": "Autenticando" @@ -5309,7 +5309,7 @@ "message": "Quick and easy login" }, "quickLoginBody": { - "message": "Set up biometric unlock and autofill to log into your accounts without typing a single letter." + "message": "Configura el desbloqueo biométrico y el autocompletado para iniciar sesión en tus cuentas sin tener que escribir ni una sola letra." }, "secureUser": { "message": "Level up your logins" @@ -5318,7 +5318,7 @@ "message": "Utilice el generador para crear y guardar contraseñas fuertes y únicas para todas sus cuentas." }, "secureDevices": { - "message": "Your data, when and where you need it" + "message": "Tus datos, dónde y cuándo los necesites" }, "secureDevicesBody": { "message": "Guarda contraseñas ilimitadas a través de dispositivos ilimitados con aplicaciones móviles, de navegador y de escritorio de Bitwarden." @@ -5348,7 +5348,7 @@ "message": "Search your vault for something else" }, "newLoginNudgeTitle": { - "message": "Save time with autofill" + "message": "Ahorra tiempo con el autocompletado" }, "newLoginNudgeBodyOne": { "message": "Include a", @@ -5372,7 +5372,7 @@ "message": "With cards, easily autofill payment forms securely and accurately." }, "newIdentityNudgeTitle": { - "message": "Simplify creating accounts" + "message": "Simplifica la creación de cuentas" }, "newIdentityNudgeBody": { "message": "With identities, quickly autofill long registration or contact forms." diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 9bb4d992b37..dba3a21d160 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -14,7 +14,7 @@ "description": "Extension description, MUST be less than 112 characters (Safari restriction)" }, "loginOrCreateNewAccount": { - "message": "Zaloguj się lub utwórz nowe konto, aby uzyskać dostęp do Twojego bezpiecznego sejfu." + "message": "Zaloguj się lub utwórz nowe konto, aby uzyskać dostęp do bezpiecznego sejfu." }, "inviteAccepted": { "message": "Zaproszenie zostało zaakceptowane" From a05776c989f2d9839eb66bdf6715eb2b7a8c1b73 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:58:29 +0000 Subject: [PATCH 42/57] Autosync the updated translations (#15392) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 3 +++ apps/web/src/locales/ar/messages.json | 3 +++ apps/web/src/locales/az/messages.json | 23 +++++++++++++---------- apps/web/src/locales/be/messages.json | 3 +++ apps/web/src/locales/bg/messages.json | 5 ++++- apps/web/src/locales/bn/messages.json | 3 +++ apps/web/src/locales/bs/messages.json | 3 +++ apps/web/src/locales/ca/messages.json | 5 ++++- apps/web/src/locales/cs/messages.json | 3 +++ apps/web/src/locales/cy/messages.json | 3 +++ apps/web/src/locales/da/messages.json | 3 +++ apps/web/src/locales/de/messages.json | 3 +++ apps/web/src/locales/el/messages.json | 3 +++ apps/web/src/locales/en_GB/messages.json | 3 +++ apps/web/src/locales/en_IN/messages.json | 3 +++ apps/web/src/locales/eo/messages.json | 3 +++ apps/web/src/locales/es/messages.json | 3 +++ apps/web/src/locales/et/messages.json | 3 +++ apps/web/src/locales/eu/messages.json | 3 +++ apps/web/src/locales/fa/messages.json | 3 +++ apps/web/src/locales/fi/messages.json | 3 +++ apps/web/src/locales/fil/messages.json | 3 +++ apps/web/src/locales/fr/messages.json | 3 +++ apps/web/src/locales/gl/messages.json | 3 +++ apps/web/src/locales/he/messages.json | 3 +++ apps/web/src/locales/hi/messages.json | 3 +++ apps/web/src/locales/hr/messages.json | 3 +++ apps/web/src/locales/hu/messages.json | 3 +++ apps/web/src/locales/id/messages.json | 3 +++ apps/web/src/locales/it/messages.json | 3 +++ apps/web/src/locales/ja/messages.json | 3 +++ apps/web/src/locales/ka/messages.json | 3 +++ apps/web/src/locales/km/messages.json | 3 +++ apps/web/src/locales/kn/messages.json | 3 +++ apps/web/src/locales/ko/messages.json | 3 +++ apps/web/src/locales/lv/messages.json | 3 +++ apps/web/src/locales/ml/messages.json | 3 +++ apps/web/src/locales/mr/messages.json | 3 +++ apps/web/src/locales/my/messages.json | 3 +++ apps/web/src/locales/nb/messages.json | 3 +++ apps/web/src/locales/ne/messages.json | 3 +++ apps/web/src/locales/nl/messages.json | 3 +++ apps/web/src/locales/nn/messages.json | 3 +++ apps/web/src/locales/or/messages.json | 3 +++ apps/web/src/locales/pl/messages.json | 3 +++ apps/web/src/locales/pt_BR/messages.json | 3 +++ apps/web/src/locales/pt_PT/messages.json | 3 +++ apps/web/src/locales/ro/messages.json | 3 +++ apps/web/src/locales/ru/messages.json | 3 +++ apps/web/src/locales/si/messages.json | 3 +++ apps/web/src/locales/sk/messages.json | 3 +++ apps/web/src/locales/sl/messages.json | 3 +++ apps/web/src/locales/sr_CS/messages.json | 3 +++ apps/web/src/locales/sr_CY/messages.json | 19 +++++++++++-------- apps/web/src/locales/sv/messages.json | 3 +++ apps/web/src/locales/te/messages.json | 3 +++ apps/web/src/locales/th/messages.json | 3 +++ apps/web/src/locales/tr/messages.json | 3 +++ apps/web/src/locales/uk/messages.json | 3 +++ apps/web/src/locales/vi/messages.json | 3 +++ apps/web/src/locales/zh_CN/messages.json | 9 ++++++--- apps/web/src/locales/zh_TW/messages.json | 3 +++ 62 files changed, 209 insertions(+), 23 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 33c8aec11af..9350bcfc0ff 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Verstekversameling" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Kry Hulp" }, diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index c96c0565eed..f8a6dc1288b 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "المساعدة" }, diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index ac1280d6ad0..062f0f35ff4 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -2154,16 +2154,16 @@ "message": "İki addımlı girişi qurmaq, Bitwarden hesabınızı birdəfəlik kilidləyə bilər. Geri qaytarma kodu, normal iki addımlı giriş provayderinizi artıq istifadə edə bilmədiyiniz hallarda (məs. cihazınızı itirəndə) hesabınıza müraciət etməyinizə imkan verir. Hesabınıza müraciəti itirsəniz, Bitwarden dəstəyi sizə kömək edə bilməyəcək. Geri qaytarma kodunuzu bir yerə yazmağınızı və ya çap etməyinizi və onu etibarlı bir yerdə saxlamağınızı məsləhət görürük." }, "restrictedItemTypePolicy": { - "message": "Remove card item type" + "message": "Kart element növünü sil" }, "restrictedItemTypePolicyDesc": { - "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + "message": "Üzvlərin kart element növlərini yaratmasına icazə verməyin. Mövcud kartlar avtomatik silinəcək." }, "restrictCardTypeImport": { - "message": "Cannot import card item types" + "message": "Kart element növləri daxilə köçürülə bilmir" }, "restrictCardTypeImportDesc": { - "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + "message": "1 və ya daha çox təşkilat tərəfindən təyin edilən bir siyasət, kartların seyfinizə köçürülməsini əngəlləyir." }, "yourSingleUseRecoveryCode": { "message": "İki addımlı giriş provayderinizə müraciəti itirdiyiniz halda, iki addımlı girişi söndürmək üçün təkistifadəlik geri qaytarma kodunu istifadə edə bilərsiniz. Bitwarden tövsiyə edir ki, geri qaytarma kodunuzu bir yerə yazıb güvənli bir yerdə saxlayın." @@ -2225,7 +2225,7 @@ "message": "Sıradan çıxart" }, "orgUserDetailsNotFound": { - "message": "Member details not found." + "message": "Üzv təfsilatları tapılmadı." }, "revokeAccess": { "message": "Müraciəti ləğv et" @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "İlkin kolleksiya" }, + "myItems": { + "message": "Elementlərim" + }, "getHelp": { "message": "Kömək alın" }, @@ -5373,7 +5376,7 @@ "message": "Fövqəladə hal müraciəti rədd edildi" }, "grantorDetailsNotFound": { - "message": "Grantor details not found" + "message": "Qrant verən təfsilatları tapılmadı" }, "passwordResetFor": { "message": "$USER$ üçün parol sıfırlandı. Artıq yeni parol ilə giriş edə bilərsiniz.", @@ -5385,7 +5388,7 @@ } }, "organizationDataOwnership": { - "message": "Enforce organization data ownership" + "message": "Təşkilata data üzərində məcburi sahiblik ver" }, "personalOwnership": { "message": "Fərdi sahiblik" @@ -5779,7 +5782,7 @@ } }, "emergencyAccessLoggedOutWarning": { - "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "message": "Davam etsəniz, $NAME$ üçün hazırkı seans bitəcək, təkrar giriş etməsi tələb olunacaq. Digər cihazlardakı aktiv seanslar, bir saata qədər aktiv qalmağa davam edə bilər.", "placeholders": { "name": { "content": "$1", @@ -5794,7 +5797,7 @@ "message": "Bir və ya daha çox təşkilat siyasəti, aşağıdakı tələbləri qarşılamaq üçün ana parolu tələb edir:" }, "changePasswordDelegationMasterPasswordPolicyInEffect": { - "message": "One or more organization policies require the master password to meet the following requirements:" + "message": "Bir və ya daha çox təşkilat siyasəti, aşağıdakı tələbləri qarşılamaq üçün ana parolu tələb edir:" }, "resetPasswordSuccess": { "message": "Parol sıfırlama uğurludur!" @@ -10678,7 +10681,7 @@ } }, "billingAddressRequiredToAddCredit": { - "message": "Billing address required to add credit.", + "message": "Kredit əlavə etmək üçün faktura ünvanı tələb olunur.", "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index f0fc9db94c3..05c49e30b44 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Прадвызначаная калекцыя" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Атрымаць даведку" }, diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index e059002f5ff..d3c7604f2d7 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Стандартна колекция" }, + "myItems": { + "message": "Моите елементи" + }, "getHelp": { "message": "Помощ" }, @@ -5385,7 +5388,7 @@ } }, "organizationDataOwnership": { - "message": "Enforce organization data ownership" + "message": "Задължителна собственост на организационните данни" }, "personalOwnership": { "message": "Индивидуално притежание" diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 39f883a7be6..c8539b19f79 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 4bf2dd3c8c8..7c611e9ad5a 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 5ba0d84fcdb..0b5d5e96988 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -502,7 +502,7 @@ "message": "Nom de la carpeta" }, "folderHintText": { - "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + "message": "Imbriqueu una carpeta afegint el nom de la carpeta principal seguit d'una \"/\". Exemple: Social/Fòrums" }, "deleteFolderPermanently": { "message": "Are you sure you want to permanently delete this folder?" @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Col·lecció per defecte" }, + "myItems": { + "message": "Els meus elements" + }, "getHelp": { "message": "Obteniu ajuda" }, diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 5e022396a0e..8a80664c6c2 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Výchozí sbírka" }, + "myItems": { + "message": "Moje položky" + }, "getHelp": { "message": "Získat nápovědu" }, diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 9e9521a6869..b8e3ccd5d25 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 7c4f7459886..1f288af356f 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Standardsamling" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Få hjælp" }, diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index da3d2bd081f..c065f26f98d 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Standardsammlung" }, + "myItems": { + "message": "Meine Einträge" + }, "getHelp": { "message": "Hilfe erhalten" }, diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 815c58a25ec..b8efb914a34 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Προεπιλεγμένη Συλλογή" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Ζητήστε Βοήθεια" }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index e3f279af8f4..bf15dd133f9 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 4d5f5ff177d..d290ea148ad 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 1366fc7c492..49924ab03fe 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Implicita kolekto" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Akiri helpon" }, diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 2bc0abb9c00..253507109a8 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Colección por defecto" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Consigue ayuda" }, diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index c718b718d15..04b51e7c23b 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Vaikekogumik" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Klienditugi" }, diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 1b41987012c..30f3f5488d7 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Bilduma lehenetsia" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Jaso laguntza" }, diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 63ef193879a..99cdefe5695 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "مجموعه پیش‌فرض" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "کمک گرفتن" }, diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 607b17d7749..9f6afee3713 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Oletuskokoelma" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Hanki apua" }, diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index c8689d54bbc..4794aef1d99 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default na koleksyon" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Humingi ng tulong" }, diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 0ebdfaa611c..3abcdf9a999 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Collection par défaut" }, + "myItems": { + "message": "Mes éléments" + }, "getHelp": { "message": "Obtenir de l'aide" }, diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index ad59838cc77..9ed6e922300 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 59f5dbc404b..e569d94437e 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "אוסף ברירת מחדל" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "קבל עזרה" }, diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 250d68f1f0a..f6e2e491f49 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index d322231d2ad..ef5730fb6b6 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Zadana zbirka" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Potraži pomoć" }, diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 211e99d3234..4ad900a4bae 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Alapértelmezett gyűjtemény" }, + "myItems": { + "message": "Saját elemek" + }, "getHelp": { "message": "Segítségkérés" }, diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index bb0fbad9361..fffe374b44b 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Koleksi Default" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Dapatkan Bantuan" }, diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 30cd8bb6d53..a861f368366 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Raccolta predefinita" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Ottieni aiuto" }, diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 95e2cc71644..9af2943f1da 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "既定のコレクション" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "ヘルプを参照する" }, diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 4ebfca7db2c..d5ae3e1978c 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index bdcdf5ed237..f422e74a569 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 9abfaf0329b..b400d0ca863 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "ಡೀಫಾಲ್ಟ್ ಸಂಗ್ರಹ" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "ಸಹಾಯ ಪಡೆ" }, diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 8ae6e906dea..8293750eed3 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "기본 컬렉션" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "문의하기" }, diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 0c738d66de2..b17131cf2ef 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Noklusējuma krājums" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Saņemt palīdzību" }, diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 2b93514d5ad..ef1bb77feb4 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default Collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get Help" }, diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 338c0228fcf..97eb93dd3eb 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index bdcdf5ed237..f422e74a569 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 7221018aca0..68cd7725bd5 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Standardsamling" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Få hjelp" }, diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 4b401cd67a8..f74848f0aee 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 556ef03ada0..fc2b6e9593c 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Standaardverzameling" }, + "myItems": { + "message": "Mijn items" + }, "getHelp": { "message": "Hulp vragen" }, diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 0707b948a38..79700ff65e3 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index bdcdf5ed237..f422e74a569 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index b70804f2b6f..13e67f9d33c 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Domyślna kolekcja" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Uzyskaj pomoc" }, diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index dbd11b62095..da4180ee0e6 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Coleção Padrão" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Obter Ajuda" }, diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 33ac3148a1c..eb06f4b7390 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Coleção predefinida" }, + "myItems": { + "message": "Os meus itens" + }, "getHelp": { "message": "Obter ajuda" }, diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index add0453a747..569faf4e9a8 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Colecție implicită" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Obținere ajutor" }, diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 146c9659ce0..d57bdc8ed45 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Коллекция по умолчанию" }, + "myItems": { + "message": "Мои элементы" + }, "getHelp": { "message": "Получить помощь" }, diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 412960c2453..80dd700f848 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 752f088a92c..c3f204beaf3 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Predvolená kolekcia" }, + "myItems": { + "message": "Moje položky" + }, "getHelp": { "message": "Získať pomoc" }, diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index dca8c4d1161..5b68d7e21af 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Pomoč" }, diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 71af7ca2062..c0681d1224f 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index 0f84a52f849..837b8c706b8 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -2154,10 +2154,10 @@ "message": "Омогућавање пријаве у два корака може вас трајно закључати са вашег Bitwarden-а налога. Код за опоравак омогућава вам приступ вашем налогу у случају да више не можете да користите свог уобичајеног добављача услуге пријављивања у два корака (нпр. ако изгубите уређај). Подршка Bitwarden-а неће вам моћи помоћи ако изгубите приступ свом налогу. Препоручујемо да запишете или одштампате код за опоравак и сачувате га на сигурном месту." }, "restrictedItemTypePolicy": { - "message": "Remove card item type" + "message": "Уклоните тип ставке картице" }, "restrictedItemTypePolicyDesc": { - "message": "Do not allow members to create card item types. Existing cards will be automatically removed." + "message": "Не дозволите члановима да креирају врсте предмета картице. Постојеће картице ће се аутоматски уклонити." }, "restrictCardTypeImport": { "message": "Не могу увозити врсте картица" @@ -2225,7 +2225,7 @@ "message": "Онемогући" }, "orgUserDetailsNotFound": { - "message": "Member details not found." + "message": "Детаљи чланова нису пронађени." }, "revokeAccess": { "message": "Опозови Приступ" @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Стандардна колекција" }, + "myItems": { + "message": "Моји предмети" + }, "getHelp": { "message": "Потражи помоћ" }, @@ -5373,7 +5376,7 @@ "message": "Одбијен хитни приступ" }, "grantorDetailsNotFound": { - "message": "Grantor details not found" + "message": "Детаљи одобравања нису пронађени" }, "passwordResetFor": { "message": "Ресетовање лозинке за $USER$. Сада се можете пријавити помоћу нове лозинке.", @@ -5385,7 +5388,7 @@ } }, "organizationDataOwnership": { - "message": "Enforce organization data ownership" + "message": "Спровести власништво података о организацији" }, "personalOwnership": { "message": "Лично власништво" @@ -5779,7 +5782,7 @@ } }, "emergencyAccessLoggedOutWarning": { - "message": "Proceeding will log $NAME$ out of their current session, requiring them to log back in. Active sessions on other devices may continue to remain active for up to one hour.", + "message": "Ако наставите, објавићете $NAME$ са тренутне сесије, захтевајући их да се поново пријаве. Активне сесије на другим уређајима могу наставити да остају активне до једног сата.", "placeholders": { "name": { "content": "$1", @@ -5794,7 +5797,7 @@ "message": "Једна или више смерница организације захтевају главну лозинку да би испуњавали следеће захтеве:" }, "changePasswordDelegationMasterPasswordPolicyInEffect": { - "message": "One or more organization policies require the master password to meet the following requirements:" + "message": "Једна или више смерница организације захтевају главну лозинку да би испуњавали следеће захтеве:" }, "resetPasswordSuccess": { "message": "Успешно ресетовање лозинке!" @@ -10678,7 +10681,7 @@ } }, "billingAddressRequiredToAddCredit": { - "message": "Billing address required to add credit.", + "message": "Адреса за наплату је потребна за додавање кредита.", "description": "Error message shown when trying to add credit to a trialing organization without a billing address." } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index a81335284a1..41813e5edc0 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Standardsamling" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Få hjälp" }, diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index bdcdf5ed237..f422e74a569 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 48cedb1c4d5..c47c6caca85 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Get help" }, diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index af3d51faa3e..211851f8450 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Varsayılan koleksiyon" }, + "myItems": { + "message": "Kayıtlarım" + }, "getHelp": { "message": "Yardım al" }, diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index c67b8d62ceb..4ddcfa3c463 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Типова збірка" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Отримати допомогу" }, diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 6b9cb8d0ac0..c3bbf855a38 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "Default collection" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "Nhận trợ giúp" }, diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 55579121b4b..550d4d56bdd 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "默认集合" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "获取帮助" }, @@ -8725,13 +8728,13 @@ "message": "管理组织的集合行为" }, "limitCollectionCreationDesc": { - "message": "限制为所有者和管理员可以创建集合" + "message": "仅限所有者和管理员可以创建集合" }, "limitCollectionDeletionDesc": { - "message": "限制为所有者和管理员可以删除集合" + "message": "仅限所有者和管理员可以删除集合" }, "limitItemDeletionDescription": { - "message": "限制为具有「管理集合」权限的成员可以删项目" + "message": "仅限具有「管理集合」权限的成员可以删除项目" }, "allowAdminAccessToAllCollectionItemsDesc": { "message": "所有者和管理员可以管理所有集合和项目" diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index a44d7eb5f28..2193c9503ab 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -3275,6 +3275,9 @@ "defaultCollection": { "message": "預設集合" }, + "myItems": { + "message": "My Items" + }, "getHelp": { "message": "尋求幫助" }, From 8fec95671d1f39c863e9977542c95c80fc5957c2 Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:38:51 +0200 Subject: [PATCH 43/57] [PM-22090] Delete password on Windows desktop throws incorrect error (#15070) * delete password on Windows desktop throws incorrect error * delete password on Windows desktop throws incorrect error * napi documentation improvements * napi documentation update * better logging verbosity * desktop native clippy errors * unit test coverage * napi TS documentation JS language friendly * fixing merge conflicts --- .../desktop_native/core/src/password/macos.rs | 25 ++-- .../desktop_native/core/src/password/mod.rs | 2 + .../desktop_native/core/src/password/unix.rs | 9 +- .../core/src/password/windows.rs | 11 +- apps/desktop/desktop_native/napi/index.d.ts | 17 ++- apps/desktop/desktop_native/napi/src/lib.rs | 10 +- .../main-biometrics-ipc.listener.ts | 2 +- .../biometrics/main-biometrics.service.ts | 3 +- .../os-biometrics-linux.service.spec.ts | 86 +++++++++++++ .../biometrics/os-biometrics-linux.service.ts | 16 ++- .../os-biometrics-mac.service.spec.ts | 78 ++++++++++++ .../biometrics/os-biometrics-mac.service.ts | 20 ++- .../os-biometrics-windows.service.spec.ts | 115 +++++++++++++++++- .../os-biometrics-windows.service.ts | 43 ++++++- .../desktop-credential-storage-listener.ts | 10 +- 15 files changed, 407 insertions(+), 40 deletions(-) create mode 100644 apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.spec.ts create mode 100644 apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.spec.ts diff --git a/apps/desktop/desktop_native/core/src/password/macos.rs b/apps/desktop/desktop_native/core/src/password/macos.rs index 1af005acb4d..3075dce3b88 100644 --- a/apps/desktop/desktop_native/core/src/password/macos.rs +++ b/apps/desktop/desktop_native/core/src/password/macos.rs @@ -1,10 +1,12 @@ +use crate::password::PASSWORD_NOT_FOUND; use anyhow::Result; use security_framework::passwords::{ delete_generic_password, get_generic_password, set_generic_password, }; pub async fn get_password(service: &str, account: &str) -> Result { - let result = String::from_utf8(get_generic_password(service, account)?)?; + let password = get_generic_password(service, account).map_err(convert_error)?; + let result = String::from_utf8(password)?; Ok(result) } @@ -14,7 +16,7 @@ pub async fn set_password(service: &str, account: &str, password: &str) -> Resul } pub async fn delete_password(service: &str, account: &str) -> Result<()> { - delete_generic_password(service, account)?; + delete_generic_password(service, account).map_err(convert_error)?; Ok(()) } @@ -22,6 +24,15 @@ pub async fn is_available() -> Result { Ok(true) } +fn convert_error(e: security_framework::base::Error) -> anyhow::Error { + match e.code() { + security_framework_sys::base::errSecItemNotFound => { + anyhow::anyhow!(PASSWORD_NOT_FOUND) + } + _ => anyhow::anyhow!(e), + } +} + #[cfg(test)] mod tests { use super::*; @@ -44,10 +55,7 @@ mod tests { // Ensure password is deleted match get_password("BitwardenTest", "BitwardenTest").await { Ok(_) => panic!("Got a result"), - Err(e) => assert_eq!( - "The specified item could not be found in the keychain.", - e.to_string() - ), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } @@ -55,10 +63,7 @@ mod tests { async fn test_error_no_password() { match get_password("Unknown", "Unknown").await { Ok(_) => panic!("Got a result"), - Err(e) => assert_eq!( - "The specified item could not be found in the keychain.", - e.to_string() - ), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } } diff --git a/apps/desktop/desktop_native/core/src/password/mod.rs b/apps/desktop/desktop_native/core/src/password/mod.rs index 351e896c40e..93c91072dd2 100644 --- a/apps/desktop/desktop_native/core/src/password/mod.rs +++ b/apps/desktop/desktop_native/core/src/password/mod.rs @@ -1,3 +1,5 @@ +pub const PASSWORD_NOT_FOUND: &str = "Password not found."; + #[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "unix.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] diff --git a/apps/desktop/desktop_native/core/src/password/unix.rs b/apps/desktop/desktop_native/core/src/password/unix.rs index a40f4d8ed43..0b1c59e7724 100644 --- a/apps/desktop/desktop_native/core/src/password/unix.rs +++ b/apps/desktop/desktop_native/core/src/password/unix.rs @@ -1,3 +1,4 @@ +use crate::password::PASSWORD_NOT_FOUND; use anyhow::{anyhow, Result}; use oo7::dbus::{self}; use std::collections::HashMap; @@ -20,7 +21,7 @@ async fn get_password_new(service: &str, account: &str) -> Result { let secret = res.secret().await?; Ok(String::from_utf8(secret.to_vec())?) } - None => Err(anyhow!("no result")), + None => Err(anyhow!(PASSWORD_NOT_FOUND)), } } @@ -46,7 +47,7 @@ async fn get_password_legacy(service: &str, account: &str) -> Result { set_password(service, account, &secret_string).await?; Ok(secret_string) } - None => Err(anyhow!("no result")), + None => Err(anyhow!(PASSWORD_NOT_FOUND)), } } @@ -152,7 +153,7 @@ mod tests { Ok(_) => { panic!("Got a result") } - Err(e) => assert_eq!("no result", e.to_string()), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } @@ -160,7 +161,7 @@ mod tests { async fn test_error_no_password() { match get_password("Unknown", "Unknown").await { Ok(_) => panic!("Got a result"), - Err(e) => assert_eq!("no result", e.to_string()), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } } diff --git a/apps/desktop/desktop_native/core/src/password/windows.rs b/apps/desktop/desktop_native/core/src/password/windows.rs index f0dcc3fb1eb..ee2361a832f 100644 --- a/apps/desktop/desktop_native/core/src/password/windows.rs +++ b/apps/desktop/desktop_native/core/src/password/windows.rs @@ -1,3 +1,4 @@ +use crate::password::PASSWORD_NOT_FOUND; use anyhow::{anyhow, Result}; use widestring::{U16CString, U16String}; use windows::{ @@ -79,7 +80,9 @@ pub async fn set_password(service: &str, account: &str, password: &str) -> Resul pub async fn delete_password(service: &str, account: &str) -> Result<()> { let target_name = U16CString::from_str(target_name(service, account))?; - unsafe { CredDeleteW(PCWSTR(target_name.as_ptr()), CRED_TYPE_GENERIC, None)? }; + let result = unsafe { CredDeleteW(PCWSTR(target_name.as_ptr()), CRED_TYPE_GENERIC, None) }; + + result.map_err(|e| anyhow!(convert_error(e)))?; Ok(()) } @@ -95,7 +98,7 @@ fn target_name(service: &str, account: &str) -> String { // Convert the internal WIN32 errors to descriptive messages fn convert_error(e: windows::core::Error) -> String { if e == ERROR_NOT_FOUND.into() { - return "Password not found.".to_string(); + return PASSWORD_NOT_FOUND.to_string(); } e.to_string() } @@ -122,7 +125,7 @@ mod tests { // Ensure password is deleted match get_password("BitwardenTest", "BitwardenTest").await { Ok(_) => panic!("Got a result"), - Err(e) => assert_eq!("Password not found.", e.to_string()), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } @@ -130,7 +133,7 @@ mod tests { async fn test_error_no_password() { match get_password("BitwardenTest", "BitwardenTest").await { Ok(_) => panic!("Got a result"), - Err(e) => assert_eq!("Password not found.", e.to_string()), + Err(e) => assert_eq!(PASSWORD_NOT_FOUND, e.to_string()), } } } diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index b3c6f715e98..cb9430290e3 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -4,18 +4,31 @@ /* auto-generated by NAPI-RS */ export declare namespace passwords { - /** Fetch the stored password from the keychain. */ + /** The error message returned when a password is not found during retrieval or deletion. */ + export const PASSWORD_NOT_FOUND: string + /** + * Fetch the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ export function getPassword(service: string, account: string): Promise /** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */ export function setPassword(service: string, account: string, password: string): Promise - /** Delete the stored password from the keychain. */ + /** + * Delete the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ export function deletePassword(service: string, account: string): Promise + /** Checks if the os secure storage is available */ export function isAvailable(): Promise } export declare namespace biometrics { export function prompt(hwnd: Buffer, message: string): Promise export function available(): Promise export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise + /** + * Retrieves the biometric secret for the given service and account. + * Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. + */ export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise /** * Derives key material from biometric data. Returns a string encoded with a diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 079872a3b03..e538dc8d432 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -6,7 +6,12 @@ mod registry; #[napi] pub mod passwords { + /// The error message returned when a password is not found during retrieval or deletion. + #[napi] + pub const PASSWORD_NOT_FOUND: &str = desktop_core::password::PASSWORD_NOT_FOUND; + /// Fetch the stored password from the keychain. + /// Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. #[napi] pub async fn get_password(service: String, account: String) -> napi::Result { desktop_core::password::get_password(&service, &account) @@ -27,6 +32,7 @@ pub mod passwords { } /// Delete the stored password from the keychain. + /// Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. #[napi] pub async fn delete_password(service: String, account: String) -> napi::Result<()> { desktop_core::password::delete_password(&service, &account) @@ -34,7 +40,7 @@ pub mod passwords { .map_err(|e| napi::Error::from_reason(e.to_string())) } - // Checks if the os secure storage is available + /// Checks if the os secure storage is available #[napi] pub async fn is_available() -> napi::Result { desktop_core::password::is_available() @@ -84,6 +90,8 @@ pub mod biometrics { .map_err(|e| napi::Error::from_reason(e.to_string())) } + /// Retrieves the biometric secret for the given service and account. + /// Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. #[napi] pub async fn get_biometric_secret( service: String, diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts b/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts index e270c4cc50f..d4ce01f53f4 100644 --- a/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts +++ b/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts @@ -55,7 +55,7 @@ export class MainBiometricsIPCListener { return; } } catch (e) { - this.logService.info(e); + this.logService.error("[Main Biometrics IPC Listener] %s failed", message.action, e); } }); } diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts index a6a0e532655..1de8e3cd12d 100644 --- a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts @@ -40,7 +40,7 @@ export class MainBiometricsService extends DesktopBiometricsService { } else if (platform === "darwin") { // eslint-disable-next-line const OsBiometricsServiceMac = require("./os-biometrics-mac.service").default; - this.osBiometricsService = new OsBiometricsServiceMac(this.i18nService); + this.osBiometricsService = new OsBiometricsServiceMac(this.i18nService, this.logService); } else if (platform === "linux") { // eslint-disable-next-line const OsBiometricsServiceLinux = require("./os-biometrics-linux.service").default; @@ -48,6 +48,7 @@ export class MainBiometricsService extends DesktopBiometricsService { this.biometricStateService, this.encryptService, this.cryptoFunctionService, + this.logService, ); } else { throw new Error("Unsupported platform"); diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.spec.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.spec.ts new file mode 100644 index 00000000000..64af0cc625e --- /dev/null +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.spec.ts @@ -0,0 +1,86 @@ +import { mock } from "jest-mock-extended"; + +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { passwords } from "@bitwarden/desktop-napi"; +import { BiometricStateService } from "@bitwarden/key-management"; + +import OsBiometricsServiceLinux from "./os-biometrics-linux.service"; + +jest.mock("@bitwarden/desktop-napi", () => ({ + biometrics: { + setBiometricSecret: jest.fn(), + getBiometricSecret: jest.fn(), + deleteBiometricSecret: jest.fn(), + prompt: jest.fn(), + available: jest.fn(), + deriveKeyMaterial: jest.fn(), + }, + passwords: { + deletePassword: jest.fn(), + getPassword: jest.fn(), + isAvailable: jest.fn(), + PASSWORD_NOT_FOUND: "Password not found", + }, +})); + +describe("OsBiometricsServiceLinux", () => { + let service: OsBiometricsServiceLinux; + let logService: LogService; + + const mockUserId = "test-user-id" as UserId; + + beforeEach(() => { + const biometricStateService = mock(); + const encryptService = mock(); + const cryptoFunctionService = mock(); + logService = mock(); + service = new OsBiometricsServiceLinux( + biometricStateService, + encryptService, + cryptoFunctionService, + logService, + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("deleteBiometricKey", () => { + const serviceName = "Bitwarden_biometric"; + const keyName = "test-user-id_user_biometric"; + + it("should delete biometric key successfully", async () => { + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + }); + + it("should not throw error if key not found", async () => { + passwords.deletePassword = jest + .fn() + .mockRejectedValueOnce(new Error(passwords.PASSWORD_NOT_FOUND)); + + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(logService.debug).toHaveBeenCalledWith( + "[OsBiometricService] Biometric key %s not found for service %s.", + keyName, + serviceName, + ); + }); + + it("should throw error for unexpected errors", async () => { + const error = new Error("Unexpected error"); + passwords.deletePassword = jest.fn().mockRejectedValueOnce(error); + + await expect(service.deleteBiometricKey(mockUserId)).rejects.toThrow(error); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + }); + }); +}); diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.ts index 8d3c8e9795f..3f13682f1b7 100644 --- a/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.ts +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-linux.service.ts @@ -2,6 +2,7 @@ import { spawn } from "child_process"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -42,6 +43,7 @@ export default class OsBiometricsServiceLinux implements OsBiometricService { private biometricStateService: BiometricStateService, private encryptService: EncryptService, private cryptoFunctionService: CryptoFunctionService, + private logService: LogService, ) {} private _iv: string | null = null; // Use getKeyMaterial helper instead of direct access @@ -62,7 +64,19 @@ export default class OsBiometricsServiceLinux implements OsBiometricService { ); } async deleteBiometricKey(userId: UserId): Promise { - await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId)); + try { + await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId)); + } catch (e) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + this.logService.debug( + "[OsBiometricService] Biometric key %s not found for service %s.", + getLookupKeyForUser(userId), + SERVICE, + ); + } else { + throw e; + } + } } async getBiometricKey(userId: UserId): Promise { diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.spec.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.spec.ts new file mode 100644 index 00000000000..6d20095d8bb --- /dev/null +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.spec.ts @@ -0,0 +1,78 @@ +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { passwords } from "@bitwarden/desktop-napi"; + +import OsBiometricsServiceMac from "./os-biometrics-mac.service"; + +jest.mock("@bitwarden/desktop-napi", () => ({ + biometrics: { + setBiometricSecret: jest.fn(), + getBiometricSecret: jest.fn(), + deleteBiometricSecret: jest.fn(), + prompt: jest.fn(), + available: jest.fn(), + deriveKeyMaterial: jest.fn(), + }, + passwords: { + deletePassword: jest.fn(), + getPassword: jest.fn(), + isAvailable: jest.fn(), + PASSWORD_NOT_FOUND: "Password not found", + }, +})); + +describe("OsBiometricsServiceMac", () => { + let service: OsBiometricsServiceMac; + let i18nService: I18nService; + let logService: LogService; + + const mockUserId = "test-user-id" as UserId; + + beforeEach(() => { + i18nService = mock(); + logService = mock(); + service = new OsBiometricsServiceMac(i18nService, logService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("deleteBiometricKey", () => { + const serviceName = "Bitwarden_biometric"; + const keyName = "test-user-id_user_biometric"; + + it("should delete biometric key successfully", async () => { + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + }); + + it("should not throw error if key not found", async () => { + passwords.deletePassword = jest + .fn() + .mockRejectedValueOnce(new Error(passwords.PASSWORD_NOT_FOUND)); + + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(logService.debug).toHaveBeenCalledWith( + "[OsBiometricService] Biometric key %s not found for service %s.", + keyName, + serviceName, + ); + }); + + it("should throw error for unexpected errors", async () => { + const error = new Error("Unexpected error"); + passwords.deletePassword = jest.fn().mockRejectedValueOnce(error); + + await expect(service.deleteBiometricKey(mockUserId)).rejects.toThrow(error); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + }); + }); +}); diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.ts index 004495b6da9..1dc64f1bcd5 100644 --- a/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.ts +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.ts @@ -1,6 +1,7 @@ import { systemPreferences } from "electron"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; import { passwords } from "@bitwarden/desktop-napi"; @@ -14,7 +15,10 @@ function getLookupKeyForUser(userId: UserId): string { } export default class OsBiometricsServiceMac implements OsBiometricService { - constructor(private i18nservice: I18nService) {} + constructor( + private i18nservice: I18nService, + private logService: LogService, + ) {} async supportsBiometrics(): Promise { return systemPreferences.canPromptTouchID(); @@ -52,7 +56,19 @@ export default class OsBiometricsServiceMac implements OsBiometricService { } async deleteBiometricKey(user: UserId): Promise { - return await passwords.deletePassword(SERVICE, getLookupKeyForUser(user)); + try { + return await passwords.deletePassword(SERVICE, getLookupKeyForUser(user)); + } catch (e) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + this.logService.debug( + "[OsBiometricService] Biometric key %s not found for service %s.", + getLookupKeyForUser(user), + SERVICE, + ); + } else { + throw e; + } + } } private async valueUpToDate(user: UserId, key: SymmetricCryptoKey): Promise { diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.spec.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.spec.ts index d0fd8682f2a..674d97bf696 100644 --- a/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.spec.ts +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.spec.ts @@ -6,8 +6,11 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; +import { passwords } from "@bitwarden/desktop-napi"; import { BiometricsStatus, BiometricStateService } from "@bitwarden/key-management"; +import { WindowMain } from "../../main/window.main"; + import OsBiometricsServiceWindows from "./os-biometrics-windows.service"; jest.mock("@bitwarden/desktop-napi", () => ({ @@ -15,28 +18,37 @@ jest.mock("@bitwarden/desktop-napi", () => ({ available: jest.fn(), setBiometricSecret: jest.fn(), getBiometricSecret: jest.fn(), - deriveKeyMaterial: jest.fn(), + deleteBiometricSecret: jest.fn(), prompt: jest.fn(), + deriveKeyMaterial: jest.fn(), }, passwords: { getPassword: jest.fn(), deletePassword: jest.fn(), + isAvailable: jest.fn(), + PASSWORD_NOT_FOUND: "Password not found", }, })); describe("OsBiometricsServiceWindows", () => { let service: OsBiometricsServiceWindows; + let i18nService: I18nService; + let windowMain: WindowMain; + let logService: LogService; let biometricStateService: BiometricStateService; + const mockUserId = "test-user-id" as UserId; + beforeEach(() => { - const i18nService = mock(); - const logService = mock(); + i18nService = mock(); + windowMain = mock(); + logService = mock(); biometricStateService = mock(); const encryptionService = mock(); const cryptoFunctionService = mock(); service = new OsBiometricsServiceWindows( i18nService, - null, + windowMain, logService, biometricStateService, encryptionService, @@ -81,7 +93,7 @@ describe("OsBiometricsServiceWindows", () => { cryptoFunctionService = mock(); service = new OsBiometricsServiceWindows( mock(), - null, + windowMain, mock(), biometricStateService, encryptionService, @@ -140,4 +152,97 @@ describe("OsBiometricsServiceWindows", () => { expect((service as any).clientKeyHalves.get(userId.toString())).toBeNull(); }); }); + + describe("deleteBiometricKey", () => { + const serviceName = "Bitwarden_biometric"; + const keyName = "test-user-id_user_biometric"; + const witnessKeyName = "test-user-id_user_biometric_witness"; + + it("should delete biometric key successfully", async () => { + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, witnessKeyName); + }); + + it.each([ + [false, false], + [false, true], + [true, false], + ])( + "should not throw error if key found: %s and witness key found: %s", + async (keyFound, witnessKeyFound) => { + passwords.deletePassword = jest.fn().mockImplementation((_, account) => { + if (account === keyName) { + if (!keyFound) { + throw new Error(passwords.PASSWORD_NOT_FOUND); + } + return Promise.resolve(); + } + if (account === witnessKeyName) { + if (!witnessKeyFound) { + throw new Error(passwords.PASSWORD_NOT_FOUND); + } + return Promise.resolve(); + } + throw new Error("Unexpected key"); + }); + + await service.deleteBiometricKey(mockUserId); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, witnessKeyName); + if (!keyFound) { + expect(logService.debug).toHaveBeenCalledWith( + "[OsBiometricService] Biometric key %s not found for service %s.", + keyName, + serviceName, + ); + } + if (!witnessKeyFound) { + expect(logService.debug).toHaveBeenCalledWith( + "[OsBiometricService] Biometric witness key %s not found for service %s.", + witnessKeyName, + serviceName, + ); + } + }, + ); + + it("should throw error when deletePassword for key throws unexpected errors", async () => { + const error = new Error("Unexpected error"); + passwords.deletePassword = jest.fn().mockImplementation((_, account) => { + if (account === keyName) { + throw error; + } + if (account === witnessKeyName) { + return Promise.resolve(); + } + throw new Error("Unexpected key"); + }); + + await expect(service.deleteBiometricKey(mockUserId)).rejects.toThrow(error); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(passwords.deletePassword).not.toHaveBeenCalledWith(serviceName, witnessKeyName); + }); + + it("should throw error when deletePassword for witness key throws unexpected errors", async () => { + const error = new Error("Unexpected error"); + passwords.deletePassword = jest.fn().mockImplementation((_, account) => { + if (account === keyName) { + return Promise.resolve(); + } + if (account === witnessKeyName) { + throw error; + } + throw new Error("Unexpected key"); + }); + + await expect(service.deleteBiometricKey(mockUserId)).rejects.toThrow(error); + + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, keyName); + expect(passwords.deletePassword).toHaveBeenCalledWith(serviceName, witnessKeyName); + }); + }); }); diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.ts index dc4f8674d7f..b1726644b0a 100644 --- a/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.ts +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-windows.service.ts @@ -116,8 +116,32 @@ export default class OsBiometricsServiceWindows implements OsBiometricService { } async deleteBiometricKey(userId: UserId): Promise { - await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId)); - await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId) + KEY_WITNESS_SUFFIX); + try { + await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId)); + } catch (e) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + this.logService.debug( + "[OsBiometricService] Biometric key %s not found for service %s.", + getLookupKeyForUser(userId), + SERVICE, + ); + } else { + throw e; + } + } + try { + await passwords.deletePassword(SERVICE, getLookupKeyForUser(userId) + KEY_WITNESS_SUFFIX); + } catch (e) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + this.logService.debug( + "[OsBiometricService] Biometric witness key %s not found for service %s.", + getLookupKeyForUser(userId) + KEY_WITNESS_SUFFIX, + SERVICE, + ); + } else { + throw e; + } + } } async authenticateBiometric(): Promise { @@ -227,8 +251,19 @@ export default class OsBiometricsServiceWindows implements OsBiometricService { storageKey + KEY_WITNESS_SUFFIX, witnessKeyMaterial, ); - } catch { - this.logService.debug("Error retrieving witness key, assuming value is not up to date."); + } catch (e) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + this.logService.debug( + "[OsBiometricService] Biometric witness key %s not found for service %s, value is not up to date.", + storageKey + KEY_WITNESS_SUFFIX, + service, + ); + } else { + this.logService.error( + "[OsBiometricService] Error retrieving witness key, assuming value is not up to date.", + e, + ); + } return false; } diff --git a/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts b/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts index ca4d9a2d3ca..6922911e367 100644 --- a/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts +++ b/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts @@ -35,13 +35,13 @@ export class DesktopCredentialStorageListener { } return val; } catch (e) { - if ( - e.message === "Password not found." || - e.message === "The specified item could not be found in the keychain." - ) { + if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) { + if (message.action === "hasPassword") { + return false; + } return null; } - this.logService.info(e); + this.logService.error("[Credential Storage Listener] %s failed", message.action, e); } }); } From 043d8b353315ae01d64aa84c12d0cf8b8ed33ea5 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:58:41 +0100 Subject: [PATCH 44/57] Dont skip payment details if triallength is zero (#15268) --- .../complete-trial-initiation.component.html | 8 ++++++-- .../complete-trial-initiation.component.ts | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html index 7f093842b6a..7a1ca2cd83d 100644 --- a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html +++ b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html @@ -38,13 +38,17 @@ [loading]="loading && (trialPaymentOptional$ | async)" (click)="orgNameEntrySubmit()" > - {{ (trialPaymentOptional$ | async) ? ("startTrial" | i18n) : ("next" | i18n) }} + {{ + (trialPaymentOptional$ | async) && trialLength > 0 + ? ("startTrial" | i18n) + : ("next" | i18n) + }} { const isTrialPaymentOptional = await firstValueFrom(this.trialPaymentOptional$); - if (isTrialPaymentOptional) { + /** Only skip payment if the flag is on AND trialLength > 0 */ + if (isTrialPaymentOptional && this.trialLength > 0) { await this.createOrganizationOnTrial(); } else { await this.conditionallyCreateOrganization(); From 0772e5c316536c567a503d8a217c770afbdc12c8 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 30 Jun 2025 13:13:00 +0000 Subject: [PATCH 45/57] Bumped client version(s) --- apps/web/package.json | 2 +- package-lock.json | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 4dfcd58cce4..f672e44a80b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2025.6.1", + "version": "2025.7.0", "scripts": { "build:oss": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/package-lock.json b/package-lock.json index c83580dd0d5..0855df67e8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -191,7 +191,7 @@ "webpack-node-externals": "3.0.0" }, "engines": { - "node": "~20", + "node": "~22", "npm": "~10" } }, @@ -235,6 +235,10 @@ }, "bin": { "bw": "build/bw.js" + }, + "engines": { + "node": "~20", + "npm": "~10" } }, "apps/cli/node_modules/define-lazy-prop": { @@ -300,7 +304,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2025.6.1" + "version": "2025.7.0" }, "libs/admin-console": { "name": "@bitwarden/admin-console", From 04ddea5bf3bd75400455d13bedf7760ea2b30a4c Mon Sep 17 00:00:00 2001 From: Vicki League Date: Mon, 30 Jun 2025 09:39:39 -0400 Subject: [PATCH 46/57] [CL-473] Adjust popup page max width and scroll containers (#14853) --- .../platform/popup/layout/popup-layout.mdx | 19 ++++ .../popup/layout/popup-layout.stories.ts | 100 ++++++++++++++++-- .../popup/layout/popup-page.component.html | 30 ++++-- .../popup/layout/popup-page.component.ts | 3 +- apps/browser/src/popup/scss/base.scss | 8 -- .../vault-list-items-container.component.html | 5 +- .../vault-list-items-container.component.ts | 2 + .../vault-v2/vault-v2.component.html | 10 +- .../src/stories/virtual-scrolling.mdx | 42 +++++++- libs/components/src/tw-theme.css | 2 + 10 files changed, 181 insertions(+), 40 deletions(-) diff --git a/apps/browser/src/platform/popup/layout/popup-layout.mdx b/apps/browser/src/platform/popup/layout/popup-layout.mdx index 017ee20b344..a2725350a8f 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.mdx +++ b/apps/browser/src/platform/popup/layout/popup-layout.mdx @@ -54,6 +54,9 @@ page looks nice when the extension is popped out. `false`. - `loadingText` - Custom text to be applied to the loading element for screenreaders only. Defaults to "Loading". +- `disablePadding` + - When `true`, disables the padding of the scrollable region inside of `main`. You will need to + add your own padding to the element you place inside of this area. Basic usage example: @@ -169,6 +172,22 @@ When the browser extension is popped out, the "popout" button should not be pass +## With Virtual Scroll + +If you are using a virtual scrolling container inside of the popup page, you'll want to apply the +`bitScrollLayout` directive to the `cdk-virtual-scroll-viewport` element. This tells the virtual +scroll viewport to use the popup page's scroll layout div as the scrolling container. + +See the code in the example below. + + + +### Known Virtual Scroll Issues + +See [Virtual Scrolling](?path=/docs/documentation-virtual-scrolling--docs#known-footgun) for more +information about how to structure virtual scrolling containers with layout components and avoid a +known issue with template construction. + # Other stories ## Centered Content diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index aecbaf673dc..894ab13dd19 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -1,5 +1,4 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore +import { ScrollingModule } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; import { Component, importProvidersFrom } from "@angular/core"; import { RouterModule } from "@angular/router"; @@ -20,6 +19,7 @@ import { NoItemsModule, SearchModule, SectionComponent, + ScrollLayoutDirective, } from "@bitwarden/components"; import { PopupRouterCacheService } from "../view-cache/popup-router-cache.service"; @@ -39,6 +39,17 @@ import { PopupTabNavigationComponent } from "./popup-tab-navigation.component"; }) class ExtensionContainerComponent {} +@Component({ + selector: "extension-popped-container", + template: ` +
+ +
+ `, + standalone: true, +}) +class ExtensionPoppedContainerComponent {} + @Component({ selector: "vault-placeholder", template: /*html*/ ` @@ -295,6 +306,7 @@ export default { decorators: [ moduleMetadata({ imports: [ + ScrollLayoutDirective, PopupTabNavigationComponent, PopupHeaderComponent, PopupPageComponent, @@ -302,6 +314,7 @@ export default { CommonModule, RouterModule, ExtensionContainerComponent, + ExtensionPoppedContainerComponent, MockBannerComponent, MockSearchComponent, MockVaultSubpageComponent, @@ -312,6 +325,11 @@ export default { MockVaultPagePoppedComponent, NoItemsModule, VaultComponent, + ScrollingModule, + ItemModule, + SectionComponent, + IconButtonModule, + BadgeModule, ], providers: [ { @@ -495,7 +513,21 @@ export const CompactMode: Story = { const compact = canvasEl.querySelector( `#${containerId} [data-testid=popup-layout-scroll-region]`, ); + + if (!compact) { + // eslint-disable-next-line + console.error(`#${containerId} [data-testid=popup-layout-scroll-region] not found`); + return; + } + const label = canvasEl.querySelector(`#${containerId} .example-label`); + + if (!label) { + // eslint-disable-next-line + console.error(`#${containerId} .example-label not found`); + return; + } + const percentVisible = 100 - Math.round((100 * (compact.scrollHeight - compact.clientHeight)) / compact.scrollHeight); @@ -510,9 +542,9 @@ export const PoppedOut: Story = { render: (args) => ({ props: args, template: /* HTML */ ` -
+ -
+ `, }), }; @@ -560,10 +592,9 @@ export const TransparentHeader: Story = { template: /* HTML */ ` - 🤠 Custom Content - + + 🤠 Custom Content + @@ -608,3 +639,56 @@ export const WidthOptions: Story = { `, }), }; + +export const WithVirtualScrollChild: Story = { + render: (args) => ({ + props: { ...args, data: Array.from(Array(20).keys()) }, + template: /* HTML */ ` + + + + + + @defer (on immediate) { + + + + + + + + + + + + + + + + + + + + } + + + + `, + }), +}; diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index 2313b942a38..b53ef6e97eb 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -1,29 +1,39 @@
+
+
-
- -
+
- +
-
+ -
+ diff --git a/libs/components/src/stories/virtual-scrolling.mdx b/libs/components/src/stories/virtual-scrolling.mdx index 94a86090dce..ab51d9865db 100644 --- a/libs/components/src/stories/virtual-scrolling.mdx +++ b/libs/components/src/stories/virtual-scrolling.mdx @@ -16,7 +16,7 @@ We export a similar directive, `bitScrollLayout`, that integrates with `bit-layo and should be used instead of `scrollWindow`. ```html - + @@ -27,7 +27,10 @@ and should be used instead of `scrollWindow`. Due to the initialization order of Angular components and their templates, `bitScrollLayout` will error if it is used _in the same template_ as the layout component: +With `bit-layout`: + ```html + @@ -35,20 +38,43 @@ error if it is used _in the same template_ as the layout component: ``` +With `popup-page`: + +```html + + + + + + +``` + In this particular composition, the child content gets constructed before the template of -`bit-layout` and thus has no scroll container to reference. Workarounds include: +`bit-layout` (or `popup-page`) and thus has no scroll container to reference. Workarounds include: 1. Wrap the child in another component. (This tends to happen by default when the layout is integrated with a `router-outlet`.) +With `bit-layout`: + ```html ``` +With `popup-page`: + +```html + + + +``` + 2. Use a `defer` block. +With `bit-layout`: + ```html @defer (on immediate) { @@ -58,3 +84,15 @@ In this particular composition, the child content gets constructed before the te } ``` + +With `popup-page`: + +```html + + @defer (on immediate) { + + +
+ } + +``` diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css index 103b90e0752..c8de973c3d1 100644 --- a/libs/components/src/tw-theme.css +++ b/libs/components/src/tw-theme.css @@ -58,6 +58,8 @@ --color-marketing-logo: 23 93 220; --tw-ring-offset-color: #ffffff; + + --tw-sm-breakpoint: 640px; } .theme_light { From a2fd4a37793dfa110b744c01fc023b7b852b02d9 Mon Sep 17 00:00:00 2001 From: Vicki League Date: Mon, 30 Jun 2025 11:03:24 -0400 Subject: [PATCH 47/57] [PM-16291] Prevent parent components from closing when esc key is pressed on select and menu (#15214) --- .../src/menu/menu-trigger-for.directive.ts | 8 ++++++++ .../components/src/select/select.component.html | 1 + libs/components/src/select/select.component.ts | 17 +++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/libs/components/src/menu/menu-trigger-for.directive.ts b/libs/components/src/menu/menu-trigger-for.directive.ts index bc174d14d23..528697600c4 100644 --- a/libs/components/src/menu/menu-trigger-for.directive.ts +++ b/libs/components/src/menu/menu-trigger-for.directive.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { hasModifierKey } from "@angular/cdk/keycodes"; import { Overlay, OverlayConfig, OverlayRef } from "@angular/cdk/overlay"; import { TemplatePortal } from "@angular/cdk/portal"; import { @@ -76,6 +77,13 @@ export class MenuTriggerForDirective implements OnDestroy { this.overlayRef.attach(templatePortal); this.closedEventsSub = this.getClosedEvents().subscribe((event: KeyboardEvent | undefined) => { + // Closing the menu is handled in this.destroyMenu, so we want to prevent the escape key + // from doing its normal default action, which would otherwise cause a parent component + // (like a dialog) or extension window to close + if (event?.key === "Escape" && !hasModifierKey(event)) { + event.preventDefault(); + } + if (["Tab", "Escape"].includes(event?.key)) { // Required to ensure tab order resumes correctly this.elementRef.nativeElement.focus(); diff --git a/libs/components/src/select/select.component.html b/libs/components/src/select/select.component.html index 84de9827b97..6d4c431f234 100644 --- a/libs/components/src/select/select.component.html +++ b/libs/components/src/select/select.component.html @@ -9,6 +9,7 @@ [clearable]="false" (close)="onClose()" appendTo="body" + [keyDownFn]="onKeyDown" >
diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index d2c48bf0f6e..909566bf1f8 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { hasModifierKey } from "@angular/cdk/keycodes"; import { Component, ContentChildren, @@ -185,4 +186,20 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce protected onClose() { this.closed.emit(); } + + /** + * Prevent Escape key press from propagating to parent components + * (for example, parent dialog should not close when Escape is pressed in the select) + * + * @returns true to keep default key behavior; false to prevent default key behavior + * + * Needs to be arrow function to retain `this` scope. + */ + protected onKeyDown = (event: KeyboardEvent) => { + if (this.select.isOpen && event.key === "Escape" && !hasModifierKey(event)) { + event.stopPropagation(); + } + + return true; + }; } From 4bfcc9d076f812cee703d4ed473c4cb17163ebd8 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Mon, 30 Jun 2025 11:32:19 -0400 Subject: [PATCH 48/57] [PM-23008] Error when attempting to export vault (#15397) * moved restrictedItemTypesService creation before export service is created * Fixed cliRestrictedItemTypesService arrangement --- .../service-container/service-container.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index df019520250..f27c69cf47b 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -795,6 +795,17 @@ export class ServiceContainer { this.totpService = new TotpService(this.sdkService); + this.restrictedItemTypesService = new RestrictedItemTypesService( + this.configService, + this.accountService, + this.organizationService, + this.policyService, + ); + + this.cliRestrictedItemTypesService = new CliRestrictedItemTypesService( + this.restrictedItemTypesService, + ); + this.importApiService = new ImportApiService(this.apiService); this.importService = new ImportService( @@ -875,17 +886,6 @@ export class ServiceContainer { ); this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService); - - this.restrictedItemTypesService = new RestrictedItemTypesService( - this.configService, - this.accountService, - this.organizationService, - this.policyService, - ); - - this.cliRestrictedItemTypesService = new CliRestrictedItemTypesService( - this.restrictedItemTypesService, - ); } async logout() { From 80116c7e5453f483f5a03486318e73cbd0cec633 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 1 Jul 2025 02:18:56 +1000 Subject: [PATCH 49/57] Use organizations$ observable instead of class member (#15377) --- .../organizations/members/members.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 633f45ae9a3..0247a8c881b 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -248,10 +248,13 @@ export class MembersComponent extends BaseMembersComponent const separateCustomRolePermissionsEnabled$ = this.configService.getFeatureFlag$( FeatureFlag.SeparateCustomRolePermissions, ); - this.showUserManagementControls$ = separateCustomRolePermissionsEnabled$.pipe( + this.showUserManagementControls$ = combineLatest([ + separateCustomRolePermissionsEnabled$, + organization$, + ]).pipe( map( - (separateCustomRolePermissionsEnabled) => - !separateCustomRolePermissionsEnabled || this.organization.canManageUsers, + ([separateCustomRolePermissionsEnabled, organization]) => + !separateCustomRolePermissionsEnabled || organization.canManageUsers, ), ); } From 62981a1bec37aae5bb0e9d9c489e88bc57e2e95c Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Mon, 30 Jun 2025 13:02:18 -0400 Subject: [PATCH 50/57] Migrate vault filter service from active user state to single user state (#15089) --- .../deprecated-vault-filter.service.ts | 18 +++++++++------ .../components/vault-filter.component.ts | 20 ++++++++++++++--- .../services/vault-filter.service.ts | 22 +++++++++++-------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts b/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts index 21528b1ddd5..9a1a31b6068 100644 --- a/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts +++ b/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts @@ -6,6 +6,7 @@ import { Observable } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { UserId } from "@bitwarden/common/types/guid"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { DynamicTreeNode } from "../vault-filter/models/dynamic-tree-node.model"; @@ -14,11 +15,14 @@ import { DynamicTreeNode } from "../vault-filter/models/dynamic-tree-node.model" * @deprecated August 30 2022: Use new VaultFilterService with observables */ export abstract class DeprecatedVaultFilterService { - buildOrganizations: () => Promise; - buildNestedFolders: (organizationId?: string) => Observable>; - buildCollections: (organizationId?: string) => Promise>; - buildCollapsedFilterNodes: () => Promise>; - storeCollapsedFilterNodes: (collapsedFilterNodes: Set) => Promise; - checkForSingleOrganizationPolicy: () => Promise; - checkForOrganizationDataOwnershipPolicy: () => Promise; + abstract buildOrganizations(): Promise; + abstract buildNestedFolders(organizationId?: string): Observable>; + abstract buildCollections(organizationId?: string): Promise>; + abstract buildCollapsedFilterNodes(userId: UserId): Promise>; + abstract storeCollapsedFilterNodes( + collapsedFilterNodes: Set, + userId: UserId, + ): Promise; + abstract checkForSingleOrganizationPolicy(): Promise; + abstract checkForOrganizationDataOwnershipPolicy(): Promise; } diff --git a/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts b/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts index 936d606b936..0b0cb14bbb8 100644 --- a/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts @@ -7,6 +7,9 @@ import { firstValueFrom, Observable } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; @@ -29,6 +32,8 @@ export class VaultFilterComponent implements OnInit { @Output() onAddFolder = new EventEmitter(); @Output() onEditFolder = new EventEmitter(); + private activeUserId: UserId; + isLoaded = false; collapsedFilterNodes: Set; organizations: Organization[]; @@ -37,14 +42,20 @@ export class VaultFilterComponent implements OnInit { collections: DynamicTreeNode; folders$: Observable>; - constructor(protected vaultFilterService: DeprecatedVaultFilterService) {} + constructor( + protected vaultFilterService: DeprecatedVaultFilterService, + protected accountService: AccountService, + ) {} get displayCollections() { return this.collections?.fullList != null && this.collections.fullList.length > 0; } async ngOnInit(): Promise { - this.collapsedFilterNodes = await this.vaultFilterService.buildCollapsedFilterNodes(); + this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + this.collapsedFilterNodes = await this.vaultFilterService.buildCollapsedFilterNodes( + this.activeUserId, + ); this.organizations = await this.vaultFilterService.buildOrganizations(); if (this.organizations != null && this.organizations.length > 0) { this.activeOrganizationDataOwnershipPolicy = @@ -68,7 +79,10 @@ export class VaultFilterComponent implements OnInit { } else { this.collapsedFilterNodes.add(node.id); } - await this.vaultFilterService.storeCollapsedFilterNodes(this.collapsedFilterNodes); + await this.vaultFilterService.storeCollapsedFilterNodes( + this.collapsedFilterNodes, + this.activeUserId, + ); } async applyFilter(filter: VaultFilter) { diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts index a4114e63285..3317f0c9002 100644 --- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts +++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts @@ -12,7 +12,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; +import { SingleUserState, StateProvider } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -28,10 +28,9 @@ const NestingDelimiter = "/"; @Injectable() export class VaultFilterService implements DeprecatedVaultFilterServiceAbstraction { - private collapsedGroupingsState: ActiveUserState = - this.stateProvider.getActive(COLLAPSED_GROUPINGS); - private readonly collapsedGroupings$: Observable> = - this.collapsedGroupingsState.state$.pipe(map((c) => new Set(c))); + private collapsedGroupingsState(userId: UserId): SingleUserState { + return this.stateProvider.getUser(userId, COLLAPSED_GROUPINGS); + } constructor( protected organizationService: OrganizationService, @@ -43,12 +42,17 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti protected accountService: AccountService, ) {} - async storeCollapsedFilterNodes(collapsedFilterNodes: Set): Promise { - await this.collapsedGroupingsState.update(() => Array.from(collapsedFilterNodes)); + async storeCollapsedFilterNodes( + collapsedFilterNodes: Set, + userId: UserId, + ): Promise { + await this.collapsedGroupingsState(userId).update(() => Array.from(collapsedFilterNodes)); } - async buildCollapsedFilterNodes(): Promise> { - return await firstValueFrom(this.collapsedGroupings$); + async buildCollapsedFilterNodes(userId: UserId): Promise> { + return await firstValueFrom( + this.collapsedGroupingsState(userId).state$.pipe(map((c) => new Set(c))), + ); } async buildOrganizations(): Promise { From ea5224da259b24442d7746f38b89db22196f2587 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Mon, 30 Jun 2025 13:04:01 -0400 Subject: [PATCH 51/57] Properly converted date strings during imports and provided default values (#15398) --- libs/common/src/models/export/cipher.export.ts | 6 +++--- libs/common/src/vault/models/domain/cipher.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/common/src/models/export/cipher.export.ts b/libs/common/src/models/export/cipher.export.ts index 7d0ef9e9c34..fa050375391 100644 --- a/libs/common/src/models/export/cipher.export.ts +++ b/libs/common/src/models/export/cipher.export.ts @@ -124,9 +124,9 @@ export class CipherExport { domain.passwordHistory = req.passwordHistory.map((ph) => PasswordHistoryExport.toDomain(ph)); } - domain.creationDate = req.creationDate; - domain.revisionDate = req.revisionDate; - domain.deletedDate = req.deletedDate; + domain.creationDate = req.creationDate ? new Date(req.creationDate) : null; + domain.revisionDate = req.revisionDate ? new Date(req.revisionDate) : null; + domain.deletedDate = req.deletedDate ? new Date(req.deletedDate) : null; return domain; } diff --git a/libs/common/src/vault/models/domain/cipher.ts b/libs/common/src/vault/models/domain/cipher.ts index e6d11a82b69..9cc9226cd46 100644 --- a/libs/common/src/vault/models/domain/cipher.ts +++ b/libs/common/src/vault/models/domain/cipher.ts @@ -353,14 +353,14 @@ export class Cipher extends Domain implements Decryptable { type: this.type, favorite: this.favorite ?? false, organizationUseTotp: this.organizationUseTotp ?? false, - edit: this.edit, + edit: this.edit ?? true, permissions: this.permissions ? { delete: this.permissions.delete, restore: this.permissions.restore, } : undefined, - viewPassword: this.viewPassword, + viewPassword: this.viewPassword ?? true, localData: this.localData ? { lastUsedDate: this.localData.lastUsedDate From 64eb9b56d7634cc6804f3125b14781e363499f6e Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:47:31 -0400 Subject: [PATCH 52/57] feat(anon-layout): [Auth/PM-17737] AnonLayout - Consolidate and extend max width control (#15362) * PM-17737 - feat(anon-layout-width) - consolidate width setting to control title, subtitle, and content together for a unified UX. Removes all titleAreaMaxWidth usages and the titleAreaMaxWidth property entirely. * PM-17737 - AnonLayout - consolidate stories into playground (w/ the exception of default icon story b/c I can't figure how to omit the icon conditionally). * PM-17737 - AnonLayoutStories - show secondary content by default * PM-17737 - Per PR feedback, adjust maxWidthClass logic to remove string concatenation as it can result in tailwind classes not being compiled and available at run time. * PM-17737 - Per PR feedback, refactor stories to use configuration to generate all the scenarios so we can still track them via snapshots * PM-17737 - Fix anon layout mdx reference * PM-17737 - Make AnonLayoutMaxWidths singular * PM-17737 - When inside of a max width container, the icon container needs explicit width to be able to scale viewbox icons that don't have defined heights / widths --- apps/web/src/app/oss-routing.module.ts | 2 - .../anon-layout-wrapper.component.html | 1 - .../anon-layout-wrapper.component.ts | 13 +- .../anon-layout/anon-layout.component.html | 11 +- .../src/anon-layout/anon-layout.component.ts | 31 +- .../src/anon-layout/anon-layout.mdx | 2 +- .../src/anon-layout/anon-layout.stories.ts | 340 +++++++++--------- 7 files changed, 198 insertions(+), 202 deletions(-) diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 783fe6ada0a..0733d1ef289 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -347,7 +347,6 @@ const routes: Routes = [ pageSubtitle: { key: "singleSignOnEnterOrgIdentifierText", }, - titleAreaMaxWidth: "md", pageIcon: SsoKeyIcon, } satisfies RouteDataProperties & AnonLayoutWrapperData, children: [ @@ -381,7 +380,6 @@ const routes: Routes = [ pageTitle: { key: "verifyYourIdentity", }, - titleAreaMaxWidth: "md", } satisfies RouteDataProperties & AnonLayoutWrapperData, }, { diff --git a/libs/components/src/anon-layout/anon-layout-wrapper.component.html b/libs/components/src/anon-layout/anon-layout-wrapper.component.html index 2cba7ca7783..0d393b30362 100644 --- a/libs/components/src/anon-layout/anon-layout-wrapper.component.html +++ b/libs/components/src/anon-layout/anon-layout-wrapper.component.html @@ -4,7 +4,6 @@ [icon]="pageIcon" [showReadonlyHostname]="showReadonlyHostname" [maxWidth]="maxWidth" - [titleAreaMaxWidth]="titleAreaMaxWidth" [hideCardWrapper]="hideCardWrapper" > diff --git a/libs/components/src/anon-layout/anon-layout-wrapper.component.ts b/libs/components/src/anon-layout/anon-layout-wrapper.component.ts index ea6a518f70d..20380f137a6 100644 --- a/libs/components/src/anon-layout/anon-layout-wrapper.component.ts +++ b/libs/components/src/anon-layout/anon-layout-wrapper.component.ts @@ -10,7 +10,7 @@ import { Translation } from "../dialog"; import { Icon } from "../icon"; import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service"; -import { AnonLayoutComponent } from "./anon-layout.component"; +import { AnonLayoutComponent, AnonLayoutMaxWidth } from "./anon-layout.component"; export interface AnonLayoutWrapperData { /** @@ -36,11 +36,7 @@ export interface AnonLayoutWrapperData { /** * Optional flag to set the max-width of the page. Defaults to 'md' if not provided. */ - maxWidth?: "md" | "3xl"; - /** - * Optional flag to set the max-width of the title area. Defaults to null if not provided. - */ - titleAreaMaxWidth?: "md"; + maxWidth?: AnonLayoutMaxWidth; /** * Hide the card that wraps the default content. Defaults to false. */ @@ -58,8 +54,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { protected pageSubtitle: string; protected pageIcon: Icon; protected showReadonlyHostname: boolean; - protected maxWidth: "md" | "3xl"; - protected titleAreaMaxWidth: "md"; + protected maxWidth: AnonLayoutMaxWidth; protected hideCardWrapper: boolean; constructor( @@ -111,7 +106,6 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]); this.maxWidth = firstChildRouteData["maxWidth"]; - this.titleAreaMaxWidth = firstChildRouteData["titleAreaMaxWidth"]; this.hideCardWrapper = Boolean(firstChildRouteData["hideCardWrapper"]); } @@ -174,7 +168,6 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { this.pageIcon = null; this.showReadonlyHostname = null; this.maxWidth = null; - this.titleAreaMaxWidth = null; this.hideCardWrapper = null; } diff --git a/libs/components/src/anon-layout/anon-layout.component.html b/libs/components/src/anon-layout/anon-layout.component.html index a8e091e6e72..4dfde5e7ef3 100644 --- a/libs/components/src/anon-layout/anon-layout.component.html +++ b/libs/components/src/anon-layout/anon-layout.component.html @@ -13,11 +13,8 @@ -
-
+
+
@@ -36,8 +33,8 @@
@if (hideCardWrapper) {
diff --git a/libs/components/src/anon-layout/anon-layout.component.ts b/libs/components/src/anon-layout/anon-layout.component.ts index abde48649af..ee3a7ca7bee 100644 --- a/libs/components/src/anon-layout/anon-layout.component.ts +++ b/libs/components/src/anon-layout/anon-layout.component.ts @@ -14,6 +14,8 @@ import { BitwardenLogo, BitwardenShield } from "../icon/icons"; import { SharedModule } from "../shared"; import { TypographyModule } from "../typography"; +export type AnonLayoutMaxWidth = "md" | "lg" | "xl" | "2xl" | "3xl"; + @Component({ selector: "auth-anon-layout", templateUrl: "./anon-layout.component.html", @@ -36,27 +38,35 @@ export class AnonLayoutComponent implements OnInit, OnChanges { @Input() hideCardWrapper: boolean = false; /** - * Max width of the title area content - * - * @default null - */ - @Input() titleAreaMaxWidth?: "md"; - - /** - * Max width of the layout content + * Max width of the anon layout title, subtitle, and content areas. * * @default 'md' */ - @Input() maxWidth: "md" | "3xl" = "md"; + @Input() maxWidth: AnonLayoutMaxWidth = "md"; protected logo = BitwardenLogo; - protected year = "2024"; + protected year: string; protected clientType: ClientType; protected hostname: string; protected version: string; protected hideYearAndVersion = false; + get maxWidthClass(): string { + switch (this.maxWidth) { + case "md": + return "tw-max-w-md"; + case "lg": + return "tw-max-w-lg"; + case "xl": + return "tw-max-w-xl"; + case "2xl": + return "tw-max-w-2xl"; + case "3xl": + return "tw-max-w-3xl"; + } + } + constructor( private environmentService: EnvironmentService, private platformUtilsService: PlatformUtilsService, @@ -68,7 +78,6 @@ export class AnonLayoutComponent implements OnInit, OnChanges { async ngOnInit() { this.maxWidth = this.maxWidth ?? "md"; - this.titleAreaMaxWidth = this.titleAreaMaxWidth ?? null; this.hostname = (await firstValueFrom(this.environmentService.environment$)).getHostname(); this.version = await this.platformUtilsService.getApplicationVersion(); diff --git a/libs/components/src/anon-layout/anon-layout.mdx b/libs/components/src/anon-layout/anon-layout.mdx index 039a1aa5f28..9d40d617b0d 100644 --- a/libs/components/src/anon-layout/anon-layout.mdx +++ b/libs/components/src/anon-layout/anon-layout.mdx @@ -165,4 +165,4 @@ import { EnvironmentSelectorComponent } from "./components/environment-selector/ --- - + diff --git a/libs/components/src/anon-layout/anon-layout.stories.ts b/libs/components/src/anon-layout/anon-layout.stories.ts index 1f4ac5bb14f..24aaf10f7ba 100644 --- a/libs/components/src/anon-layout/anon-layout.stories.ts +++ b/libs/components/src/anon-layout/anon-layout.stories.ts @@ -8,6 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ButtonModule } from "../button"; +import { Icon } from "../icon"; import { LockIcon } from "../icon/icons"; import { I18nMockService } from "../utils/i18n-mock.service"; @@ -18,6 +19,23 @@ class MockPlatformUtilsService implements Partial { getClientType = () => ClientType.Web; } +type StoryArgs = Pick< + AnonLayoutComponent, + | "title" + | "subtitle" + | "showReadonlyHostname" + | "hideCardWrapper" + | "hideIcon" + | "hideLogo" + | "hideFooter" + | "maxWidth" +> & { + contentLength: "normal" | "long" | "thin"; + showSecondary: boolean; + useDefaultIcon: boolean; + icon: Icon; +}; + export default { title: "Component Library/Anon Layout", component: AnonLayoutComponent, @@ -31,12 +49,11 @@ export default { }, { provide: I18nService, - useFactory: () => { - return new I18nMockService({ + useFactory: () => + new I18nMockService({ accessing: "Accessing", appLogoLabel: "app logo label", - }); - }, + }), }, { provide: EnvironmentService, @@ -55,196 +72,179 @@ export default { ], }), ], + + render: (args: StoryArgs) => { + const { useDefaultIcon, icon, ...rest } = args; + return { + props: { + ...rest, + icon: useDefaultIcon ? null : icon, + }, + template: ` + + +
Thin Content
+
+
Long Content
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
+
+
+
Normal Content
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+
+ +
+
+ Secondary Projected Content (optional) +
+ +
+
+ `, + }; + }, + + argTypes: { + title: { control: "text" }, + subtitle: { control: "text" }, + + icon: { control: false, table: { disable: true } }, + useDefaultIcon: { + control: false, + table: { disable: true }, + description: "If true, passes null so component falls back to its built-in icon", + }, + + showReadonlyHostname: { control: "boolean" }, + maxWidth: { + control: "select", + options: ["md", "lg", "xl", "2xl", "3xl"], + }, + + hideCardWrapper: { control: "boolean" }, + hideIcon: { control: "boolean" }, + hideLogo: { control: "boolean" }, + hideFooter: { control: "boolean" }, + + contentLength: { + control: "radio", + options: ["normal", "long", "thin"], + }, + + showSecondary: { control: "boolean" }, + }, + args: { title: "The Page Title", subtitle: "The subtitle (optional)", - showReadonlyHostname: true, icon: LockIcon, - hideLogo: false, + useDefaultIcon: false, + showReadonlyHostname: false, + maxWidth: "md", hideCardWrapper: false, + hideIcon: false, + hideLogo: false, + hideFooter: false, + contentLength: "normal", + showSecondary: false, }, -} as Meta; +} as Meta; -type Story = StoryObj; +type Story = StoryObj; -export const WithPrimaryContent: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const NormalPrimaryContent: Story = { + args: { + contentLength: "normal", + }, }; -export const WithSecondaryContent: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
's) and styling is just a sample and can be replaced with any content/styling. - // Notice that slot="secondary" is requred to project any secondary content. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
- -
-
Secondary Projected Content (optional)
- -
-
- `, - }), +export const LongPrimaryContent: Story = { + args: { + contentLength: "long", + }, }; -export const WithLongContent: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
's) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam? Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit. Lorem ipsum dolor sit amet consectetur adipisicing elit.
-
- -
-
Secondary Projected Content (optional)
-

Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias laborum nostrum natus. Expedita, quod est?

- -
-
- `, - }), +export const ThinPrimaryContent: Story = { + args: { + contentLength: "thin", + }, }; -export const WithThinPrimaryContent: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
's) and styling is just a sample and can be replaced with any content/styling. - ` - -
Lorem ipsum
- -
-
Secondary Projected Content (optional)
- -
-
- `, - }), +export const LongContentAndTitlesAndDefaultWidth: Story = { + args: { + title: + "This is a very long title that might not fit in the default width. It's really long and descriptive, so it might take up more space than usual.", + subtitle: + "This is a very long subtitle that might not fit in the default width. It's really long and descriptive, so it might take up more space than usual.", + contentLength: "long", + }, }; -export const WithCustomIcon: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const LongContentAndTitlesAndLargestWidth: Story = { + args: { + title: + "This is a very long title that might not fit in the default width. It's really long and descriptive, so it might take up more space than usual.", + subtitle: + "This is a very long subtitle that might not fit in the default width. It's really long and descriptive, so it might take up more space than usual.", + contentLength: "long", + maxWidth: "3xl", + }, }; -export const HideCardWrapper: Story = { - render: (args) => ({ - props: { - ...args, - hideCardWrapper: true, - }, - template: ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
-
Secondary Projected Content (optional)
- -
-
- `, - }), +export const SecondaryContent: Story = { + args: { + showSecondary: true, + }, }; -export const HideIcon: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const NoTitle: Story = { args: { title: undefined } }; + +export const NoSubtitle: Story = { args: { subtitle: undefined } }; + +export const NoWrapper: Story = { + args: { hideCardWrapper: true }, }; -export const HideLogo: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const DefaultIcon: Story = { + args: { useDefaultIcon: true }, }; -export const HideFooter: Story = { - render: (args) => ({ - props: args, - template: - // Projected content (the
) and styling is just a sample and can be replaced with any content/styling. - ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const NoIcon: Story = { + args: { hideIcon: true }, }; -export const WithTitleAreaMaxWidth: Story = { - render: (args) => ({ - props: { - ...args, - title: "This is a very long long title to demonstrate titleAreaMaxWidth set to 'md'", - subtitle: - "This is a very long subtitle that demonstrates how the max width container handles longer text content with the titleAreaMaxWidth input set to 'md'. Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, quod est?", - }, - template: ` - -
-
Primary Projected Content Area (customizable)
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus illum vero, placeat recusandae esse ratione eius minima veniam nemo, quas beatae! Impedit molestiae alias sapiente explicabo. Sapiente corporis ipsa numquam?
-
-
- `, - }), +export const NoLogo: Story = { + args: { hideLogo: true }, +}; + +export const NoFooter: Story = { + args: { hideFooter: true }, +}; + +export const ReadonlyHostname: Story = { + args: { showReadonlyHostname: true }, +}; + +export const MinimalState: Story = { + args: { + title: undefined, + subtitle: undefined, + contentLength: "normal", + hideCardWrapper: true, + hideIcon: true, + hideLogo: true, + hideFooter: true, + }, }; From 782dc930ad16aaa9b94c3663b98b9b985497de96 Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Mon, 30 Jun 2025 13:56:26 -0400 Subject: [PATCH 53/57] refactor(storage-test-utils): cut a new library for storage test tools (#15259) * refactor(platform): generate a storage-test-utils library * refactor(storage-test-utils): move FakeStorageService out of common --- .github/CODEOWNERS | 1 + libs/common/spec/fake-storage.service.ts | 120 +----------------- libs/storage-test-utils/README.md | 5 + libs/storage-test-utils/eslint.config.mjs | 3 + libs/storage-test-utils/jest.config.js | 10 ++ libs/storage-test-utils/package.json | 11 ++ libs/storage-test-utils/project.json | 33 +++++ .../src/fake-storage.service.ts | 119 +++++++++++++++++ libs/storage-test-utils/src/index.ts | 1 + .../src/storage-test-utils.spec.ts | 8 ++ libs/storage-test-utils/tsconfig.json | 13 ++ libs/storage-test-utils/tsconfig.lib.json | 10 ++ libs/storage-test-utils/tsconfig.spec.json | 10 ++ package-lock.json | 11 +- tsconfig.base.json | 1 + 15 files changed, 235 insertions(+), 121 deletions(-) create mode 100644 libs/storage-test-utils/README.md create mode 100644 libs/storage-test-utils/eslint.config.mjs create mode 100644 libs/storage-test-utils/jest.config.js create mode 100644 libs/storage-test-utils/package.json create mode 100644 libs/storage-test-utils/project.json create mode 100644 libs/storage-test-utils/src/fake-storage.service.ts create mode 100644 libs/storage-test-utils/src/index.ts create mode 100644 libs/storage-test-utils/src/storage-test-utils.spec.ts create mode 100644 libs/storage-test-utils/tsconfig.json create mode 100644 libs/storage-test-utils/tsconfig.lib.json create mode 100644 libs/storage-test-utils/tsconfig.spec.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 17a1cb5720e..db60ad6a93b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -91,6 +91,7 @@ libs/common/spec @bitwarden/team-platform-dev libs/common/src/state-migrations @bitwarden/team-platform-dev libs/platform @bitwarden/team-platform-dev libs/storage-core @bitwarden/team-platform-dev +libs/storage-test-utils @bitwarden/team-platform-dev # Web utils used across app and connectors apps/web/src/utils/ @bitwarden/team-platform-dev # Web core and shared files diff --git a/libs/common/spec/fake-storage.service.ts b/libs/common/spec/fake-storage.service.ts index c6d989c5abf..1eae3dbfbe3 100644 --- a/libs/common/spec/fake-storage.service.ts +++ b/libs/common/spec/fake-storage.service.ts @@ -1,119 +1 @@ -import { MockProxy, mock } from "jest-mock-extended"; -import { Subject } from "rxjs"; - -import { - AbstractStorageService, - ObservableStorageService, - StorageUpdate, -} from "../src/platform/abstractions/storage.service"; -import { StorageOptions } from "../src/platform/models/domain/storage-options"; - -const INTERNAL_KEY = "__internal__"; - -export class FakeStorageService implements AbstractStorageService, ObservableStorageService { - private store: Record; - private updatesSubject = new Subject(); - private _valuesRequireDeserialization = false; - - /** - * Returns a mock of a {@see AbstractStorageService} for asserting the expected - * amount of calls. It is not recommended to use this to mock implementations as - * they are not respected. - */ - mock: MockProxy; - - constructor(initial?: Record) { - this.store = initial ?? {}; - this.mock = mock(); - } - - /** - * Updates the internal store for this fake implementation, this bypasses any mock calls - * or updates to the {@link updates$} observable. - * @param store - */ - internalUpdateStore(store: Record) { - this.store = store; - } - - get internalStore() { - return this.store; - } - - internalUpdateValuesRequireDeserialization(value: boolean) { - this._valuesRequireDeserialization = value; - } - - get valuesRequireDeserialization(): boolean { - return this._valuesRequireDeserialization; - } - - get updates$() { - return this.updatesSubject.asObservable(); - } - - get(key: string, options?: StorageOptions): Promise { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.mock.get(key, options); - const value = this.store[key] as T; - return Promise.resolve(value); - } - has(key: string, options?: StorageOptions): Promise { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.mock.has(key, options); - return Promise.resolve(this.store[key] != null); - } - async save(key: string, obj: T, options?: StorageOptions): Promise { - // These exceptions are copied from https://github.com/sindresorhus/conf/blob/608adb0c46fb1680ddbd9833043478367a64c120/source/index.ts#L193-L203 - // which is a library that is used by `ElectronStorageService`. We add them here to ensure that the behavior in our testing mirrors the real world. - if (typeof key !== "string" && typeof key !== "object") { - throw new TypeError( - `Expected \`key\` to be of type \`string\` or \`object\`, got ${typeof key}`, - ); - } - - // We don't throw this error because ElectronStorageService automatically detects this case - // and calls `delete()` instead of `set()`. - // if (typeof key !== "object" && obj === undefined) { - // throw new TypeError("Use `delete()` to clear values"); - // } - - if (this._containsReservedKey(key)) { - throw new TypeError( - `Please don't use the ${INTERNAL_KEY} key, as it's used to manage this module internal operations.`, - ); - } - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.mock.save(key, obj, options); - this.store[key] = obj; - this.updatesSubject.next({ key: key, updateType: "save" }); - } - remove(key: string, options?: StorageOptions): Promise { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.mock.remove(key, options); - delete this.store[key]; - this.updatesSubject.next({ key: key, updateType: "remove" }); - return Promise.resolve(); - } - - private _containsReservedKey(key: string | Partial): boolean { - if (typeof key === "object") { - const firsKey = Object.keys(key)[0]; - - if (firsKey === INTERNAL_KEY) { - return true; - } - } - - if (typeof key !== "string") { - return false; - } - - return false; - } -} +export { FakeStorageService } from "@bitwarden/storage-test-utils"; diff --git a/libs/storage-test-utils/README.md b/libs/storage-test-utils/README.md new file mode 100644 index 00000000000..2be8817e402 --- /dev/null +++ b/libs/storage-test-utils/README.md @@ -0,0 +1,5 @@ +# storage-test-utils + +Owned by: platform + +Test tools for the storage library diff --git a/libs/storage-test-utils/eslint.config.mjs b/libs/storage-test-utils/eslint.config.mjs new file mode 100644 index 00000000000..9c37d10e3ff --- /dev/null +++ b/libs/storage-test-utils/eslint.config.mjs @@ -0,0 +1,3 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [...baseConfig]; diff --git a/libs/storage-test-utils/jest.config.js b/libs/storage-test-utils/jest.config.js new file mode 100644 index 00000000000..a145b5b2f4c --- /dev/null +++ b/libs/storage-test-utils/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + displayName: "storage-test-utils", + preset: "../../jest.preset.js", + testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], + }, + moduleFileExtensions: ["ts", "js", "html"], + coverageDirectory: "../../coverage/libs/storage-test-utils", +}; diff --git a/libs/storage-test-utils/package.json b/libs/storage-test-utils/package.json new file mode 100644 index 00000000000..22d83f2334e --- /dev/null +++ b/libs/storage-test-utils/package.json @@ -0,0 +1,11 @@ +{ + "name": "@bitwarden/storage-test-utils", + "version": "0.0.1", + "description": "Test tools for the storage library", + "private": true, + "type": "commonjs", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "GPL-3.0", + "author": "platform" +} diff --git a/libs/storage-test-utils/project.json b/libs/storage-test-utils/project.json new file mode 100644 index 00000000000..f1aad63c9e3 --- /dev/null +++ b/libs/storage-test-utils/project.json @@ -0,0 +1,33 @@ +{ + "name": "storage-test-utils", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/storage-test-utils/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/storage-test-utils", + "main": "libs/storage-test-utils/src/index.ts", + "tsConfig": "libs/storage-test-utils/tsconfig.lib.json", + "assets": ["libs/storage-test-utils/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/storage-test-utils/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/storage-test-utils/jest.config.js" + } + } + } +} diff --git a/libs/storage-test-utils/src/fake-storage.service.ts b/libs/storage-test-utils/src/fake-storage.service.ts new file mode 100644 index 00000000000..aa902cf0da8 --- /dev/null +++ b/libs/storage-test-utils/src/fake-storage.service.ts @@ -0,0 +1,119 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { Subject } from "rxjs"; + +import { + AbstractStorageService, + ObservableStorageService, + StorageUpdate, + StorageOptions, +} from "@bitwarden/storage-core"; + +const INTERNAL_KEY = "__internal__"; + +export class FakeStorageService implements AbstractStorageService, ObservableStorageService { + private store: Record; + private updatesSubject = new Subject(); + private _valuesRequireDeserialization = false; + + /** + * Returns a mock of a {@see AbstractStorageService} for asserting the expected + * amount of calls. It is not recommended to use this to mock implementations as + * they are not respected. + */ + mock: MockProxy; + + constructor(initial?: Record) { + this.store = initial ?? {}; + this.mock = mock(); + } + + /** + * Updates the internal store for this fake implementation, this bypasses any mock calls + * or updates to the {@link updates$} observable. + * @param store + */ + internalUpdateStore(store: Record) { + this.store = store; + } + + get internalStore() { + return this.store; + } + + internalUpdateValuesRequireDeserialization(value: boolean) { + this._valuesRequireDeserialization = value; + } + + get valuesRequireDeserialization(): boolean { + return this._valuesRequireDeserialization; + } + + get updates$() { + return this.updatesSubject.asObservable(); + } + + get(key: string, options?: StorageOptions): Promise { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.mock.get(key, options); + const value = this.store[key] as T; + return Promise.resolve(value); + } + has(key: string, options?: StorageOptions): Promise { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.mock.has(key, options); + return Promise.resolve(this.store[key] != null); + } + async save(key: string, obj: T, options?: StorageOptions): Promise { + // These exceptions are copied from https://github.com/sindresorhus/conf/blob/608adb0c46fb1680ddbd9833043478367a64c120/source/index.ts#L193-L203 + // which is a library that is used by `ElectronStorageService`. We add them here to ensure that the behavior in our testing mirrors the real world. + if (typeof key !== "string" && typeof key !== "object") { + throw new TypeError( + `Expected \`key\` to be of type \`string\` or \`object\`, got ${typeof key}`, + ); + } + + // We don't throw this error because ElectronStorageService automatically detects this case + // and calls `delete()` instead of `set()`. + // if (typeof key !== "object" && obj === undefined) { + // throw new TypeError("Use `delete()` to clear values"); + // } + + if (this._containsReservedKey(key)) { + throw new TypeError( + `Please don't use the ${INTERNAL_KEY} key, as it's used to manage this module internal operations.`, + ); + } + + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.mock.save(key, obj, options); + this.store[key] = obj; + this.updatesSubject.next({ key: key, updateType: "save" }); + } + remove(key: string, options?: StorageOptions): Promise { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.mock.remove(key, options); + delete this.store[key]; + this.updatesSubject.next({ key: key, updateType: "remove" }); + return Promise.resolve(); + } + + private _containsReservedKey(key: string | Partial): boolean { + if (typeof key === "object") { + const firsKey = Object.keys(key)[0]; + + if (firsKey === INTERNAL_KEY) { + return true; + } + } + + if (typeof key !== "string") { + return false; + } + + return false; + } +} diff --git a/libs/storage-test-utils/src/index.ts b/libs/storage-test-utils/src/index.ts new file mode 100644 index 00000000000..dc5fdc1125b --- /dev/null +++ b/libs/storage-test-utils/src/index.ts @@ -0,0 +1 @@ +export * from "./fake-storage.service"; diff --git a/libs/storage-test-utils/src/storage-test-utils.spec.ts b/libs/storage-test-utils/src/storage-test-utils.spec.ts new file mode 100644 index 00000000000..c323d4ce386 --- /dev/null +++ b/libs/storage-test-utils/src/storage-test-utils.spec.ts @@ -0,0 +1,8 @@ +import * as lib from "./index"; + +describe("storage-test-utils", () => { + // This test will fail until something is exported from index.ts + it("should work", () => { + expect(lib).toBeDefined(); + }); +}); diff --git a/libs/storage-test-utils/tsconfig.json b/libs/storage-test-utils/tsconfig.json new file mode 100644 index 00000000000..62ebbd94647 --- /dev/null +++ b/libs/storage-test-utils/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/storage-test-utils/tsconfig.lib.json b/libs/storage-test-utils/tsconfig.lib.json new file mode 100644 index 00000000000..9cbf6736007 --- /dev/null +++ b/libs/storage-test-utils/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.js", "src/**/*.spec.ts"] +} diff --git a/libs/storage-test-utils/tsconfig.spec.json b/libs/storage-test-utils/tsconfig.spec.json new file mode 100644 index 00000000000..901c72378dd --- /dev/null +++ b/libs/storage-test-utils/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../..//dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/package-lock.json b/package-lock.json index 0855df67e8b..176aa40a650 100644 --- a/package-lock.json +++ b/package-lock.json @@ -250,8 +250,6 @@ }, "apps/cli/node_modules/is-docker": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "license": "MIT", "bin": { "is-docker": "cli.js" @@ -375,6 +373,11 @@ "version": "0.0.1", "license": "GPL-3.0" }, + "libs/storage-test-utils": { + "name": "@bitwarden/storage-test-utils", + "version": "0.0.1", + "license": "GPL-3.0" + }, "libs/tools/export/vault-export/vault-export-core": { "name": "@bitwarden/vault-export-core", "version": "0.0.0", @@ -4621,6 +4624,10 @@ "resolved": "libs/storage-core", "link": true }, + "node_modules/@bitwarden/storage-test-utils": { + "resolved": "libs/storage-test-utils", + "link": true + }, "node_modules/@bitwarden/ui-common": { "resolved": "libs/ui/common", "link": true diff --git a/tsconfig.base.json b/tsconfig.base.json index fd3f898b319..b826d51e66e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -43,6 +43,7 @@ "@bitwarden/platform/*": ["./libs/platform/src/*"], "@bitwarden/send-ui": ["./libs/tools/send/send-ui/src"], "@bitwarden/storage-core": ["libs/storage-core/src/index.ts"], + "@bitwarden/storage-test-utils": ["libs/storage-test-utils/src/index.ts"], "@bitwarden/ui-common": ["./libs/ui/common/src"], "@bitwarden/ui-common/setup-jest": ["./libs/ui/common/src/setup-jest"], "@bitwarden/vault": ["./libs/vault/src"], From 7eb7507229cec9dbf0fe0a2c69139970cd970ac5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 30 Jun 2025 20:04:31 +0200 Subject: [PATCH 54/57] Enable ptrace prevention on Linux (except snap) (#15204) --- apps/desktop/src/main/window.main.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index f1a55866079..4d9438b588d 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -16,7 +16,15 @@ import { BiometricStateService } from "@bitwarden/key-management"; import { WindowState } from "../platform/models/domain/window-state"; import { applyMainWindowStyles, applyPopupModalStyles } from "../platform/popup-modal-styles"; import { DesktopSettingsService } from "../platform/services/desktop-settings.service"; -import { cleanUserAgent, isDev, isLinux, isMac, isMacAppStore, isWindows } from "../utils"; +import { + cleanUserAgent, + isDev, + isLinux, + isMac, + isMacAppStore, + isSnapStore, + isWindows, +} from "../utils"; const mainWindowSizeKey = "mainWindowSize"; const WindowEventHandlingDelay = 100; @@ -156,9 +164,8 @@ export class WindowMain { } } - // this currently breaks the file portal, so should only be used when - // no files are needed but security requirements are super high https://github.com/flatpak/xdg-desktop-portal/issues/785 - if (process.env.EXPERIMENTAL_PREVENT_DEBUGGER_MEMORY_ACCESS === "true") { + // this currently breaks the file portal for snap https://github.com/flatpak/xdg-desktop-portal/issues/785 + if (!isSnapStore()) { this.logService.info("Disabling memory dumps in main process"); try { await processisolations.disableMemoryAccess(); From f9d0e6fe4a89834c458d59cfcb1029fcf54462e5 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Mon, 30 Jun 2025 15:10:01 -0400 Subject: [PATCH 55/57] [CL-572] adding new colors for icons, update icon docs (#15367) * adding new colors for icons, update icon docs, remove art from colors mdx --- libs/components/src/icon/icon.mdx | 24 +++++++++++++----------- libs/components/src/stories/colors.mdx | 10 ++++++++-- libs/components/src/tw-theme.css | 16 ++++++++++++++++ libs/components/tailwind.config.base.js | 9 +++++++++ 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/libs/components/src/icon/icon.mdx b/libs/components/src/icon/icon.mdx index d1809c81cd2..01f03d1861b 100644 --- a/libs/components/src/icon/icon.mdx +++ b/libs/components/src/icon/icon.mdx @@ -41,12 +41,14 @@ import { IconModule } from "@bitwarden/components"; - A non-comprehensive list of common colors and their associated classes is below: - | Hardcoded Value | Tailwind Stroke Class | Tailwind Fill Class | Tailwind Variable | - | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----------------------- | ----------------------- | - | `#020F66` | `tw-stroke-art-primary` | `tw-fill-art-primary` | `--color-art-primary` | - | `#10949D` | `tw-stroke-art-accent` | `tw-fill-art-accent` | `--color-art-accent` | - | `#2CDDE9` | `tw-stroke-art-accent` | `tw-fill-art-accent` | `--color-art-accent` | - | `#89929F` | `tw-stroke-secondary-600` | `tw-fill-secondary-600` | `--color-secondary-600` | + | Hardcoded Value | Tailwind Stroke Class | Tailwind Fill Class | Tailwind Variable | + | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ----------------------------------- | ----------------------------------- | + | `#020F66` | `tw-stroke-illustration-outline` | `tw-fill-illustration-outline` | `--color-illustration-outline` | + | `#DBE5F6` | `tw-stroke-illustration-bg-primary` | `tw-fill-illustration-bg-primary` | `--color-illustration-bg-primary` | + | `#AAC3EF` | `tw-stroke-illustration-bg-secondary` | `tw-fill-illustration-bg-secondary` | `--color-illustration-bg-secondary` | + | `#FFFFFF` | `tw-stroke-illustration-bg-tertiary` | `tw-fill-illustration-bg-tertiary` | `--color-illustration-bg-tertiary` | + | `#FFBF00` | `tw-stroke-illustration-tertiary` | `tw-fill-illustration-tertiary` | `--color-illustration-tertiary` | + | `#175DDC` | `tw-stroke-illustration-logo` | `tw-fill-illustration-logo` | `--color-illustration-logo` | - If the hex that you have on an SVG path is not listed above, there are a few ways to figure out the appropriate Tailwind class: @@ -56,20 +58,20 @@ import { IconModule } from "@bitwarden/components"; - Click on an individual path on the SVG until you see the path's properties in the right-hand panel. - Scroll down to the Colors section. - - Example: `Color/Art/Primary` + - Example: `Color/Illustration/Outline` - This also includes Hex or RGB values that can be used to find the appropriate Tailwind variable as well if you follow the manual search option below. - Create the appropriate stroke or fill class from the color used. - - Example: `Color/Art/Primary` corresponds to `--color-art-primary` which corresponds to - `tw-stroke-art-primary` or `tw-fill-art-primary`. + - Example: `Color/Illustration/Outline` corresponds to `--color-illustration-outline` which + corresponds to `tw-stroke-illustration-outline` or `tw-fill-illustration-outline`. - **Option 2: Manual Search** - Take the path's stroke or fill hex value and convert it to RGB using a tool like [Hex to RGB](https://www.rgbtohex.net/hex-to-rgb/). - Search for the RGB value without commas in our `tw-theme.css` to find the Tailwind variable that corresponds to the color. - Create the appropriate stroke or fill class using the Tailwind variable. - - Example: `--color-art-primary` corresponds to `tw-stroke-art-primary` or - `tw-fill-art-primary`. + - Example: `--color-illustration-outline` corresponds to `tw-stroke-illustration-outline` + or `tw-fill-illustration-outline`. 6. **Remove any hardcoded width or height attributes** if your SVG has a configured [viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) attribute in order diff --git a/libs/components/src/stories/colors.mdx b/libs/components/src/stories/colors.mdx index 3a4a4f0fe3a..87ca673797b 100644 --- a/libs/components/src/stories/colors.mdx +++ b/libs/components/src/stories/colors.mdx @@ -62,9 +62,14 @@ export const Table = (args) => ( {Row("notification-600")} - {Row("art-primary")} - {Row("art-accent")} + {Row("illustration-outline")} + {Row("illustration-bg-primary")} + {Row("illustration-bg-secondary")} + {Row("illustration-bg-tertiary")} + {Row("illustration-tertiary")} + {Row("illustration-logo")} + Text @@ -78,6 +83,7 @@ export const Table = (args) => ( {Row("text-alt2")} {Row("text-code")} + ); diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css index c8de973c3d1..078357491e5 100644 --- a/libs/components/src/tw-theme.css +++ b/libs/components/src/tw-theme.css @@ -46,6 +46,7 @@ --color-notification-100: 255 225 247; --color-notification-600: 192 17 118; + /*art styles deprecated, use 'illustration' instead*/ --color-art-primary: 2 15 102; --color-art-accent: 44 221 223; @@ -60,6 +61,13 @@ --tw-ring-offset-color: #ffffff; --tw-sm-breakpoint: 640px; + + --color-illustration-outline: 2 15 102; + --color-illustration-bg-primary: 219 229 246; + --color-illustration-bg-secondary: 170 195 239; + --color-illustration-bg-tertiary: 255 255 255; + --color-illustration-tertiary: 255 191 0; + --color-illustration-logo: 23 93 220; } .theme_light { @@ -106,6 +114,7 @@ --color-notification-100: 117 37 83; --color-notification-600: 255 143 208; + /*art styles deprecated, use 'illustration' instead*/ --color-art-primary: 243 246 249; --color-art-accent: 44 221 233; @@ -118,6 +127,13 @@ --color-marketing-logo: 255 255 255; --tw-ring-offset-color: #1f242e; + + --color-illustration-outline: 23 93 220; + --color-illustration-bg-primary: 170 195 239; + --color-illustration-bg-secondary: 121 161 233; + --color-illustration-bg-tertiary: 243 246 249; + --color-illustration-tertiary: 255 191 0; + --color-illustration-logo: 255 255 255; } /** diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js index fde59f4a089..c38515cf775 100644 --- a/libs/components/tailwind.config.base.js +++ b/libs/components/tailwind.config.base.js @@ -63,6 +63,7 @@ module.exports = { 100: rgba("--color-notification-100"), 600: rgba("--color-notification-600"), }, + // art styles deprecated, use 'illustration' instead art: { primary: rgba("--color-art-primary"), accent: rgba("--color-art-accent"), @@ -83,6 +84,14 @@ module.exports = { alt4: rgba("--color-background-alt4"), }, "marketing-logo": rgba("--color-marketing-logo"), + illustration: { + outline: rgba("--color-illustration-outline"), + "bg-primary": rgba("--color-illustration-bg-primary"), + "bg-secondary": rgba("--color-illustration-bg-secondary"), + "bg-tertiary": rgba("--color-illustration-bg-tertiary"), + tertiary: rgba("--color-illustration-tertiary"), + logo: rgba("--color-illustration-logo"), + }, }, textColor: { main: rgba("--color-text-main"), From 5639668d3fa72643f540b29c51191fe3f6e453b5 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:39:53 -0700 Subject: [PATCH 56/57] feat:(set-initial-password): [Auth/PM-18457] Create SetInitialPasswordComponent (#14186) Creates a `SetInitialPasswordComponent` to be used in scenarios where an existing and authed user must set an initial password. Feature Flag: `PM16117_SetInitialPasswordRefactor` --- apps/browser/src/_locales/en/messages.json | 3 + apps/browser/src/popup/app-routing.module.ts | 11 + apps/desktop/src/app/app-routing.module.ts | 10 + .../src/app/services/services.module.ts | 19 + ...sktop-set-initial-password.service.spec.ts | 177 +++++ .../desktop-set-initial-password.service.ts | 59 ++ apps/desktop/src/locales/en/messages.json | 3 + apps/web/src/app/auth/core/services/index.ts | 1 + .../services/password-management/index.ts | 1 + .../web-set-initial-password.service.spec.ts | 208 ++++++ .../web-set-initial-password.service.ts | 83 +++ apps/web/src/app/core/core.module.ts | 20 + apps/web/src/app/oss-routing.module.ts | 11 + apps/web/src/locales/en/messages.json | 3 + libs/angular/src/auth/guards/auth.guard.ts | 39 +- ...initial-password.service.implementation.ts | 248 +++++++ ...fault-set-initial-password.service.spec.ts | 633 ++++++++++++++++++ .../set-initial-password.component.html | 29 + .../set-initial-password.component.ts | 249 +++++++ ...et-initial-password.service.abstraction.ts | 64 ++ .../src/services/jslib-services.module.ts | 18 + 21 files changed, 1876 insertions(+), 13 deletions(-) create mode 100644 apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts create mode 100644 apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts create mode 100644 apps/web/src/app/auth/core/services/password-management/index.ts create mode 100644 apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts create mode 100644 apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts create mode 100644 libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts create mode 100644 libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts create mode 100644 libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.html create mode 100644 libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts create mode 100644 libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index b6a8d1834b4..ce5787c46bd 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2919,6 +2919,9 @@ "emailVerificationRequiredDesc": { "message": "You must verify your email to use this feature. You can verify your email in the web vault." }, + "masterPasswordSuccessfullySet": { + "message": "Master password successfully set" + }, "updatedMasterPassword": { "message": "Updated master password" }, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index e3574c4e142..f836f5ffac7 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -15,6 +15,8 @@ import { tdeDecryptionRequiredGuard, unauthGuardFn, } from "@bitwarden/angular/auth/guards"; +import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { DevicesIcon, LoginComponent, @@ -38,6 +40,7 @@ import { UserLockIcon, VaultIcon, } from "@bitwarden/auth/angular"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, Icons } from "@bitwarden/components"; import { LockComponent } from "@bitwarden/key-management-ui"; @@ -376,6 +379,14 @@ const routes: Routes = [ }, ], }, + { + path: "set-initial-password", + canActivate: [canAccessFeature(FeatureFlag.PM16117_SetInitialPasswordRefactor), authGuard], + component: SetInitialPasswordComponent, + data: { + elevation: 1, + } satisfies RouteDataProperties, + }, { path: "login", canActivate: [unauthGuardFn(unauthRouteOverrides), IntroCarouselGuard], diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index d90f3cf0d26..42846878d03 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -14,6 +14,8 @@ import { tdeDecryptionRequiredGuard, unauthGuardFn, } from "@bitwarden/angular/auth/guards"; +import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route"; import { LoginComponent, @@ -315,6 +317,14 @@ const routes: Routes = [ }, } satisfies AnonLayoutWrapperData, }, + { + path: "set-initial-password", + canActivate: [canAccessFeature(FeatureFlag.PM16117_SetInitialPasswordRefactor), authGuard], + component: SetInitialPasswordComponent, + data: { + maxWidth: "lg", + } satisfies AnonLayoutWrapperData, + }, { path: "2fa", canActivate: [unauthGuardFn(), TwoFactorAuthGuard], diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 06c42c5b0bc..0abd810bd18 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -5,6 +5,7 @@ import { Router } from "@angular/router"; import { Subject, merge } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SECURE_STORAGE, @@ -140,6 +141,7 @@ import { DesktopSetPasswordJitService } from "./desktop-set-password-jit.service import { InitService } from "./init.service"; import { NativeMessagingManifestService } from "./native-messaging-manifest.service"; import { RendererCryptoFunctionService } from "./renderer-crypto-function.service"; +import { DesktopSetInitialPasswordService } from "./set-initial-password/desktop-set-initial-password.service"; const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK"); @@ -392,6 +394,23 @@ const safeProviders: SafeProvider[] = [ InternalUserDecryptionOptionsServiceAbstraction, ], }), + safeProvider({ + provide: SetInitialPasswordService, + useClass: DesktopSetInitialPasswordService, + deps: [ + ApiService, + EncryptService, + I18nServiceAbstraction, + KdfConfigService, + KeyService, + MasterPasswordApiService, + InternalMasterPasswordServiceAbstraction, + OrganizationApiServiceAbstraction, + OrganizationUserApiService, + InternalUserDecryptionOptionsServiceAbstraction, + MessagingServiceAbstraction, + ], + }), safeProvider({ provide: SsoUrlService, useClass: SsoUrlService, diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts new file mode 100644 index 00000000000..02438300e94 --- /dev/null +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts @@ -0,0 +1,177 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; +import { + FakeUserDecryptionOptions as UserDecryptionOptions, + InternalUserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; + +import { DesktopSetInitialPasswordService } from "./desktop-set-initial-password.service"; + +describe("DesktopSetInitialPasswordService", () => { + let sut: SetInitialPasswordService; + + let apiService: MockProxy; + let encryptService: MockProxy; + let i18nService: MockProxy; + let kdfConfigService: MockProxy; + let keyService: MockProxy; + let masterPasswordApiService: MockProxy; + let masterPasswordService: MockProxy; + let organizationApiService: MockProxy; + let organizationUserApiService: MockProxy; + let userDecryptionOptionsService: MockProxy; + let messagingService: MockProxy; + + beforeEach(() => { + apiService = mock(); + encryptService = mock(); + i18nService = mock(); + kdfConfigService = mock(); + keyService = mock(); + masterPasswordApiService = mock(); + masterPasswordService = mock(); + organizationApiService = mock(); + organizationUserApiService = mock(); + userDecryptionOptionsService = mock(); + messagingService = mock(); + + sut = new DesktopSetInitialPasswordService( + apiService, + encryptService, + i18nService, + kdfConfigService, + keyService, + masterPasswordApiService, + masterPasswordService, + organizationApiService, + organizationUserApiService, + userDecryptionOptionsService, + messagingService, + ); + }); + + it("should instantiate", () => { + expect(sut).not.toBeFalsy(); + }); + + describe("setInitialPassword(...)", () => { + // Mock function parameters + let credentials: SetInitialPasswordCredentials; + let userType: SetInitialPasswordUserType; + let userId: UserId; + + // Mock other function data + let userKey: UserKey; + let userKeyEncString: EncString; + let masterKeyEncryptedUserKey: [UserKey, EncString]; + + let keyPair: [string, EncString]; + let keysRequest: KeysRequest; + + let userDecryptionOptions: UserDecryptionOptions; + let userDecryptionOptionsSubject: BehaviorSubject; + let setPasswordRequest: SetPasswordRequest; + + beforeEach(() => { + // Mock function parameters + credentials = { + newMasterKey: new SymmetricCryptoKey(new Uint8Array(32).buffer as CsprngArray) as MasterKey, + newServerMasterKeyHash: "newServerMasterKeyHash", + newLocalMasterKeyHash: "newLocalMasterKeyHash", + newPasswordHint: "newPasswordHint", + kdfConfig: DEFAULT_KDF_CONFIG, + orgSsoIdentifier: "orgSsoIdentifier", + orgId: "orgId", + resetPasswordAutoEnroll: false, + }; + userId = "userId" as UserId; + userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; + + // Mock other function data + userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; + userKeyEncString = new EncString("masterKeyEncryptedUserKey"); + masterKeyEncryptedUserKey = [userKey, userKeyEncString]; + + keyPair = ["publicKey", new EncString("privateKey")]; + keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); + + userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: true }); + userDecryptionOptionsSubject = new BehaviorSubject(userDecryptionOptions); + userDecryptionOptionsService.userDecryptionOptions$ = userDecryptionOptionsSubject; + + setPasswordRequest = new SetPasswordRequest( + credentials.newServerMasterKeyHash, + masterKeyEncryptedUserKey[1].encryptedString, + credentials.newPasswordHint, + credentials.orgSsoIdentifier, + keysRequest, + credentials.kdfConfig.kdfType, + credentials.kdfConfig.iterations, + ); + }); + + function setupMocks() { + // Mock makeMasterKeyEncryptedUserKey() values + keyService.userKey$.mockReturnValue(of(userKey)); + keyService.encryptUserKeyWithMasterKey.mockResolvedValue(masterKeyEncryptedUserKey); + + // Mock keyPair values + keyService.userPrivateKey$.mockReturnValue(of(null)); + keyService.userPublicKey$.mockReturnValue(of(null)); + keyService.makeKeyPair.mockResolvedValue(keyPair); + } + + describe("given the initial password was successfully set", () => { + it("should send a 'redrawMenu' message", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(messagingService.send).toHaveBeenCalledTimes(1); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + }); + + describe("given the initial password was NOT successfully set (due to some error in setInitialPassword())", () => { + it("should NOT send a 'redrawMenu' message", async () => { + // Arrange + credentials.newMasterKey = null; // will trigger an error in setInitialPassword() + setupMocks(); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow(); + expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); + expect(messagingService.send).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts new file mode 100644 index 00000000000..8de7e73fafe --- /dev/null +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts @@ -0,0 +1,59 @@ +import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; +import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { KdfConfigService, KeyService } from "@bitwarden/key-management"; + +export class DesktopSetInitialPasswordService + extends DefaultSetInitialPasswordService + implements SetInitialPasswordService +{ + constructor( + protected apiService: ApiService, + protected encryptService: EncryptService, + protected i18nService: I18nService, + protected kdfConfigService: KdfConfigService, + protected keyService: KeyService, + protected masterPasswordApiService: MasterPasswordApiService, + protected masterPasswordService: InternalMasterPasswordServiceAbstraction, + protected organizationApiService: OrganizationApiServiceAbstraction, + protected organizationUserApiService: OrganizationUserApiService, + protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, + private messagingService: MessagingService, + ) { + super( + apiService, + encryptService, + i18nService, + kdfConfigService, + keyService, + masterPasswordApiService, + masterPasswordService, + organizationApiService, + organizationUserApiService, + userDecryptionOptionsService, + ); + } + + override async setInitialPassword( + credentials: SetInitialPasswordCredentials, + userType: SetInitialPasswordUserType, + userId: UserId, + ) { + await super.setInitialPassword(credentials, userType, userId); + + this.messagingService.send("redrawMenu"); + } +} diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index f67de2d51d7..ac9307c482c 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2392,6 +2392,9 @@ "passwordConfirmationDesc": { "message": "This action is protected. To continue, please re-enter your master password to verify your identity." }, + "masterPasswordSuccessfullySet": { + "message": "Master password successfully set" + }, "updatedMasterPassword": { "message": "Updated master password" }, diff --git a/apps/web/src/app/auth/core/services/index.ts b/apps/web/src/app/auth/core/services/index.ts index 5539e3b76ea..8c556986225 100644 --- a/apps/web/src/app/auth/core/services/index.ts +++ b/apps/web/src/app/auth/core/services/index.ts @@ -2,6 +2,7 @@ export * from "./change-password"; export * from "./login"; export * from "./login-decryption-options"; export * from "./webauthn-login"; +export * from "./password-management"; export * from "./set-password-jit"; export * from "./registration"; export * from "./two-factor-auth"; diff --git a/apps/web/src/app/auth/core/services/password-management/index.ts b/apps/web/src/app/auth/core/services/password-management/index.ts new file mode 100644 index 00000000000..1444fd024af --- /dev/null +++ b/apps/web/src/app/auth/core/services/password-management/index.ts @@ -0,0 +1 @@ +export * from "./set-initial-password/web-set-initial-password.service"; diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts new file mode 100644 index 00000000000..b90d0624b3f --- /dev/null +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts @@ -0,0 +1,208 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; +import { + FakeUserDecryptionOptions as UserDecryptionOptions, + InternalUserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; +import { AcceptOrganizationInviteService } from "@bitwarden/web-vault/app/auth/organization-invite/accept-organization.service"; +import { RouterService } from "@bitwarden/web-vault/app/core"; + +import { WebSetInitialPasswordService } from "./web-set-initial-password.service"; + +describe("WebSetInitialPasswordService", () => { + let sut: SetInitialPasswordService; + + let apiService: MockProxy; + let encryptService: MockProxy; + let i18nService: MockProxy; + let kdfConfigService: MockProxy; + let keyService: MockProxy; + let masterPasswordApiService: MockProxy; + let masterPasswordService: MockProxy; + let organizationApiService: MockProxy; + let organizationUserApiService: MockProxy; + let userDecryptionOptionsService: MockProxy; + let acceptOrganizationInviteService: MockProxy; + let routerService: MockProxy; + + beforeEach(() => { + apiService = mock(); + encryptService = mock(); + i18nService = mock(); + kdfConfigService = mock(); + keyService = mock(); + masterPasswordApiService = mock(); + masterPasswordService = mock(); + organizationApiService = mock(); + organizationUserApiService = mock(); + userDecryptionOptionsService = mock(); + acceptOrganizationInviteService = mock(); + routerService = mock(); + + sut = new WebSetInitialPasswordService( + apiService, + encryptService, + i18nService, + kdfConfigService, + keyService, + masterPasswordApiService, + masterPasswordService, + organizationApiService, + organizationUserApiService, + userDecryptionOptionsService, + acceptOrganizationInviteService, + routerService, + ); + }); + + it("should instantiate", () => { + expect(sut).not.toBeFalsy(); + }); + + describe("setInitialPassword(...)", () => { + // Mock function parameters + let credentials: SetInitialPasswordCredentials; + let userType: SetInitialPasswordUserType; + let userId: UserId; + + // Mock other function data + let userKey: UserKey; + let userKeyEncString: EncString; + let masterKeyEncryptedUserKey: [UserKey, EncString]; + + let keyPair: [string, EncString]; + let keysRequest: KeysRequest; + + let userDecryptionOptions: UserDecryptionOptions; + let userDecryptionOptionsSubject: BehaviorSubject; + let setPasswordRequest: SetPasswordRequest; + + beforeEach(() => { + // Mock function parameters + credentials = { + newMasterKey: new SymmetricCryptoKey(new Uint8Array(32).buffer as CsprngArray) as MasterKey, + newServerMasterKeyHash: "newServerMasterKeyHash", + newLocalMasterKeyHash: "newLocalMasterKeyHash", + newPasswordHint: "newPasswordHint", + kdfConfig: DEFAULT_KDF_CONFIG, + orgSsoIdentifier: "orgSsoIdentifier", + orgId: "orgId", + resetPasswordAutoEnroll: false, + }; + userId = "userId" as UserId; + userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; + + // Mock other function data + userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; + userKeyEncString = new EncString("masterKeyEncryptedUserKey"); + masterKeyEncryptedUserKey = [userKey, userKeyEncString]; + + keyPair = ["publicKey", new EncString("privateKey")]; + keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); + + userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: true }); + userDecryptionOptionsSubject = new BehaviorSubject(userDecryptionOptions); + userDecryptionOptionsService.userDecryptionOptions$ = userDecryptionOptionsSubject; + + setPasswordRequest = new SetPasswordRequest( + credentials.newServerMasterKeyHash, + masterKeyEncryptedUserKey[1].encryptedString, + credentials.newPasswordHint, + credentials.orgSsoIdentifier, + keysRequest, + credentials.kdfConfig.kdfType, + credentials.kdfConfig.iterations, + ); + }); + + function setupMocks() { + // Mock makeMasterKeyEncryptedUserKey() values + keyService.userKey$.mockReturnValue(of(userKey)); + keyService.encryptUserKeyWithMasterKey.mockResolvedValue(masterKeyEncryptedUserKey); + + // Mock keyPair values + keyService.userPrivateKey$.mockReturnValue(of(null)); + keyService.userPublicKey$.mockReturnValue(of(null)); + keyService.makeKeyPair.mockResolvedValue(keyPair); + } + + describe("given the initial password was successfully set", () => { + it("should call routerService.getAndClearLoginRedirectUrl()", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(routerService.getAndClearLoginRedirectUrl).toHaveBeenCalledTimes(1); + }); + + it("should call acceptOrganizationInviteService.clearOrganizationInvitation()", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(acceptOrganizationInviteService.clearOrganizationInvitation).toHaveBeenCalledTimes( + 1, + ); + }); + }); + + describe("given the initial password was NOT successfully set (due to some error in setInitialPassword())", () => { + it("should NOT call routerService.getAndClearLoginRedirectUrl()", async () => { + // Arrange + credentials.newMasterKey = null; // will trigger an error in setInitialPassword() + setupMocks(); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow(); + expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); + expect(routerService.getAndClearLoginRedirectUrl).not.toHaveBeenCalled(); + }); + + it("should NOT call acceptOrganizationInviteService.clearOrganizationInvitation()", async () => { + // Arrange + credentials.newMasterKey = null; // will trigger an error in setInitialPassword() + setupMocks(); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow(); + expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); + expect(acceptOrganizationInviteService.clearOrganizationInvitation).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts new file mode 100644 index 00000000000..41e7e8ad4ab --- /dev/null +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts @@ -0,0 +1,83 @@ +import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; +import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { KdfConfigService, KeyService } from "@bitwarden/key-management"; +import { AcceptOrganizationInviteService } from "@bitwarden/web-vault/app/auth/organization-invite/accept-organization.service"; +import { RouterService } from "@bitwarden/web-vault/app/core"; + +export class WebSetInitialPasswordService + extends DefaultSetInitialPasswordService + implements SetInitialPasswordService +{ + constructor( + protected apiService: ApiService, + protected encryptService: EncryptService, + protected i18nService: I18nService, + protected kdfConfigService: KdfConfigService, + protected keyService: KeyService, + protected masterPasswordApiService: MasterPasswordApiService, + protected masterPasswordService: InternalMasterPasswordServiceAbstraction, + protected organizationApiService: OrganizationApiServiceAbstraction, + protected organizationUserApiService: OrganizationUserApiService, + protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, + private acceptOrganizationInviteService: AcceptOrganizationInviteService, + private routerService: RouterService, + ) { + super( + apiService, + encryptService, + i18nService, + kdfConfigService, + keyService, + masterPasswordApiService, + masterPasswordService, + organizationApiService, + organizationUserApiService, + userDecryptionOptionsService, + ); + } + + override async setInitialPassword( + credentials: SetInitialPasswordCredentials, + userType: SetInitialPasswordUserType, + userId: UserId, + ) { + await super.setInitialPassword(credentials, userType, userId); + + /** + * TODO: Investigate refactoring the following logic in https://bitwarden.atlassian.net/browse/PM-22615 + * --- + * When a user has been invited to an org, they can be accepted into the org in two different ways: + * + * 1) By clicking the email invite link, which triggers the normal AcceptOrganizationComponent flow + * a. This flow sets an org invite in state + * b. However, if the user does not already have an account AND the org has SSO enabled AND the require + * SSO policy enabled, the AcceptOrganizationComponent will send the user to /sso to accelerate + * the user through the SSO JIT provisioning process (see #2 below) + * + * 2) By logging in via SSO, which triggers the JIT provisioning process + * a. This flow does NOT (itself) set an org invite in state + * b. The set initial password process on the server accepts the user into the org after successfully + * setting the password (see server - SetInitialMasterPasswordCommand.cs) + * + * If a user clicks the email link but gets accelerated through the SSO JIT process (see 1b), + * the SSO JIT process will accept the user into the org upon setting their initial password (see 2b), + * at which point we must remember to clear the deep linked URL used for accepting the org invite, as well + * as clear the org invite itself that was originally set in state by the AcceptOrganizationComponent. + */ + await this.routerService.getAndClearLoginRedirectUrl(); + await this.acceptOrganizationInviteService.clearOrganizationInvitation(); + } +} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 46435981a5e..b6a6ca102d8 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -10,6 +10,7 @@ import { OrganizationUserApiService, CollectionService, } from "@bitwarden/admin-console/common"; +import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { CLIENT_TYPE, @@ -117,6 +118,7 @@ import { WebLoginDecryptionOptionsService, WebTwoFactorAuthDuoComponentService, LinkSsoService, + WebSetInitialPasswordService, } from "../auth"; import { WebSsoComponentService } from "../auth/core/services/login/web-sso-component.service"; import { AcceptOrganizationInviteService } from "../auth/organization-invite/accept-organization.service"; @@ -283,6 +285,24 @@ const safeProviders: SafeProvider[] = [ InternalUserDecryptionOptionsServiceAbstraction, ], }), + safeProvider({ + provide: SetInitialPasswordService, + useClass: WebSetInitialPasswordService, + deps: [ + ApiService, + EncryptService, + I18nServiceAbstraction, + KdfConfigService, + KeyServiceAbstraction, + MasterPasswordApiService, + InternalMasterPasswordServiceAbstraction, + OrganizationApiServiceAbstraction, + OrganizationUserApiService, + InternalUserDecryptionOptionsServiceAbstraction, + AcceptOrganizationInviteService, + RouterService, + ], + }), safeProvider({ provide: AppIdService, useClass: DefaultAppIdService, diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 0733d1ef289..615bb545811 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -10,6 +10,8 @@ import { unauthGuardFn, activeAuthGuard, } from "@bitwarden/angular/auth/guards"; +import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { PasswordHintComponent, RegistrationFinishComponent, @@ -36,6 +38,7 @@ import { NewDeviceVerificationComponent, DeviceVerificationIcon, } from "@bitwarden/auth/angular"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, Icons } from "@bitwarden/components"; import { LockComponent } from "@bitwarden/key-management-ui"; import { VaultIcons } from "@bitwarden/vault"; @@ -305,6 +308,14 @@ const routes: Routes = [ }, ], }, + { + path: "set-initial-password", + canActivate: [canAccessFeature(FeatureFlag.PM16117_SetInitialPasswordRefactor), authGuard], + component: SetInitialPasswordComponent, + data: { + maxWidth: "lg", + } satisfies AnonLayoutWrapperData, + }, { path: "set-password-jit", component: SetPasswordJitComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 5c9b02e5287..eed2757eacc 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6065,6 +6065,9 @@ "add": { "message": "Add" }, + "masterPasswordSuccessfullySet": { + "message": "Master password successfully set" + }, "updatedMasterPassword": { "message": "Master password saved" }, diff --git a/libs/angular/src/auth/guards/auth.guard.ts b/libs/angular/src/auth/guards/auth.guard.ts index f99a91fda34..7b8c21fef62 100644 --- a/libs/angular/src/auth/guards/auth.guard.ts +++ b/libs/angular/src/auth/guards/auth.guard.ts @@ -39,7 +39,32 @@ export const authGuard: CanActivateFn = async ( return false; } - if (authStatus === AuthenticationStatus.Locked) { + const userId = (await firstValueFrom(accountService.activeAccount$)).id; + const forceSetPasswordReason = await firstValueFrom( + masterPasswordService.forceSetPasswordReason$(userId), + ); + + const isSetInitialPasswordFlagOn = await configService.getFeatureFlag( + FeatureFlag.PM16117_SetInitialPasswordRefactor, + ); + const isChangePasswordFlagOn = await configService.getFeatureFlag( + FeatureFlag.PM16117_ChangeExistingPasswordRefactor, + ); + + // User JIT provisioned into a master-password-encryption org + if ( + authStatus === AuthenticationStatus.Locked && + forceSetPasswordReason === ForceSetPasswordReason.SsoNewJitProvisionedUser && + !routerState.url.includes("set-initial-password") && + isSetInitialPasswordFlagOn + ) { + return router.createUrlTree(["/set-initial-password"]); + } + + if ( + authStatus === AuthenticationStatus.Locked && + forceSetPasswordReason !== ForceSetPasswordReason.SsoNewJitProvisionedUser + ) { if (routerState != null) { messagingService.send("lockedUrl", { url: routerState.url }); } @@ -55,18 +80,6 @@ export const authGuard: CanActivateFn = async ( return router.createUrlTree(["/remove-password"]); } - const userId = (await firstValueFrom(accountService.activeAccount$)).id; - const forceSetPasswordReason = await firstValueFrom( - masterPasswordService.forceSetPasswordReason$(userId), - ); - - const isSetInitialPasswordFlagOn = await configService.getFeatureFlag( - FeatureFlag.PM16117_SetInitialPasswordRefactor, - ); - const isChangePasswordFlagOn = await configService.getFeatureFlag( - FeatureFlag.PM16117_ChangeExistingPasswordRefactor, - ); - // TDE org user has "manage account recovery" permission if ( forceSetPasswordReason === diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts new file mode 100644 index 00000000000..1c5edb00c8c --- /dev/null +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts @@ -0,0 +1,248 @@ +import { firstValueFrom } from "rxjs"; + +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { + OrganizationUserApiService, + OrganizationUserResetPasswordEnrollmentRequest, +} from "@bitwarden/admin-console/common"; +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { KdfConfigService, KeyService, KdfConfig } from "@bitwarden/key-management"; + +import { + SetInitialPasswordService, + SetInitialPasswordCredentials, + SetInitialPasswordUserType, +} from "./set-initial-password.service.abstraction"; + +export class DefaultSetInitialPasswordService implements SetInitialPasswordService { + constructor( + protected apiService: ApiService, + protected encryptService: EncryptService, + protected i18nService: I18nService, + protected kdfConfigService: KdfConfigService, + protected keyService: KeyService, + protected masterPasswordApiService: MasterPasswordApiService, + protected masterPasswordService: InternalMasterPasswordServiceAbstraction, + protected organizationApiService: OrganizationApiServiceAbstraction, + protected organizationUserApiService: OrganizationUserApiService, + protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, + ) {} + + async setInitialPassword( + credentials: SetInitialPasswordCredentials, + userType: SetInitialPasswordUserType, + userId: UserId, + ): Promise { + const { + newMasterKey, + newServerMasterKeyHash, + newLocalMasterKeyHash, + newPasswordHint, + kdfConfig, + orgSsoIdentifier, + orgId, + resetPasswordAutoEnroll, + } = credentials; + + for (const [key, value] of Object.entries(credentials)) { + if (value == null) { + throw new Error(`${key} not found. Could not set password.`); + } + } + if (userId == null) { + throw new Error("userId not found. Could not set password."); + } + if (userType == null) { + throw new Error("userType not found. Could not set password."); + } + + const masterKeyEncryptedUserKey = await this.makeMasterKeyEncryptedUserKey( + newMasterKey, + userId, + ); + if (masterKeyEncryptedUserKey == null || !masterKeyEncryptedUserKey[1].encryptedString) { + throw new Error("masterKeyEncryptedUserKey not found. Could not set password."); + } + + let keyPair: [string, EncString] | null = null; + let keysRequest: KeysRequest | null = null; + + if (userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { + /** + * A user being JIT provisioned into a MP encryption org does not yet have a user + * asymmetric key pair, so we create it for them here. + * + * Sidenote: + * In the case of a TDE user whose permissions require that they have a MP - that user + * will already have a user asymmetric key pair by this point, so we skip this if-block + * so that we don't create a new key pair for them. + */ + + // Extra safety check (see description on https://github.com/bitwarden/clients/pull/10180): + // In case we have have a local private key and are not sure whether it has been posted to the server, + // we post the local private key instead of generating a new one + const existingUserPrivateKey = (await firstValueFrom( + this.keyService.userPrivateKey$(userId), + )) as Uint8Array; + + const existingUserPublicKey = await firstValueFrom(this.keyService.userPublicKey$(userId)); + + if (existingUserPrivateKey != null && existingUserPublicKey != null) { + const existingUserPublicKeyB64 = Utils.fromBufferToB64(existingUserPublicKey); + + // Existing key pair + keyPair = [ + existingUserPublicKeyB64, + await this.encryptService.wrapDecapsulationKey( + existingUserPrivateKey, + masterKeyEncryptedUserKey[0], + ), + ]; + } else { + // New key pair + keyPair = await this.keyService.makeKeyPair(masterKeyEncryptedUserKey[0]); + } + + if (keyPair == null) { + throw new Error("keyPair not found. Could not set password."); + } + if (!keyPair[1].encryptedString) { + throw new Error("encrypted private key not found. Could not set password."); + } + + keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); + } + + const request = new SetPasswordRequest( + newServerMasterKeyHash, + masterKeyEncryptedUserKey[1].encryptedString, + newPasswordHint, + orgSsoIdentifier, + keysRequest, + kdfConfig.kdfType, + kdfConfig.iterations, + ); + + await this.masterPasswordApiService.setPassword(request); + + // Clear force set password reason to allow navigation back to vault. + await this.masterPasswordService.setForceSetPasswordReason(ForceSetPasswordReason.None, userId); + + // User now has a password so update account decryption options in state + await this.updateAccountDecryptionProperties( + newMasterKey, + kdfConfig, + masterKeyEncryptedUserKey, + userId, + ); + + /** + * Set the private key only for new JIT provisioned users in MP encryption orgs. + * (Existing TDE users will have their private key set on sync or on login.) + */ + if (keyPair != null && userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { + if (!keyPair[1].encryptedString) { + throw new Error("encrypted private key not found. Could not set private key in state."); + } + await this.keyService.setPrivateKey(keyPair[1].encryptedString, userId); + } + + await this.masterPasswordService.setMasterKeyHash(newLocalMasterKeyHash, userId); + + if (resetPasswordAutoEnroll) { + await this.handleResetPasswordAutoEnroll(newServerMasterKeyHash, orgId, userId); + } + } + + private async makeMasterKeyEncryptedUserKey( + masterKey: MasterKey, + userId: UserId, + ): Promise<[UserKey, EncString]> { + let masterKeyEncryptedUserKey: [UserKey, EncString] | null = null; + + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + + if (userKey == null) { + masterKeyEncryptedUserKey = await this.keyService.makeUserKey(masterKey); + } else { + masterKeyEncryptedUserKey = await this.keyService.encryptUserKeyWithMasterKey(masterKey); + } + + return masterKeyEncryptedUserKey; + } + + private async updateAccountDecryptionProperties( + masterKey: MasterKey, + kdfConfig: KdfConfig, + masterKeyEncryptedUserKey: [UserKey, EncString], + userId: UserId, + ) { + const userDecryptionOpts = await firstValueFrom( + this.userDecryptionOptionsService.userDecryptionOptions$, + ); + userDecryptionOpts.hasMasterPassword = true; + await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts); + await this.kdfConfigService.setKdfConfig(userId, kdfConfig); + await this.masterPasswordService.setMasterKey(masterKey, userId); + await this.keyService.setUserKey(masterKeyEncryptedUserKey[0], userId); + } + + private async handleResetPasswordAutoEnroll( + masterKeyHash: string, + orgId: string, + userId: UserId, + ) { + const organizationKeys = await this.organizationApiService.getKeys(orgId); + + if (organizationKeys == null) { + throw new Error( + "Organization keys response is null. Could not handle reset password auto enroll.", + ); + } + + const orgPublicKey = Utils.fromB64ToArray(organizationKeys.publicKey); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + + if (userKey == null) { + throw new Error("userKey not found. Could not handle reset password auto enroll."); + } + + // RSA encrypt user key with organization public key + const orgPublicKeyEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned( + userKey, + orgPublicKey, + ); + + if (orgPublicKeyEncryptedUserKey == null || !orgPublicKeyEncryptedUserKey.encryptedString) { + throw new Error( + "orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.", + ); + } + + const enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest(); + enrollmentRequest.masterPasswordHash = masterKeyHash; + enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString; + + await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment( + orgId, + userId, + enrollmentRequest, + ); + } +} diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts new file mode 100644 index 00000000000..ca4d9adbd67 --- /dev/null +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts @@ -0,0 +1,633 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { + OrganizationUserApiService, + OrganizationUserResetPasswordEnrollmentRequest, +} from "@bitwarden/admin-console/common"; +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { + FakeUserDecryptionOptions as UserDecryptionOptions, + InternalUserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { OrganizationKeysResponse } from "@bitwarden/common/admin-console/models/response/organization-keys.response"; +import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey, UserKey, UserPrivateKey, UserPublicKey } from "@bitwarden/common/types/key"; +import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; + +import { DefaultSetInitialPasswordService } from "./default-set-initial-password.service.implementation"; +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "./set-initial-password.service.abstraction"; + +describe("DefaultSetInitialPasswordService", () => { + let sut: SetInitialPasswordService; + + let apiService: MockProxy; + let encryptService: MockProxy; + let i18nService: MockProxy; + let kdfConfigService: MockProxy; + let keyService: MockProxy; + let masterPasswordApiService: MockProxy; + let masterPasswordService: MockProxy; + let organizationApiService: MockProxy; + let organizationUserApiService: MockProxy; + let userDecryptionOptionsService: MockProxy; + + beforeEach(() => { + apiService = mock(); + encryptService = mock(); + i18nService = mock(); + kdfConfigService = mock(); + keyService = mock(); + masterPasswordApiService = mock(); + masterPasswordService = mock(); + organizationApiService = mock(); + organizationUserApiService = mock(); + userDecryptionOptionsService = mock(); + + sut = new DefaultSetInitialPasswordService( + apiService, + encryptService, + i18nService, + kdfConfigService, + keyService, + masterPasswordApiService, + masterPasswordService, + organizationApiService, + organizationUserApiService, + userDecryptionOptionsService, + ); + }); + + it("should instantiate", () => { + expect(sut).not.toBeFalsy(); + }); + + describe("setInitialPassword(...)", () => { + // Mock function parameters + let credentials: SetInitialPasswordCredentials; + let userType: SetInitialPasswordUserType; + let userId: UserId; + + // Mock other function data + let userKey: UserKey; + let userKeyEncString: EncString; + let masterKeyEncryptedUserKey: [UserKey, EncString]; + + let existingUserPublicKey: UserPublicKey; + let existingUserPrivateKey: UserPrivateKey; + let userKeyEncryptedPrivateKey: EncString; + + let keyPair: [string, EncString]; + let keysRequest: KeysRequest; + + let organizationKeys: OrganizationKeysResponse; + let orgPublicKeyEncryptedUserKey: EncString; + + let userDecryptionOptions: UserDecryptionOptions; + let userDecryptionOptionsSubject: BehaviorSubject; + let setPasswordRequest: SetPasswordRequest; + + let enrollmentRequest: OrganizationUserResetPasswordEnrollmentRequest; + + beforeEach(() => { + // Mock function parameters + credentials = { + newMasterKey: new SymmetricCryptoKey(new Uint8Array(32).buffer as CsprngArray) as MasterKey, + newServerMasterKeyHash: "newServerMasterKeyHash", + newLocalMasterKeyHash: "newLocalMasterKeyHash", + newPasswordHint: "newPasswordHint", + kdfConfig: DEFAULT_KDF_CONFIG, + orgSsoIdentifier: "orgSsoIdentifier", + orgId: "orgId", + resetPasswordAutoEnroll: false, + }; + userId = "userId" as UserId; + userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; + + // Mock other function data + userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; + userKeyEncString = new EncString("masterKeyEncryptedUserKey"); + masterKeyEncryptedUserKey = [userKey, userKeyEncString]; + + existingUserPublicKey = Utils.fromB64ToArray("existingUserPublicKey") as UserPublicKey; + existingUserPrivateKey = Utils.fromB64ToArray("existingUserPrivateKey") as UserPrivateKey; + userKeyEncryptedPrivateKey = new EncString("userKeyEncryptedPrivateKey"); + + keyPair = ["publicKey", new EncString("privateKey")]; + keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); + + organizationKeys = { + privateKey: "orgPrivateKey", + publicKey: "orgPublicKey", + } as OrganizationKeysResponse; + orgPublicKeyEncryptedUserKey = new EncString("orgPublicKeyEncryptedUserKey"); + + userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: true }); + userDecryptionOptionsSubject = new BehaviorSubject(userDecryptionOptions); + userDecryptionOptionsService.userDecryptionOptions$ = userDecryptionOptionsSubject; + + setPasswordRequest = new SetPasswordRequest( + credentials.newServerMasterKeyHash, + masterKeyEncryptedUserKey[1].encryptedString, + credentials.newPasswordHint, + credentials.orgSsoIdentifier, + keysRequest, + credentials.kdfConfig.kdfType, + credentials.kdfConfig.iterations, + ); + + enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest(); + enrollmentRequest.masterPasswordHash = credentials.newServerMasterKeyHash; + enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString; + }); + + interface MockConfig { + userType: SetInitialPasswordUserType; + userHasUserKey: boolean; + userHasLocalKeyPair: boolean; + resetPasswordAutoEnroll: boolean; + } + + const defaultMockConfig: MockConfig = { + userType: SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER, + userHasUserKey: true, + userHasLocalKeyPair: false, + resetPasswordAutoEnroll: false, + }; + + function setupMocks(config: MockConfig = defaultMockConfig) { + // Mock makeMasterKeyEncryptedUserKey() values + if (config.userHasUserKey) { + keyService.userKey$.mockReturnValue(of(userKey)); + keyService.encryptUserKeyWithMasterKey.mockResolvedValue(masterKeyEncryptedUserKey); + } else { + keyService.userKey$.mockReturnValue(of(null)); + keyService.makeUserKey.mockResolvedValue(masterKeyEncryptedUserKey); + } + + // Mock keyPair values + if (config.userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { + if (config.userHasLocalKeyPair) { + keyService.userPrivateKey$.mockReturnValue(of(existingUserPrivateKey)); + keyService.userPublicKey$.mockReturnValue(of(existingUserPublicKey)); + encryptService.wrapDecapsulationKey.mockResolvedValue(userKeyEncryptedPrivateKey); + } else { + keyService.userPrivateKey$.mockReturnValue(of(null)); + keyService.userPublicKey$.mockReturnValue(of(null)); + keyService.makeKeyPair.mockResolvedValue(keyPair); + } + } + + // Mock handleResetPasswordAutoEnroll() values + if (config.resetPasswordAutoEnroll) { + organizationApiService.getKeys.mockResolvedValue(organizationKeys); + encryptService.encapsulateKeyUnsigned.mockResolvedValue(orgPublicKeyEncryptedUserKey); + keyService.userKey$.mockReturnValue(of(userKey)); + } + } + + describe("general error handling", () => { + [ + "newMasterKey", + "newServerMasterKeyHash", + "newLocalMasterKeyHash", + "newPasswordHint", + "kdfConfig", + "orgSsoIdentifier", + "orgId", + "resetPasswordAutoEnroll", + ].forEach((key) => { + it(`should throw if ${key} is not provided on the SetInitialPasswordCredentials object`, async () => { + // Arrange + const invalidCredentials: SetInitialPasswordCredentials = { + ...credentials, + [key]: null, + }; + + // Act + const promise = sut.setInitialPassword(invalidCredentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow(`${key} not found. Could not set password.`); + }); + }); + + ["userId", "userType"].forEach((param) => { + it(`should throw if ${param} was not passed in`, async () => { + // Arrange & Act + const promise = sut.setInitialPassword( + credentials, + param === "userType" ? null : userType, + param === "userId" ? null : userId, + ); + + // Assert + await expect(promise).rejects.toThrow(`${param} not found. Could not set password.`); + }); + }); + }); + + describe("given SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER", () => { + beforeEach(() => { + userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; + }); + + describe("given the user has an existing local key pair", () => { + it("should NOT create a brand new key pair for the user", async () => { + // Arrange + setPasswordRequest.keys = { + encryptedPrivateKey: userKeyEncryptedPrivateKey.encryptedString, + publicKey: Utils.fromBufferToB64(existingUserPublicKey), + }; + + setupMocks({ ...defaultMockConfig, userHasLocalKeyPair: true }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(keyService.userPrivateKey$).toHaveBeenCalledWith(userId); + expect(keyService.userPublicKey$).toHaveBeenCalledWith(userId); + expect(encryptService.wrapDecapsulationKey).toHaveBeenCalledWith( + existingUserPrivateKey, + masterKeyEncryptedUserKey[0], + ); + expect(keyService.makeKeyPair).not.toHaveBeenCalled(); + }); + }); + + describe("given the user has a userKey", () => { + it("should successfully set an initial password", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + }); + }); + + describe("given the user does NOT have a userKey", () => { + it("should successfully set an initial password", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userHasUserKey: false }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + }); + }); + + it("should throw if a key pair is not found", async () => { + // Arrange + keyPair = null; + + setupMocks(); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow("keyPair not found. Could not set password."); + expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); + }); + + it("should throw if an encrypted private key is not found", async () => { + // Arrange + keyPair[1].encryptedString = "" as EncryptedString; + + setupMocks(); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow( + "encrypted private key not found. Could not set password.", + ); + expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); + }); + + describe("given the initial password has been successfully set", () => { + it("should clear the ForceSetPasswordReason by setting it to None", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(masterPasswordService.setForceSetPasswordReason).toHaveBeenCalledWith( + ForceSetPasswordReason.None, + userId, + ); + }); + + it("should update account decryption properties", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(userDecryptionOptionsService.setUserDecryptionOptions).toHaveBeenCalledWith( + userDecryptionOptions, + ); + expect(kdfConfigService.setKdfConfig).toHaveBeenCalledWith(userId, credentials.kdfConfig); + expect(masterPasswordService.setMasterKey).toHaveBeenCalledWith( + credentials.newMasterKey, + userId, + ); + expect(keyService.setUserKey).toHaveBeenCalledWith(masterKeyEncryptedUserKey[0], userId); + }); + + it("should set the private key to state", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(keyService.setPrivateKey).toHaveBeenCalledWith(keyPair[1].encryptedString, userId); + }); + + it("should set the local master key hash to state", async () => { + // Arrange + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith( + credentials.newLocalMasterKeyHash, + userId, + ); + }); + + describe("given resetPasswordAutoEnroll is true", () => { + it(`should handle reset password (account recovery) auto enroll`, async () => { + // Arrange + credentials.resetPasswordAutoEnroll = true; + + setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).toHaveBeenCalledWith(credentials.orgId, userId, enrollmentRequest); + }); + + it("should throw if organization keys are not found", async () => { + // Arrange + credentials.resetPasswordAutoEnroll = true; + organizationKeys = null; + + setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow( + "Organization keys response is null. Could not handle reset password auto enroll.", + ); + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).not.toHaveBeenCalled(); + }); + + ["orgPublicKeyEncryptedUserKey", "orgPublicKeyEncryptedUserKey.encryptedString"].forEach( + (property) => { + it("should throw if orgPublicKeyEncryptedUserKey is not found", async () => { + // Arrange + credentials.resetPasswordAutoEnroll = true; + + if (property === "orgPublicKeyEncryptedUserKey") { + orgPublicKeyEncryptedUserKey = null; + } else { + orgPublicKeyEncryptedUserKey.encryptedString = "" as EncryptedString; + } + + setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); + + // Act + const promise = sut.setInitialPassword(credentials, userType, userId); + + // Assert + await expect(promise).rejects.toThrow( + "orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.", + ); + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith( + setPasswordRequest, + ); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).not.toHaveBeenCalled(); + }); + }, + ); + }); + + describe("given resetPasswordAutoEnroll is false", () => { + it(`should NOT handle reset password (account recovery) auto enroll`, async () => { + // Arrange + credentials.resetPasswordAutoEnroll = false; + + setupMocks(); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).not.toHaveBeenCalled(); + }); + }); + }); + }); + + describe("given SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP", () => { + beforeEach(() => { + userType = SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP; + setPasswordRequest.keys = null; + }); + + it("should NOT generate a keyPair", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(keyService.userPrivateKey$).not.toHaveBeenCalled(); + expect(keyService.userPublicKey$).not.toHaveBeenCalled(); + expect(encryptService.wrapDecapsulationKey).not.toHaveBeenCalled(); + expect(keyService.makeKeyPair).not.toHaveBeenCalled(); + }); + + describe("given the user has a userKey", () => { + it("should successfully set an initial password", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + }); + }); + + describe("given the user does NOT have a userKey", () => { + it("should successfully set an initial password", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + }); + }); + + describe("given the initial password has been successfully set", () => { + it("should clear the ForceSetPasswordReason by setting it to None", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(masterPasswordService.setForceSetPasswordReason).toHaveBeenCalledWith( + ForceSetPasswordReason.None, + userId, + ); + }); + + it("should update account decryption properties", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(userDecryptionOptionsService.setUserDecryptionOptions).toHaveBeenCalledWith( + userDecryptionOptions, + ); + expect(kdfConfigService.setKdfConfig).toHaveBeenCalledWith(userId, credentials.kdfConfig); + expect(masterPasswordService.setMasterKey).toHaveBeenCalledWith( + credentials.newMasterKey, + userId, + ); + expect(keyService.setUserKey).toHaveBeenCalledWith(masterKeyEncryptedUserKey[0], userId); + }); + + it("should NOT set the private key to state", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should set the local master key hash to state", async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith( + credentials.newLocalMasterKeyHash, + userId, + ); + }); + + describe("given resetPasswordAutoEnroll is true", () => { + it(`should handle reset password (account recovery) auto enroll`, async () => { + // Arrange + credentials.resetPasswordAutoEnroll = true; + + setupMocks({ ...defaultMockConfig, userType, resetPasswordAutoEnroll: true }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).toHaveBeenCalledWith(credentials.orgId, userId, enrollmentRequest); + }); + }); + + describe("given resetPasswordAutoEnroll is false", () => { + it(`should NOT handle reset password (account recovery) auto enroll`, async () => { + // Arrange + setupMocks({ ...defaultMockConfig, userType }); + + // Act + await sut.setInitialPassword(credentials, userType, userId); + + // Assert + expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); + expect( + organizationUserApiService.putOrganizationUserResetPasswordEnrollment, + ).not.toHaveBeenCalled(); + }); + }); + }); + }); + }); +}); diff --git a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.html b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.html new file mode 100644 index 00000000000..c83cbbe3521 --- /dev/null +++ b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.html @@ -0,0 +1,29 @@ +@if (initializing) { +
+ +
+} @else { + + {{ "resetPasswordAutoEnrollInviteWarning" | i18n }} + + + +} diff --git a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts new file mode 100644 index 00000000000..fbab9eaa2c3 --- /dev/null +++ b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts @@ -0,0 +1,249 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; + +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { + InputPasswordComponent, + InputPasswordFlow, + PasswordInputResult, +} from "@bitwarden/auth/angular"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + AnonLayoutWrapperDataService, + CalloutComponent, + DialogService, + ToastService, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +import { + SetInitialPasswordCredentials, + SetInitialPasswordService, + SetInitialPasswordUserType, +} from "./set-initial-password.service.abstraction"; + +@Component({ + standalone: true, + templateUrl: "set-initial-password.component.html", + imports: [CalloutComponent, CommonModule, InputPasswordComponent, I18nPipe], +}) +export class SetInitialPasswordComponent implements OnInit { + protected inputPasswordFlow = InputPasswordFlow.SetInitialPasswordAuthedUser; + + protected email?: string; + protected forceSetPasswordReason?: ForceSetPasswordReason; + protected initializing = true; + protected masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null; + protected orgId?: string; + protected orgSsoIdentifier?: string; + protected resetPasswordAutoEnroll?: boolean; + protected submitting = false; + protected userId?: UserId; + protected userType?: SetInitialPasswordUserType; + + constructor( + private accountService: AccountService, + private activatedRoute: ActivatedRoute, + private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, + private dialogService: DialogService, + private i18nService: I18nService, + private masterPasswordService: InternalMasterPasswordServiceAbstraction, + private messagingService: MessagingService, + private organizationApiService: OrganizationApiServiceAbstraction, + private policyApiService: PolicyApiServiceAbstraction, + private router: Router, + private setInitialPasswordService: SetInitialPasswordService, + private ssoLoginService: SsoLoginServiceAbstraction, + private syncService: SyncService, + private toastService: ToastService, + private validationService: ValidationService, + ) {} + + async ngOnInit() { + await this.syncService.fullSync(true); + + const activeAccount = await firstValueFrom(this.accountService.activeAccount$); + this.userId = activeAccount?.id; + this.email = activeAccount?.email; + + await this.determineUserType(); + await this.handleQueryParams(); + + this.initializing = false; + } + + private async determineUserType() { + if (!this.userId) { + throw new Error("userId not found. Could not determine user type."); + } + + this.forceSetPasswordReason = await firstValueFrom( + this.masterPasswordService.forceSetPasswordReason$(this.userId), + ); + + if ( + this.forceSetPasswordReason === + ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission + ) { + this.userType = SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP; + this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ + pageTitle: { key: "setMasterPassword" }, + pageSubtitle: { key: "orgPermissionsUpdatedMustSetPassword" }, + }); + } else { + this.userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; + this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ + pageTitle: { key: "joinOrganization" }, + pageSubtitle: { key: "finishJoiningThisOrganizationBySettingAMasterPassword" }, + }); + } + } + + private async handleQueryParams() { + if (!this.userId) { + throw new Error("userId not found. Could not handle query params."); + } + + const qParams = await firstValueFrom(this.activatedRoute.queryParams); + + this.orgSsoIdentifier = + qParams.identifier ?? + (await this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(this.userId)); + + if (this.orgSsoIdentifier != null) { + try { + const autoEnrollStatus = await this.organizationApiService.getAutoEnrollStatus( + this.orgSsoIdentifier, + ); + this.orgId = autoEnrollStatus.id; + this.resetPasswordAutoEnroll = autoEnrollStatus.resetPasswordEnabled; + this.masterPasswordPolicyOptions = + await this.policyApiService.getMasterPasswordPolicyOptsForOrgUser(this.orgId); + } catch { + this.toastService.showToast({ + variant: "error", + title: "", + message: this.i18nService.t("errorOccurred"), + }); + } + } + } + + protected async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) { + this.submitting = true; + + if (!passwordInputResult.newMasterKey) { + throw new Error("newMasterKey not found. Could not set initial password."); + } + if (!passwordInputResult.newServerMasterKeyHash) { + throw new Error("newServerMasterKeyHash not found. Could not set initial password."); + } + if (!passwordInputResult.newLocalMasterKeyHash) { + throw new Error("newLocalMasterKeyHash not found. Could not set initial password."); + } + // newPasswordHint can have an empty string as a valid value, so we specifically check for null or undefined + if (passwordInputResult.newPasswordHint == null) { + throw new Error("newPasswordHint not found. Could not set initial password."); + } + if (!passwordInputResult.kdfConfig) { + throw new Error("kdfConfig not found. Could not set initial password."); + } + if (!this.userId) { + throw new Error("userId not found. Could not set initial password."); + } + if (!this.userType) { + throw new Error("userType not found. Could not set initial password."); + } + if (!this.orgSsoIdentifier) { + throw new Error("orgSsoIdentifier not found. Could not set initial password."); + } + if (!this.orgId) { + throw new Error("orgId not found. Could not set initial password."); + } + // resetPasswordAutoEnroll can have `false` as a valid value, so we specifically check for null or undefined + if (this.resetPasswordAutoEnroll == null) { + throw new Error("resetPasswordAutoEnroll not found. Could not set initial password."); + } + + try { + const credentials: SetInitialPasswordCredentials = { + newMasterKey: passwordInputResult.newMasterKey, + newServerMasterKeyHash: passwordInputResult.newServerMasterKeyHash, + newLocalMasterKeyHash: passwordInputResult.newLocalMasterKeyHash, + newPasswordHint: passwordInputResult.newPasswordHint, + kdfConfig: passwordInputResult.kdfConfig, + orgSsoIdentifier: this.orgSsoIdentifier, + orgId: this.orgId, + resetPasswordAutoEnroll: this.resetPasswordAutoEnroll, + }; + + await this.setInitialPasswordService.setInitialPassword( + credentials, + this.userType, + this.userId, + ); + + this.showSuccessToastByUserType(); + + this.submitting = false; + await this.router.navigate(["vault"]); + } catch (e) { + this.validationService.showError(e); + this.submitting = false; + } + } + + private showSuccessToastByUserType() { + if (this.userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("accountSuccessfullyCreated"), + }); + + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("inviteAccepted"), + }); + } + + if ( + this.userType === + SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP + ) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("masterPasswordSuccessfullySet"), + }); + } + } + + protected async logout() { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "logOut" }, + content: { key: "logOutConfirmation" }, + acceptButtonText: { key: "logOut" }, + type: "warning", + }); + + if (confirmed) { + this.messagingService.send("logout"); + } + } +} diff --git a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts new file mode 100644 index 00000000000..e594053a906 --- /dev/null +++ b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts @@ -0,0 +1,64 @@ +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey } from "@bitwarden/common/types/key"; +import { KdfConfig } from "@bitwarden/key-management"; + +export const _SetInitialPasswordUserType = { + /** + * A user being "just-in-time" (JIT) provisioned into a master-password-encryption org + */ + JIT_PROVISIONED_MP_ORG_USER: "jit_provisioned_mp_org_user", + + /** + * Could be one of two scenarios: + * 1. A user being "just-in-time" (JIT) provisioned into a trusted-device-encryption org + * with the reset password permission granted ("manage account recovery"), which requires + * that the user sets a master password + * 2. An user in a trusted-device-encryption org whose permissions were upgraded to include + * the reset password permission ("manage account recovery"), which requires that the user + * sets a master password + */ + TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP: + "tde_org_user_reset_password_permission_requires_mp", +} as const; + +type _SetInitialPasswordUserType = typeof _SetInitialPasswordUserType; + +export type SetInitialPasswordUserType = + _SetInitialPasswordUserType[keyof _SetInitialPasswordUserType]; +export const SetInitialPasswordUserType: Readonly<{ + [K in keyof typeof _SetInitialPasswordUserType]: SetInitialPasswordUserType; +}> = Object.freeze(_SetInitialPasswordUserType); + +export interface SetInitialPasswordCredentials { + newMasterKey: MasterKey; + newServerMasterKeyHash: string; + newLocalMasterKeyHash: string; + newPasswordHint: string; + kdfConfig: KdfConfig; + orgSsoIdentifier: string; + orgId: string; + resetPasswordAutoEnroll: boolean; +} + +/** + * Handles setting an initial password for an existing authed user. + * + * To see the different scenarios where an existing authed user needs to set an + * initial password, see {@link SetInitialPasswordUserType} + */ +export abstract class SetInitialPasswordService { + /** + * Sets an initial password for an existing authed user who is either: + * - {@link SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER} + * - {@link SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP} + * + * @param credentials An object of the credentials needed to set the initial password + * @throws If any property on the `credentials` object is null or undefined, or if a + * masterKeyEncryptedUserKey or newKeyPair could not be created. + */ + abstract setInitialPassword: ( + credentials: SetInitialPasswordCredentials, + userType: SetInitialPasswordUserType, + userId: UserId, + ) => Promise; +} diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 780604f048d..96a95de501e 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -339,6 +339,8 @@ import { VaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; +import { DefaultSetInitialPasswordService } from "../auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; +import { SetInitialPasswordService } from "../auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction"; import { DeviceTrustToastService } from "../auth/services/device-trust-toast.service.implementation"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; @@ -1419,6 +1421,22 @@ const safeProviders: SafeProvider[] = [ InternalUserDecryptionOptionsServiceAbstraction, ], }), + safeProvider({ + provide: SetInitialPasswordService, + useClass: DefaultSetInitialPasswordService, + deps: [ + ApiServiceAbstraction, + EncryptService, + I18nServiceAbstraction, + KdfConfigService, + KeyService, + MasterPasswordApiServiceAbstraction, + InternalMasterPasswordServiceAbstraction, + OrganizationApiServiceAbstraction, + OrganizationUserApiService, + InternalUserDecryptionOptionsServiceAbstraction, + ], + }), safeProvider({ provide: DefaultServerSettingsService, useClass: DefaultServerSettingsService, From 6b627502bea44314910d64ddb1915c23fdbec824 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Tue, 1 Jul 2025 15:52:04 +0200 Subject: [PATCH 57/57] [PM-18668] Create importer for xml files from Password Depot 17 (#14760) * Create importer for xml file from Password Depot 17 - Create importer - Create test data - Create unit tests * Add support for parsing credit cards * Update comment on importer class * Wire up the importer to be selectable in the UI * Import and set favorites based on export file * Refactor: Extract method for parsing login fields * Parse comment on credit cards * Add support for parsing identity records * Add support for parsing rdp records * Remove html decoding of password field * Include setting credit card brand * Create type to describe the different source item types * Add support for SoftwareLicense item type * Add support for teamviewer item type * Add support for Putty item type * Skip processing historical entries * Add support for banking item type * Add support for information item type * Add support for certificate into login type * Rename encrypted-file.xml to noop-encrypted-file due to a source type with the same name * Add support for encrypted file item type * Add support for document type * Add mapping of source field types to bitwarden custom field types * Remove duplicate code (copy-pasta mistake) * Added missing docs * Adding fallback to support MacOS Password Depot 17 xml files Instead of a version node they have a dataformat node which contains the file format version * Add support to parse export files from the MacOS client * Skip creating a folder if it has no name * Fix recognition and assignment to folders/collections --------- Co-authored-by: Daniel James Smith --- .../src/components/import.component.html | 6 + libs/importer/src/importers/index.ts | 1 + .../src/importers/password-depot/index.ts | 1 + ...ssword-depot-17-macos-xml-importer.spec.ts | 62 +++ .../password-depot-17-xml-importer.spec.ts | 496 +++++++++++++++++ .../password-depot-17-xml-importer.ts | 500 ++++++++++++++++++ .../importers/password-depot/types/index.ts | 2 + .../types/password-depot-custom-field-type.ts | 15 + .../types/password-depot-item-type.ts | 19 + .../password-depot-xml/banking.xml.ts | 283 ++++++++++ .../password-depot-xml/certificate.xml.ts | 76 +++ .../password-depot-xml/credit-card.xml.ts | 148 ++++++ .../password-depot-xml/document.xml.ts | 99 ++++ .../password-depot-xml/encrypted-file.xml.ts | 76 +++ .../password-depot-xml/identity.xml.ts | 197 +++++++ .../spec-data/password-depot-xml/index.ts | 19 + .../password-depot-xml/information.xml.ts | 85 +++ .../macos-customfields.xml.ts | 42 ++ .../macos-multiple-folders.xml.ts | 215 ++++++++ .../macos-wrong-version.xml.ts | 21 + .../missing-passwords-node.xml.ts | 25 + .../missing-root-node.xml.ts | 28 + .../noop-encrypted-file.xml.ts | 27 + .../password-depot-xml/password.xml.ts | 222 ++++++++ .../spec-data/password-depot-xml/putty.xml.ts | 106 ++++ .../spec-data/password-depot-xml/rdp.xml.ts | 76 +++ .../software-license.xml.ts | 169 ++++++ .../password-depot-xml/team-viewer.xml.ts | 85 +++ .../password-depot-xml/wrong-version.xml.ts | 28 + libs/importer/src/models/import-options.ts | 1 + libs/importer/src/services/import.service.ts | 3 + 31 files changed, 3133 insertions(+) create mode 100644 libs/importer/src/importers/password-depot/index.ts create mode 100644 libs/importer/src/importers/password-depot/password-depot-17-macos-xml-importer.spec.ts create mode 100644 libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts create mode 100644 libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts create mode 100644 libs/importer/src/importers/password-depot/types/index.ts create mode 100644 libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts create mode 100644 libs/importer/src/importers/password-depot/types/password-depot-item-type.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/index.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts create mode 100644 libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html index 59ab6739c06..f6d6603dca9 100644 --- a/libs/importer/src/components/import.component.html +++ b/libs/importer/src/components/import.component.html @@ -435,6 +435,12 @@ and select Export items → Enter your Master Password and select Continue. → Save the CSV file on your device. + + On the desktop application, go to Tools → Export → Enter your master password + → Select XML Format (*.xml) as Export format → Click on next → Choose which + entries should be included in the export → Click on next to export into the location + previously chosen. + { + it("should return error with invalid export version", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(MacOS_WrongVersion); + expect(result.errorMessage).toBe( + "Unsupported export version detected - (only 17.0 is supported)", + ); + }); + + it("should not create a folder/collection if the group fingerprint is null", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(MacOS_PasswordDepotXmlFile); + expect(result.folders.length).toBe(0); + }); + + it("should create folders and with correct assignments", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(MacOS_MultipleFolders); + + // Expect 10 ciphers, 5 without a folder and 3 within 'folder macos' and 2 with 'folder 2' + expect(result.ciphers.length).toBe(10); + + expect(result.folders.length).toBe(2); + expect(result.folders[0].name).toBe("folder macos"); + expect(result.folders[1].name).toBe("folder 2"); + + // 3 items within 'folder macos' + expect(result.folderRelationships[0]).toEqual([5, 0]); + expect(result.folderRelationships[1]).toEqual([6, 0]); + expect(result.folderRelationships[2]).toEqual([7, 0]); + + //2 items with 'folder 2' + expect(result.folderRelationships[3]).toEqual([8, 1]); + expect(result.folderRelationships[4]).toEqual([9, 1]); + }); + + it("should parse custom fields from a MacOS exported file", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(MacOS_PasswordDepotXmlFile); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toBe("card 1"); + expect(cipher.notes).toBe("comment"); + + expect(cipher.card).not.toBeNull(); + + expect(cipher.card.cardholderName).toBe("some CC holder"); + expect(cipher.card.number).toBe("4242424242424242"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.expMonth).toBe("8"); + expect(cipher.card.expYear).toBe("2028"); + expect(cipher.card.code).toBe("125"); + }); +}); diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts new file mode 100644 index 00000000000..ea84603aef4 --- /dev/null +++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts @@ -0,0 +1,496 @@ +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { CollectionView } from "@bitwarden/admin-console/common"; +import { FieldType, SecureNoteType } from "@bitwarden/common/vault/enums"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { CipherType } from "@bitwarden/sdk-internal"; + +import { + EncryptedFileData, + InvalidRootNodeData, + InvalidVersionData, + CreditCardTestData, + MissingPasswordsNodeData, + PasswordTestData, + IdentityTestData, + RDPTestData, + SoftwareLicenseTestData, + TeamViewerTestData, + PuttyTestData, + BankingTestData, + InformationTestData, + CertificateTestData, + EncryptedFileTestData, + DocumentTestData, +} from "../spec-data/password-depot-xml"; + +import { PasswordDepot17XmlImporter } from "./password-depot-17-xml-importer"; + +describe("Password Depot 17 Xml Importer", () => { + it("should return error with missing root tag", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(InvalidRootNodeData); + expect(result.errorMessage).toBe("Missing `passwordfile` node."); + }); + + it("should return error with invalid export version", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(InvalidVersionData); + expect(result.errorMessage).toBe( + "Unsupported export version detected - (only 17.0 is supported)", + ); + }); + + it("should return error if file is marked as encrypted", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(EncryptedFileData); + expect(result.errorMessage).toBe("Encrypted Password Depot files are not supported."); + }); + + it("should return error with missing passwords node tag", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(MissingPasswordsNodeData); + expect(result.success).toBe(false); + expect(result.errorMessage).toBe("Missing `passwordfile > passwords` node."); + }); + + it("should parse groups nodes into folders", async () => { + const importer = new PasswordDepot17XmlImporter(); + const folder = new FolderView(); + folder.name = "tempDB"; + const actual = [folder]; + + const result = await importer.parse(PasswordTestData); + expect(result.folders).toEqual(actual); + }); + + it("should parse password type into logins", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(PasswordTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("password type"); + expect(cipher.notes).toBe("someComment"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.password).toBe("p6J<]fmjv!:H&iJ7/Mwt@3i8"); + expect(cipher.login.uri).toBe("http://example.com"); + }); + + it("should parse any unmapped fields as custom fields", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(PasswordTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.name).toBe("password type"); + + expect(cipher.fields).not.toBeNull(); + + expect(cipher.fields[0].name).toBe("lastmodified"); + expect(cipher.fields[0].value).toBe("07.05.2025 13:37:56"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[1].name).toBe("expirydate"); + expect(cipher.fields[1].value).toBe("07.05.2025"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[2].name).toBe("importance"); + expect(cipher.fields[2].value).toBe("0"); + + let customField = cipher.fields.find((f) => f.name === "passwort"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("password"); + expect(customField.type).toEqual(FieldType.Hidden); + + customField = cipher.fields.find((f) => f.name === "memo"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("memo"); + expect(customField.type).toEqual(FieldType.Text); + + customField = cipher.fields.find((f) => f.name === "datum"); + expect(customField).toBeDefined(); + const expectedDate = new Date("2025-05-13T00:00:00Z"); + expect(customField.value).toEqual(expectedDate.toLocaleDateString()); + expect(customField.type).toEqual(FieldType.Text); + + customField = cipher.fields.find((f) => f.name === "nummer"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1"); + expect(customField.type).toEqual(FieldType.Text); + + customField = cipher.fields.find((f) => f.name === "boolean"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1"); + expect(customField.type).toEqual(FieldType.Boolean); + + customField = cipher.fields.find((f) => f.name === "decimal"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1,01"); + expect(customField.type).toEqual(FieldType.Text); + + customField = cipher.fields.find((f) => f.name === "email"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("who@cares.com"); + expect(customField.type).toEqual(FieldType.Text); + + customField = cipher.fields.find((f) => f.name === "url"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("example.com"); + expect(customField.type).toEqual(FieldType.Text); + }); + + it("should parse credit cards", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(CreditCardTestData); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toBe("some CreditCard"); + expect(cipher.notes).toBe("someComment"); + + expect(cipher.card).not.toBeNull(); + + expect(cipher.card.cardholderName).toBe("some CC holder"); + expect(cipher.card.number).toBe("4222422242224222"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.expMonth).toBe("5"); + expect(cipher.card.expYear).toBe("2026"); + expect(cipher.card.code).toBe("123"); + }); + + it("should parse identity type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(IdentityTestData); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toBe("identity type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.identity).not.toBeNull(); + + expect(cipher.identity.firstName).toBe("firstName"); + expect(cipher.identity.lastName).toBe("surName"); + expect(cipher.identity.email).toBe("email"); + expect(cipher.identity.company).toBe("someCompany"); + expect(cipher.identity.address1).toBe("someStreet"); + expect(cipher.identity.address2).toBe("address 2"); + expect(cipher.identity.city).toBe("town"); + expect(cipher.identity.state).toBe("state"); + expect(cipher.identity.postalCode).toBe("zipCode"); + expect(cipher.identity.country).toBe("country"); + expect(cipher.identity.phone).toBe("phoneNumber"); + }); + + it("should parse RDP type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(RDPTestData); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("rdp type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.password).toBe("somePassword"); + expect(cipher.login.uri).toBe("ms-rd:subscribe?url=https://contoso.com"); + }); + + it("should parse software license into secure notes", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(SoftwareLicenseTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toBe("software-license type"); + expect(cipher.notes).toBe("someComment"); + + expect(cipher.secureNote).not.toBeNull(); + expect(cipher.secureNote.type).toBe(SecureNoteType.Generic); + + let customField = cipher.fields.find((f) => f.name === "IDS_LicenseProduct"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("someProduct"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseVersion"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("someVersion"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseName"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("some User"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseKey"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("license-key"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseAdditionalKey"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("additional-license-key"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseURL"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("example.com"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseProtected"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseUserName"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("someUserName"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicensePassword"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("somePassword"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicensePurchaseDate"); + expect(customField).toBeDefined(); + const expectedDate = new Date("2025-05-12T00:00:00Z"); + expect(customField.value).toEqual(expectedDate.toLocaleDateString()); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseOrderNumber"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("order number"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseEmail"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("someEmail"); + + customField = cipher.fields.find((f) => f.name === "IDS_LicenseExpires"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("Nie"); + }); + + it("should parse team viewer into login type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(TeamViewerTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("TeamViewer type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.password).toBe("somePassword"); + expect(cipher.login.username).toBe(""); + expect(cipher.login.uri).toBe("partnerId"); + + const customField = cipher.fields.find((f) => f.name === "IDS_TeamViewerMode"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("0"); + }); + + it("should parse putty into login type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(PuttyTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("Putty type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.password).toBe("somePassword"); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.uri).toBe("localhost"); + + let customField = cipher.fields.find((f) => f.name === "IDS_PuTTyProtocol"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("0"); + + customField = cipher.fields.find((f) => f.name === "IDS_PuTTyKeyFile"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("pathToKeyFile"); + + customField = cipher.fields.find((f) => f.name === "IDS_PuTTyKeyPassword"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("passwordForKeyFile"); + + customField = cipher.fields.find((f) => f.name === "IDS_PuTTyPort"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("8080"); + }); + + it("should parse banking item type into login type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(BankingTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("banking type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.password).toBe("somePassword"); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.uri).toBe("http://some-bank.com"); + + let customField = cipher.fields.find((f) => f.name === "IDS_ECHolder"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("account holder"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECAccountNumber"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1234567890"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECBLZ"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("12345678"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECBankName"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("someBank"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECBIC"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("bic"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECIBAN"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("iban"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECCardNumber"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("12345678"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECPhone"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("0049"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECLegitimacyID"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1234"); + + customField = cipher.fields.find((f) => f.name === "IDS_ECPIN"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("123"); + + customField = cipher.fields.find((f) => f.name === "tan_1_value"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("1234"); + + customField = cipher.fields.find((f) => f.name === "tan_1_used"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("12.05.2025 15:10:54"); + + // TAN entries + customField = cipher.fields.find((f) => f.name === "tan_1_amount"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual(" 100,00"); + + customField = cipher.fields.find((f) => f.name === "tan_1_comment"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("some TAN note"); + + customField = cipher.fields.find((f) => f.name === "tan_1_ccode"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("123"); + + customField = cipher.fields.find((f) => f.name === "tan_2_value"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("4321"); + + customField = cipher.fields.find((f) => f.name === "tan_2_amount"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual(" 0,00"); + }); + + it("should parse information into secure note type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(InformationTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toBe("information type"); + expect(cipher.notes).toBe("some note content"); + }); + + it("should parse certificate into login type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(CertificateTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("certificate type"); + expect(cipher.notes).toBe("someNote"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.password).toBe("somePassword"); + }); + + it("should parse encrypted file into login type", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(EncryptedFileTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toBe("encrypted file type"); + expect(cipher.notes).toBe("some comment"); + + expect(cipher.login).not.toBeNull(); + expect(cipher.login.password).toBe("somePassword"); + }); + + it("should parse document type into secure note", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(DocumentTestData); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toBe("document type"); + expect(cipher.notes).toBe("document comment"); + + let customField = cipher.fields.find((f) => f.name === "IDS_DocumentSize"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("27071"); + + customField = cipher.fields.find((f) => f.name === "IDS_DocumentFolder"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("C:\\Users\\DJSMI\\Downloads\\"); + + customField = cipher.fields.find((f) => f.name === "IDS_DocumentFile"); + expect(customField).toBeDefined(); + expect(customField.value).toEqual("C:\\Users\\DJSMI\\Downloads\\some.pdf"); + }); + + it("should parse favourites and set them on the target item", async () => { + const importer = new PasswordDepot17XmlImporter(); + const result = await importer.parse(PasswordTestData); + + let cipher = result.ciphers.shift(); + expect(cipher.name).toBe("password type"); + expect(cipher.favorite).toBe(false); + + cipher = result.ciphers.shift(); + expect(cipher.name).toBe("password type (2)"); + expect(cipher.favorite).toBe(true); + + cipher = result.ciphers.shift(); + expect(cipher.name).toBe("password type (3)"); + expect(cipher.favorite).toBe(true); + }); + + it("should parse groups nodes into collections when importing into an organization", async () => { + const importer = new PasswordDepot17XmlImporter(); + importer.organizationId = "someOrgId"; + const collection = new CollectionView(); + collection.name = "tempDB"; + const actual = [collection]; + + const result = await importer.parse(PasswordTestData); + expect(result.collections).toEqual(actual); + }); +}); diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts new file mode 100644 index 00000000000..ab1f6b4689f --- /dev/null +++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts @@ -0,0 +1,500 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { CipherType, FieldType, SecureNoteType } from "@bitwarden/common/vault/enums"; +import { CardView } from "@bitwarden/common/vault/models/view/card.view"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; +import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; +import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view"; + +import { ImportResult } from "../../models/import-result"; +import { BaseImporter } from "../base-importer"; +import { Importer } from "../importer"; + +import { PasswordDepotItemType, PasswordDepotCustomFieldType } from "./types"; + +/** + * Importer for Password Depot 17 xml files. + * @see https://www.password-depot.de/ + * It provides methods to parse the XML data, extract relevant information, and create cipher objects + */ +export class PasswordDepot17XmlImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + _favouritesLookupTable = new Set(); + + // Parse the XML data from the Password Depot export file and extracts the relevant information + parse(data: string): Promise { + const doc: XMLDocument = this.parseXml(data); + if (doc == null) { + this.result.success = false; + return Promise.resolve(this.result); + } + + // Check if the root node is present + const rootNode = doc.querySelector("passwordfile"); + if (rootNode == null) { + this.result.errorMessage = "Missing `passwordfile` node."; + this.result.success = false; + return Promise.resolve(this.result); + } + + // Check if the version is supported + const headerNode = this.querySelectorDirectChild(rootNode, "header"); + if (headerNode == null) { + this.result.success = false; + return Promise.resolve(this.result); + } + + let versionNode = this.querySelectorDirectChild(headerNode, "version"); + if (versionNode == null) { + // Adding a fallback for MacOS Password Depot 17.0 export files + // These files do not have a version node, but a dataformat node instead + versionNode = this.querySelectorDirectChild(headerNode, "dataformat"); + if (versionNode == null) { + this.result.success = false; + return Promise.resolve(this.result); + } + } + + const version = versionNode.textContent; + if (!version.startsWith("17")) { + this.result.errorMessage = "Unsupported export version detected - (only 17.0 is supported)"; + this.result.success = false; + return Promise.resolve(this.result); + } + + // Abort import if the file is encrypted + const encryptedNode = this.querySelectorDirectChild(headerNode, "encrypted"); + if (encryptedNode != null && encryptedNode.textContent == "True") { + this.result.errorMessage = "Encrypted Password Depot files are not supported."; + this.result.success = false; + return Promise.resolve(this.result); + } + + // Check if the passwords node is present + // This node contains all the password entries + const passwordsNode = rootNode.querySelector("passwords"); + if (passwordsNode == null) { + this.result.errorMessage = "Missing `passwordfile > passwords` node."; + this.result.success = false; + return Promise.resolve(this.result); + } + + this.buildFavouritesLookupTable(rootNode); + + this.querySelectorAllDirectChild(passwordsNode, "group").forEach((group) => { + this.traverse(group, ""); + }); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + // Traverses the XML tree and processes each node + // It starts from the root node and goes through each group and item + // This method is recursive and handles nested groups + private traverse(node: Element, groupPrefixName: string) { + const folderIndex = this.result.folders.length; + let groupName = groupPrefixName; + + if (groupName !== "") { + groupName += "/"; + } + + // Check if the group has a fingerprint attribute (GUID of a folder) + const groupFingerprint = node.attributes.getNamedItem("fingerprint"); + if (groupFingerprint?.textContent != "" && groupFingerprint.textContent != "null") { + const nameEl = node.attributes.getNamedItem("name"); + groupName += nameEl == null ? "-" : nameEl.textContent; + const folder = new FolderView(); + folder.name = groupName; + this.result.folders.push(folder); + } + + this.querySelectorAllDirectChild(node, "item").forEach((entry) => { + const cipherIndex = this.result.ciphers.length; + + const cipher = this.initLoginCipher(); + //Set default item type similar how we default to Login in other importers + let sourceType: PasswordDepotItemType = PasswordDepotItemType.Password; + + const entryFields = entry.children; + for (let i = 0; i < entryFields.length; i++) { + const entryField = entryFields[i]; + + // Skip processing historical entries + if (entryField.tagName === "hitems") { + continue; + } + + if (entryField.tagName === "description") { + cipher.name = entryField.textContent; + continue; + } + + if (entryField.tagName === "comment") { + cipher.notes = entryField.textContent; + continue; + } + + if (entryField.tagName === "type") { + sourceType = entryField.textContent as PasswordDepotItemType; + switch (sourceType) { + case PasswordDepotItemType.Password: + case PasswordDepotItemType.RDP: + case PasswordDepotItemType.Putty: + case PasswordDepotItemType.TeamViewer: + case PasswordDepotItemType.Banking: + case PasswordDepotItemType.Certificate: + case PasswordDepotItemType.EncryptedFile: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + break; + case PasswordDepotItemType.CreditCard: + cipher.type = CipherType.Card; + cipher.card = new CardView(); + break; + case PasswordDepotItemType.SoftwareLicense: + case PasswordDepotItemType.Information: + case PasswordDepotItemType.Document: + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + case PasswordDepotItemType.Identity: + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + break; + } + continue; + } + + if ( + sourceType === PasswordDepotItemType.Password || + sourceType === PasswordDepotItemType.RDP || + sourceType === PasswordDepotItemType.Putty || + sourceType === PasswordDepotItemType.TeamViewer || + sourceType === PasswordDepotItemType.Banking || + sourceType === PasswordDepotItemType.Certificate || + sourceType === PasswordDepotItemType.EncryptedFile + ) { + if (this.parseLoginFields(entryField, cipher)) { + continue; + } + } + + // fingerprint is the GUID of the entry + // Base on the previously parsed favourites, we can identify an entry and set the favorite flag accordingly + if (entryField.tagName === "fingerprint") { + if (this._favouritesLookupTable.has(entryField.textContent)) { + cipher.favorite = true; + } + } + + if (entryField.tagName === "customfields") { + this.parseCustomFields(entryField, sourceType, cipher); + continue; + } + + if (sourceType === PasswordDepotItemType.Banking && entryField.tagName === "tans") { + this.querySelectorAllDirectChild(entryField, "tan").forEach((tanEntry) => { + this.parseBankingTANs(tanEntry, cipher); + }); + continue; + } + + this.processKvp(cipher, entryField.tagName, entryField.textContent, FieldType.Text); + } + + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + + if (groupName !== "") { + this.result.folderRelationships.push([cipherIndex, folderIndex]); + } + }); + + this.querySelectorAllDirectChild(node, "group").forEach((group) => { + this.traverse(group, groupName); + }); + } + + // Parses custom fields and adds them to the cipher + // It iterates through all the custom fields and adds them to the cipher + private parseCustomFields( + entryField: Element, + sourceType: PasswordDepotItemType, + cipher: CipherView, + ) { + this.querySelectorAllDirectChild(entryField, "field").forEach((customField) => { + const customFieldObject = this.parseCustomField(customField); + if (customFieldObject == null) { + return; + } + + switch (sourceType) { + case PasswordDepotItemType.CreditCard: + if (this.parseCreditCardCustomFields(customFieldObject, cipher)) { + return; + } + break; + case PasswordDepotItemType.Identity: + if (this.parseIdentityCustomFields(customFieldObject, cipher)) { + return; + } + break; + case PasswordDepotItemType.Information: + if (this.parseInformationCustomFields(customFieldObject, cipher)) { + return; + } + break; + default: + // For other types, we will process the custom field as a regular key-value pair + break; + } + + this.processKvp( + cipher, + customFieldObject.name, + customFieldObject.value, + customFieldObject.type, + ); + }); + } + + // Parses login fields and adds them to the cipher + private parseLoginFields(entryField: Element, cipher: CipherView): boolean { + if (entryField.tagName === "username") { + cipher.login.username = entryField.textContent; + return true; + } + + if (entryField.tagName === "password") { + cipher.login.password = entryField.textContent; + return true; + } + + if (entryField.tagName === "url") { + cipher.login.uris = this.makeUriArray(entryField.textContent); + return true; + } + + return false; + } + + // Parses a custom field and adds it to the cipher + private parseCustomField(customField: Element): FieldView | null { + let key: string = undefined; + let value: string = undefined; + let sourceFieldType: PasswordDepotCustomFieldType = PasswordDepotCustomFieldType.Memo; + let visible: string = undefined; + // A custom field is represented by a element + // On exports from the Windows clients: It contains a , , and optionally a and element + // On exports from the MacOs clients the key-values are defined as xml attributes instead of child nodes + if (customField.hasAttributes()) { + key = customField.getAttribute("name"); + if (key == null) { + return null; + } + + value = customField.getAttribute("value"); + + const typeAttr = customField.getAttribute("type"); + sourceFieldType = + typeAttr != null + ? (typeAttr as PasswordDepotCustomFieldType) + : PasswordDepotCustomFieldType.Memo; + + visible = customField.getAttribute("visible"); + } else { + const keyEl = this.querySelectorDirectChild(customField, "name"); + key = keyEl != null ? keyEl.textContent : null; + + if (key == null) { + return null; + } + + const valueEl = this.querySelectorDirectChild(customField, "value"); + value = valueEl != null ? valueEl.textContent : null; + + const typeEl = this.querySelectorDirectChild(customField, "type"); + sourceFieldType = + typeEl != null + ? (typeEl.textContent as PasswordDepotCustomFieldType) + : PasswordDepotCustomFieldType.Memo; + + const visibleEl = this.querySelectorDirectChild(customField, "visible"); + visible = visibleEl != null ? visibleEl.textContent : null; + } + + if (sourceFieldType === PasswordDepotCustomFieldType.Date) { + if (!isNaN(value as unknown as number)) { + // Convert excel date format to JavaScript date + const numericValue = parseInt(value); + const secondsInDay = 86400; + const missingLeapYearDays = secondsInDay * 1000; + value = new Date((numericValue - (25567 + 2)) * missingLeapYearDays).toLocaleDateString(); + } + } + + if (sourceFieldType === PasswordDepotCustomFieldType.Password) { + return { name: key, value: value, type: FieldType.Hidden, linkedId: null } as FieldView; + } + + if (sourceFieldType === PasswordDepotCustomFieldType.Boolean) { + return { name: key, value: value, type: FieldType.Boolean, linkedId: null } as FieldView; + } + + if (visible == "0") { + return { name: key, value: value, type: FieldType.Hidden, linkedId: null } as FieldView; + } + + return { name: key, value: value, type: FieldType.Text, linkedId: null } as FieldView; + } + + // Parses credit card fields and adds them to the cipher + private parseCreditCardCustomFields(customField: FieldView, cipher: CipherView): boolean { + if (customField.name === "IDS_CardHolder") { + cipher.card.cardholderName = customField.value; + return true; + } + + if (customField.name === "IDS_CardNumber") { + cipher.card.number = customField.value; + cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number); + return true; + } + + if (customField.name === "IDS_CardExpires") { + this.setCardExpiration(cipher, customField.value); + return true; + } + + if (customField.name === "IDS_CardCode") { + cipher.card.code = customField.value; + return true; + } + + return false; + } + + // Parses identity fields and adds them to the cipher + private parseIdentityCustomFields(customField: FieldView, cipher: CipherView): boolean { + if (customField.name === "IDS_IdentityName") { + this.processFullName(cipher, customField.value); + return true; + } + + if (customField.name === "IDS_IdentityEmail") { + cipher.identity.email = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityFirstName") { + cipher.identity.firstName = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityLastName") { + cipher.identity.lastName = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityCompany") { + cipher.identity.company = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityAddress1") { + cipher.identity.address1 = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityAddress2") { + cipher.identity.address2 = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityCity") { + cipher.identity.city = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityState") { + cipher.identity.state = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityZIP") { + cipher.identity.postalCode = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityCountry") { + cipher.identity.country = customField.value; + return true; + } + + if (customField.name === "IDS_IdentityPhone") { + cipher.identity.phone = customField.value; + return true; + } + + return false; + } + + // Parses information custom fields and adds them to the cipher + private parseInformationCustomFields(customField: FieldView, cipher: CipherView): boolean { + if (customField.name === "IDS_InformationText") { + cipher.notes = customField.value; + return true; + } + + return false; + } + + // Parses TAN objects and adds them to the cipher + // It iterates through all the TAN fields and adds them to the cipher + private parseBankingTANs(TANsField: Element, cipher: CipherView) { + let tanNumber = "0"; + const entryFields = TANsField.children; + for (let i = 0; i < entryFields.length; i++) { + const entryField = entryFields[i]; + + if (entryField.tagName === "number") { + tanNumber = entryField.textContent; + continue; + } + + this.processKvp(cipher, `tan_${tanNumber}_${entryField.tagName}`, entryField.textContent); + } + } + + // Parses the favourites-node from the XML file, which contains a base64 encoded string + // The string contains the fingerprints/GUIDs of the favourited entries, separated by new lines + private buildFavouritesLookupTable(rootNode: Element): void { + const favouritesNode = this.querySelectorDirectChild(rootNode, "favorites"); + if (favouritesNode == null) { + return; + } + + const decodedBase64String = atob(favouritesNode.textContent); + if (decodedBase64String.indexOf("\r\n") > 0) { + decodedBase64String.split("\r\n").forEach((line) => { + this._favouritesLookupTable.add(line); + }); + return; + } + + decodedBase64String.split("\n").forEach((line) => { + this._favouritesLookupTable.add(line); + }); + } +} diff --git a/libs/importer/src/importers/password-depot/types/index.ts b/libs/importer/src/importers/password-depot/types/index.ts new file mode 100644 index 00000000000..709e5992ae8 --- /dev/null +++ b/libs/importer/src/importers/password-depot/types/index.ts @@ -0,0 +1,2 @@ +export * from "./password-depot-item-type"; +export * from "./password-depot-custom-field-type"; diff --git a/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts b/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts new file mode 100644 index 00000000000..166378b38b4 --- /dev/null +++ b/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts @@ -0,0 +1,15 @@ +/** This object represents the different custom field types in Password Depot */ +export const PasswordDepotCustomFieldType = Object.freeze({ + Password: "1", + Memo: "2", + Date: "3", + Number: "4", + Boolean: "5", + Decimal: "6", + Email: "7", + URL: "8", +} as const); + +/** This type represents the different custom field types in Password Depot */ +export type PasswordDepotCustomFieldType = + (typeof PasswordDepotCustomFieldType)[keyof typeof PasswordDepotCustomFieldType]; diff --git a/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts b/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts new file mode 100644 index 00000000000..04ec3a48c85 --- /dev/null +++ b/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts @@ -0,0 +1,19 @@ +/** This object represents the different item types in Password Depot */ +export const PasswordDepotItemType = Object.freeze({ + Password: "0", + CreditCard: "1", + SoftwareLicense: "2", + Identity: "3", + Information: "4", + Banking: "5", + EncryptedFile: "6", + Document: "7", + RDP: "8", + Putty: "9", + TeamViewer: "10", + Certificate: "11", +} as const); + +/** This type represents the different item types in Password Depot */ +export type PasswordDepotItemType = + (typeof PasswordDepotItemType)[keyof typeof PasswordDepotItemType]; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts new file mode 100644 index 00000000000..3a2da1c27bf --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts @@ -0,0 +1,283 @@ +export const BankingTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + banking type + 5 + somePassword + someUser + some-bank.com + someNote + 12.05.2025 15:11:29 + 02.05.2027 + 1 + DEB91652-52C6-402E-9D44-3557829BC6DF + + 127 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:09:17 + 12.05.2025 15:09:17 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_ECHolder + account holder + -1 + 0 + 0 + + + IDS_ECAccountNumber + 1234567890 + -1 + 0 + 0 + + + IDS_ECBLZ + 12345678 + -1 + 0 + 0 + + + IDS_ECBankName + someBank + -1 + 0 + 0 + + + IDS_ECBIC + bic + -1 + 0 + 0 + + + IDS_ECIBAN + iban + -1 + 0 + 0 + + + IDS_ECCardNumber + 12345678 + -1 + 0 + 10 + + + IDS_ECPhone + 0049 + 0 + 0 + 0 + + + IDS_ECLegitimacyID + 1234 + -1 + 0 + 0 + + + IDS_ECPIN + 123 + 0 + 0 + 1 + + + + + 1 + 1234 + 12.05.2025 15:10:54 + 100,00 + some TAN note + 123 + + + 2 + 4321 + 0,00 + + + + + + + banking type + 5 + somePassword + someUser + some-bank.com + someNote + 12.05.2025 15:10:35 + 02.05.2027 + 1 + DEB91652-52C6-402E-9D44-3557829BC6DF + + 127 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:09:17 + 12.05.2025 15:09:17 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_ECHolder + account holder + -1 + 0 + 0 + + + IDS_ECAccountNumber + 1234567890 + -1 + 0 + 0 + + + IDS_ECBLZ + 12345678 + -1 + 0 + 0 + + + IDS_ECBankName + someBank + -1 + 0 + 0 + + + IDS_ECBIC + bic + -1 + 0 + 0 + + + IDS_ECIBAN + iban + -1 + 0 + 0 + + + IDS_ECCardNumber + 12345678 + -1 + 0 + 10 + + + IDS_ECPhone + 0049 + 0 + 0 + 0 + + + IDS_ECLegitimacyID + 1234 + -1 + 0 + 0 + + + IDS_ECPIN + 123 + 0 + 0 + 1 + + + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts new file mode 100644 index 00000000000..eb8c463d57f --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts @@ -0,0 +1,76 @@ +export const CertificateTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + certificate type + 11 + somePassword + + + someNote + 12.05.2025 15:15:57 + 00.00.0000 + 1 + 21288702-B042-46D9-9DDF-B44A5CD04A72 + + 130 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:15:26 + 12.05.2025 15:15:26 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts new file mode 100644 index 00000000000..f0af49bbfae --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts @@ -0,0 +1,148 @@ +export const CreditCardTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + some CreditCard + 1 + 4222422242224222 + some CC holder + + someComment + 08.05.2025 12:09:47 + 08.05.2026 + 1 + DD9B52F8-B2CE-42C2-A891-5E20DA23EA20 + + 126 + 0 + + + + + 0 + + + 08.05.2025 12:08:48 + 08.05.2025 12:08:48 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_CardType + 0 + 0 + 0 + 4 + + + IDS_CardHolder + some CC holder + -1 + 0 + 0 + + + IDS_CardNumber + 4222422242224222 + -1 + 0 + 10 + + + IDS_CardExpires + 05/2026 + -1 + 0 + 3 + + + IDS_CardCode + 123 + -1 + 0 + 0 + + + IDS_CardPhone + + -1 + 0 + 0 + + + IDS_CardURL + + -1 + 0 + 8 + + + IDS_CardAdditionalCode + + -1 + 0 + 0 + + + IDS_CardAdditionalInfo + + -1 + 0 + 0 + + + IDS_CardPIN + 123 + -1 + 0 + 1 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts new file mode 100644 index 00000000000..4f607c9b048 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts @@ -0,0 +1,99 @@ +export const DocumentTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + document type + 7 + + + + document comment + 03.06.2025 17:45:30 + 00.00.0000 + 1 + 1B8E7F2C-9229-43C6-AB89-42101809C822 + + 133 + 0 + + + Allgemein + + 0 + + + 05.06.2025 21:49:49 + 30.12.1899 00:00:00 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_DocumentSize + 27071 + 0 + 0 + 4 + + + IDS_DocumentFolder + C:\\Users\\DJSMI\\Downloads\\ + 0 + 0 + 0 + + + IDS_DocumentFile + C:\\Users\\DJSMI\\Downloads\\some.pdf + 0 + 0 + 0 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts new file mode 100644 index 00000000000..2d2e929440a --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts @@ -0,0 +1,76 @@ +export const EncryptedFileTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + encrypted file type + 6 + somePassword + + + some comment + 12.05.2025 15:15:17 + 00.00.0000 + 1 + E4CA245D-A326-4359-9488-CC207B33C6C0 + + 132 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:14:58 + 12.05.2025 15:14:58 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts new file mode 100644 index 00000000000..dfa275aa778 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts @@ -0,0 +1,197 @@ +export const IdentityTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + identity type + 3 + + account-name/id + website + someNote + 12.05.2025 15:14:33 + 00.00.0000 + 1 + 0E6085E9-7560-4826-814E-EFE1724E1377 + + 129 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:12:52 + 12.05.2025 15:12:52 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_IdentityName + account-name/id + -1 + 0 + 0 + + + IDS_IdentityEmail + email + -1 + 0 + 0 + + + IDS_IdentityFirstName + firstName + -1 + 0 + 0 + + + IDS_IdentityLastName + surName + -1 + 0 + 0 + + + IDS_IdentityCompany + someCompany + -1 + 0 + 0 + + + IDS_IdentityAddress1 + someStreet + -1 + 0 + 0 + + + IDS_IdentityAddress2 + address 2 + -1 + 0 + 0 + + + IDS_IdentityCity + town + -1 + 0 + 0 + + + IDS_IdentityState + state + -1 + 0 + 0 + + + IDS_IdentityZIP + zipCode + -1 + 0 + 0 + + + IDS_IdentityCountry + country + -1 + 0 + 0 + + + IDS_IdentityPhone + phoneNumber + -1 + 0 + 0 + + + IDS_IdentityWebsite + website + -1 + 0 + 8 + + + IDS_IdentityBirthDate + 45789 + -1 + 0 + 3 + + + IDS_IdentityMobile + mobileNumber + -1 + 0 + 0 + + + IDS_IdentityFax + faxNumber + -1 + 0 + 0 + + + IDS_IdentityHouseNumber + 123 + -1 + 0 + 0 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/index.ts b/libs/importer/src/importers/spec-data/password-depot-xml/index.ts new file mode 100644 index 00000000000..6d5903ed471 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/index.ts @@ -0,0 +1,19 @@ +export { InvalidRootNodeData } from "./missing-root-node.xml"; +export { MissingPasswordsNodeData } from "./missing-passwords-node.xml"; +export { InvalidVersionData } from "./wrong-version.xml"; +export { EncryptedFileData } from "./noop-encrypted-file.xml"; +export { PasswordTestData } from "./password.xml"; +export { CreditCardTestData } from "./credit-card.xml"; +export { IdentityTestData } from "./identity.xml"; +export { RDPTestData } from "./rdp.xml"; +export { SoftwareLicenseTestData } from "./software-license.xml"; +export { TeamViewerTestData } from "./team-viewer.xml"; +export { PuttyTestData } from "./putty.xml"; +export { BankingTestData } from "./banking.xml"; +export { InformationTestData } from "./information.xml"; +export { CertificateTestData } from "./certificate.xml"; +export { EncryptedFileTestData } from "./encrypted-file.xml"; +export { DocumentTestData } from "./document.xml"; +export { MacOS_WrongVersion } from "./macos-wrong-version.xml"; +export { MacOS_PasswordDepotXmlFile } from "./macos-customfields.xml"; +export { MacOS_MultipleFolders } from "./macos-multiple-folders.xml"; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts new file mode 100644 index 00000000000..1f07882ea64 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts @@ -0,0 +1,85 @@ +export const InformationTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + information type + 4 + + + + + 12.05.2025 15:14:54 + 00.00.0000 + 1 + 546AFAE7-6F64-4040-838B-AFE691580356 + + 131 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:14:39 + 12.05.2025 15:14:39 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_InformationText + some note content + 0 + 0 + 2 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts new file mode 100644 index 00000000000..d83eae6bb6d --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts @@ -0,0 +1,42 @@ +export const MacOS_PasswordDepotXmlFile = ` + +
+ Password Depot + 0 + 0 + 0 + 1 + 1 + 17 + 30.12.1899 00:00:00 + 23.06.2025 16:30:50 + 25.06.2025 14:30:47 + 2C1A154A-3BB0-4871-9537-3634DE303F8E + 7 +
+ + + + card 1 + 1 + comment + 23.06.2025 16:14:33 + EBF4AC3D-86C9-49BE-826B-BAE5FF9E3575 + 23.06.2025 16:13:40 + 25.06.2025 14:17:10 + + + + + + + + + + + + + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts new file mode 100644 index 00000000000..174e9415fa1 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts @@ -0,0 +1,215 @@ +export const MacOS_MultipleFolders = ` + +
+ Password Depot + 0 + 0 + 0 + 1 + 1 + 17 + 30.12.1899 00:00:00 + 27.06.2025 10:39:07 + 27.06.2025 10:39:27 + 7DCDD3FA-F512-4CD4-AEED-DE2A4C8375CF + 7 +
+ + + + remote desktop + 8 + pass + username + compjter + comment + 26.06.2025 16:04:57 + 81316050-9B9C-4D9B-9549-45B52A0BE6BB + + Private + 26.06.2025 16:04:32 + 27.06.2025 10:28:02 + tgmac + + + teamviewer + 10 + pass + partnerid + comment + 26.06.2025 16:05:28 + 26.06.2025 + 8AAACC16-4FD4-4E52-9C1F-6979B051B510 + Internet + 26.06.2025 16:05:03 + 27.06.2025 10:28:02 + tag + + + + + + ec card + 5 + pass + user + url + 26.06.2025 16:08:35 + B5C148A4-C408-427C-B69C-F88E7C529FA4 + + 26.06.2025 16:08:00 + 27.06.2025 10:28:02 + + + + + + + + + + + + + + + identity + 3 + 26.06.2025 16:09:50 + 87574AD4-8844-4A01-9381-AFF0907198A3 + 26.06.2025 16:09:19 + 27.06.2025 10:28:02 + + + + + + + + + + + + + + + + + + + + + + credit card + 1 + comment + 26.06.2025 19:07:38 + E98E3CBA-1578-48AD-8E41-CFD3280045BB + 26.06.2025 16:06:32 + 27.06.2025 10:28:02 + + + + + + + + + + + + + + + + password + passmac + usernam + comment + 26.06.2025 16:04:30 + DE8AD61B-8EC0-4E72-9BC8-971E80712B50 + + General + 26.06.2025 16:04:04 + 27.06.2025 10:28:02 + tagmac + + + informationb + 4 + 26.06.2025 16:10:01 + 7E9E6941-BB3B-47F2-9E43-33F900EBBF95 + Banking + 26.06.2025 16:09:53 + 27.06.2025 10:28:02 + + + + + + certificat + 11 + passsss + comment + 26.06.2025 16:10:28 + 26.06.2025 + 1F36748F-0374-445E-B020-282EAE26259F + Internet + 26.06.2025 16:10:10 + 27.06.2025 10:28:02 + tag + + + + + putty + 9 + pass + username + host + comment + 26.06.2025 16:06:08 + 26.06.2025 + 7947A949-98F0-4F26-BE12-5FFAFE7601C8 + + Banking + 26.06.2025 16:05:38 + 27.06.2025 10:28:02 + tag + + + + + + + + + soft license + 2 + 26.06.2025 16:09:02 + 2A5CF4C1-70D0-4F27-A1DE-4CFEF5FB71CF + 26.06.2025 16:08:43 + 27.06.2025 10:28:02 + + + + + + + + + + + + + + + + + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts new file mode 100644 index 00000000000..771bf813e48 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts @@ -0,0 +1,21 @@ +export const MacOS_WrongVersion = ` + +
+ Password Depot + 0 + 0 + 0 + 1 + 1 + 18 + 30.12.1899 00:00:00 + 23.06.2025 16:30:50 + 25.06.2025 14:30:47 + 2C1A154A-3BB0-4871-9537-3634DE303F8E + 7 +
+ + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts new file mode 100644 index 00000000000..d07beb8521c --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts @@ -0,0 +1,25 @@ +export const MissingPasswordsNodeData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + + + + + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts new file mode 100644 index 00000000000..aca2a2f6fa1 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts @@ -0,0 +1,28 @@ +export const InvalidRootNodeData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + + + + + + + + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts new file mode 100644 index 00000000000..e8050726b25 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts @@ -0,0 +1,27 @@ +export const EncryptedFileData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + True + 0 + 30.12.1899 00:00:00 + +
+ + + + + + + + + + + + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts new file mode 100644 index 00000000000..7d15fce3aa8 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts @@ -0,0 +1,222 @@ +export const PasswordTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + password type + 0 + p6J<]fmjv!:H&iJ7/Mwt@3i8 + someUser + example.com + someComment + 07.05.2025 13:37:56 + 07.05.2025 + 0 + 27ACAC2D-8DDA-4088-8D3A-E6C5F40ED46E + + 0 + 0 + + + Allgemein + + 0 + + + 07.05.2025 13:36:48 + 07.05.2025 13:36:48 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + passwort + password + -1 + 0 + 1 + + + memo + memo + -1 + 0 + 2 + + + datum + 45790 + -1 + 0 + 3 + + + nummer + 1 + -1 + 0 + 4 + + + boolean + 1 + -1 + 0 + 5 + + + decimal + 1,01 + -1 + 0 + 6 + + + email + who@cares.com + -1 + 0 + 7 + + + url + example.com + -1 + 0 + 8 + + + + + password type (2) + 0 + p6J<]fmjv!:H&iJ7/Mwt@3i8 + someUser + + someComment + 07.05.2025 13:37:56 + 07.05.2025 + 0 + AF74FF86-FE39-4584-8E96-FE950C289DF8 + + 0 + 0 + + + Allgemein + + 0 + + + 07.05.2025 13:36:48 + 07.05.2025 13:36:48 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + password type (3) + 0 + p6J<]fmjv!:H&iJ7/Mwt@3i8 + someUser + + someComment + 07.05.2025 13:37:56 + 07.05.2025 + 0 + BF74FF86-FA39-4584-8E96-FA950C249DF8 + + 0 + 0 + + + Allgemein + + 0 + + + 07.05.2025 13:36:48 + 07.05.2025 13:36:48 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4CkJGNzRGRjg2LUZBMzktNDU4NC04RTk2LUZBOTUwQzI0OURGOA== + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts new file mode 100644 index 00000000000..d878b04cd3c --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts @@ -0,0 +1,106 @@ +export const PuttyTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + Putty type + 9 + somePassword + someUser + localhost + someNote + 12.05.2025 15:09:09 + 00.00.0000 + 1 + 32207D79-B70B-4987-BC73-3F7AD75D2C63 + + 125 + 0 + + cli command + Allgemein + + 0 + + + 12.05.2025 15:08:18 + 12.05.2025 15:08:18 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_PuTTyProtocol + 0 + 0 + 0 + 0 + + + IDS_PuTTyKeyFile + pathToKeyFile + -1 + 0 + 0 + + + IDS_PuTTyKeyPassword + passwordForKeyFile + -1 + 0 + 1 + + + IDS_PuTTyPort + 8080 + -1 + 0 + 4 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts new file mode 100644 index 00000000000..fafc375f9a6 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts @@ -0,0 +1,76 @@ +export const RDPTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + rdp type + 8 + somePassword + someUser + ms-rd:subscribe?url=https://contoso.com + someNote + 12.05.2025 15:07:33 + 12.05.2025 + 1 + 24CFF328-3036-48E3-99A3-85CD337725D3 + + 123 + 0 + + commandline command + Allgemein + + 0 + + + 12.05.2025 15:06:24 + 12.05.2025 15:06:24 + 0 + 0 + 0 + 1 + + 0 + 2 + sometag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts new file mode 100644 index 00000000000..5ab9437c3d7 --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts @@ -0,0 +1,169 @@ +export const SoftwareLicenseTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + software-license type + 2 + somePassword + someUserName + example.com + someComment + 12.05.2025 15:12:48 + 00.00.0000 + 1 + 220206EB-BE82-4E78-8FFB-9316D854721F + + 128 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:11:33 + 12.05.2025 15:11:33 + 0 + 0 + 0 + 1 + + 0 + 2 + + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_LicenseProduct + someProduct + 0 + 0 + 0 + + + IDS_LicenseVersion + someVersion + 0 + 0 + 0 + + + IDS_LicenseName + some User + -1 + 0 + 0 + + + IDS_LicenseKey + license-key + -1 + 0 + 0 + + + IDS_LicenseAdditionalKey + additional-license-key + -1 + 0 + 0 + + + IDS_LicenseURL + example.com + -1 + 0 + 8 + + + IDS_LicenseProtected + 1 + 0 + 0 + 5 + + + IDS_LicenseUserName + someUserName + -1 + 0 + 0 + + + IDS_LicensePassword + somePassword + -1 + 0 + 1 + + + IDS_LicensePurchaseDate + 45789 + 0 + 0 + 3 + + + IDS_LicenseOrderNumber + order number + -1 + 0 + 0 + + + IDS_LicenseEmail + someEmail + -1 + 0 + 7 + + + IDS_LicenseExpires + Nie + 0 + 0 + 3 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts new file mode 100644 index 00000000000..911c621c59a --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts @@ -0,0 +1,85 @@ +export const TeamViewerTestData = ` + +
+ Password Depot + 17.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + TeamViewer type + 10 + somePassword + + partnerId + someNote + 12.05.2025 15:08:14 + 00.00.0000 + 1 + AE650032-5963-4D93-8E2E-69F216405C29 + + 124 + 0 + + + Allgemein + + 0 + + + 12.05.2025 15:07:41 + 12.05.2025 15:07:41 + 0 + 0 + 0 + 1 + + 0 + 2 + someTag + DJSMI + + + 0 + + 0 + + 0 + + 161 + 0 + + + + IDS_TeamViewerMode + 0 + 0 + 0 + 0 + + + + + + + + + + + QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo= + + + + + + QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg== + +
`; diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts new file mode 100644 index 00000000000..90b766ded1b --- /dev/null +++ b/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts @@ -0,0 +1,28 @@ +export const InvalidVersionData = ` + +
+ Password Depot + 18.0.0 + + + CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D + 12.05.2025 15:16:11 + False + 0 + 30.12.1899 00:00:00 + +
+ + + + + + + + + + + + + +
`; diff --git a/libs/importer/src/models/import-options.ts b/libs/importer/src/models/import-options.ts index a8c4b4e0a8a..205dbaf0198 100644 --- a/libs/importer/src/models/import-options.ts +++ b/libs/importer/src/models/import-options.ts @@ -73,6 +73,7 @@ export const regularImportOptions = [ { id: "passkyjson", name: "Passky (json)" }, { id: "passwordxpcsv", name: "Password XP (csv)" }, { id: "netwrixpasswordsecure", name: "Netwrix Password Secure (csv)" }, + { id: "passworddepot17xml", name: "Password Depot 17 (xml)" }, ] as const; export type ImportType = diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 3789ee7536c..2b9d2e490f7 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -90,6 +90,7 @@ import { YotiCsvImporter, ZohoVaultCsvImporter, PasswordXPCsvImporter, + PasswordDepot17XmlImporter, } from "../importers"; import { Importer } from "../importers/importer"; import { @@ -348,6 +349,8 @@ export class ImportService implements ImportServiceAbstraction { return new PasswordXPCsvImporter(); case "netwrixpasswordsecure": return new NetwrixPasswordSecureCsvImporter(); + case "passworddepot17xml": + return new PasswordDepot17XmlImporter(); default: return null; }