From 7fbfce953d28a40d8e7bdb8b32fa154ac8b40672 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 23 Nov 2022 17:26:57 -0500 Subject: [PATCH 01/40] [PS-1854] Split services between background and visualizations (#4075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Elevate Map <-> Record JSON helpers to Utils * Build Account from a StateService provided AccountDeserializer * Allow Manifest V2 usage of session sync Expands use of SessionSyncer to all Subject types. Correctly handles replay buffer for each type to ignore the flood of data upon subscription to each Subject type. * Create browser-synced Policy Service * Move BrowserFolderService * Libs account serialization improvements * Serialize Browser Accounts * Separate StateService in background/visualizations Visualizer state services share storages with background page, which nicely emulates mv3 synchronization through session/local storage. There should not be multithreading issues since all of these services are still running through a single thread, we just now have multiple places we are reading/writing data from. Smaller improvements * Rename browser's state service to BrowserStateService * Remove unused WithPrototype decorator :celebrate: * Removed conversion on withPrototypeForArrayMembers. It's reasonable to think that if the type is maintained, it doesn't need conversion. Eventually, we should be able to remove the withPrototypeForArrayMembers decorator as well, but that will require a bit more work on (de)serialization of the Accounts.data property. * Make Record <-> Map idempotent Should we get in a situation where we _think_ an object has been jsonified, but hasn't been, we need to correctly deal with the object received to create our target. * Check all requirements while duck typing * Name client services after the client * Use union type to limit initialize options * Fixup usages of `initializeAs` * Add OrganizationService to synced services Co-Authored-By: Daniel James Smith * Add Settings service to synced services Co-Authored-By: Daniel James Smith * Add missing BrowserStateService * Fix factories to use browser-specific service overides * Fix org-service registration in services.module * Revert "Add missing BrowserStateService" This reverts commit 81cf384e872718e86e84f9c54731e78a083e1ee3. * Fix session syncer tests * Fix synced item metadata tests * Early return null json objects * Prefer abstract service dependencies * Prefer minimal browser service overrides * [SG-632] - Change forwarded providers radio buttons list to dropdown (#4045) * SG-632 - Changed forwarded providers list of radio buttons to dropdown * SG-632 - Added role attributes to improve accessibility. * SG-632 - Added sorting to array and empty option * SG-632 - Fix styling to match standards. * rename cipehrs component to vault items component (#4081) * Update the version hash for the QA Web build artifact to follow SemVer syntax (#4102) * Remove extra call to toJSON() (#4101) Co-authored-by: Daniel James Smith Co-authored-by: Daniel James Smith Co-authored-by: Carlos Gonçalves Co-authored-by: Jake Fink Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com> Co-authored-by: Robyn MacCallum --- .../browser/src/background/idle.background.ts | 4 +- .../browser/src/background/main.background.ts | 22 ++--- .../src/background/notification.background.ts | 4 +- .../folder-service.factory.ts | 4 +- .../organization-service.factory.ts | 5 +- .../policy-service.factory.ts | 5 +- .../settings-service.factory.ts | 5 +- .../state-service.factory.ts | 8 +- apps/browser/src/clipboard/clipboard-state.ts | 6 +- ...rate-password-to-clipboard-command.spec.ts | 6 +- .../generate-password-to-clipboard-command.ts | 4 +- .../browser-session.decorator.spec.ts | 12 +-- .../browser-session.decorator.ts | 12 ++- .../session-sync.decorator.ts | 13 +-- .../session-syncer.spec.ts | 89 +++++++++++++------ .../session-sync-observable/session-syncer.ts | 46 ++++++---- .../sync-item-metadata.ts | 14 ++- .../synced-item-metadata.spec.ts | 38 ++++++-- apps/browser/src/listeners/update-badge.ts | 4 +- apps/browser/src/models/account.ts | 24 +++++ .../src/models/browserComponentState.ts | 10 +++ .../models/browserGroupingsComponentState.ts | 26 ++++++ .../src/models/browserSendComponentState.ts | 19 ++++ apps/browser/src/popup/app.component.ts | 4 +- .../src/popup/send/send-add-edit.component.ts | 4 +- .../popup/send/send-groupings.component.ts | 8 +- .../src/popup/send/send-type.component.ts | 4 +- .../src/popup/services/init.service.ts | 2 +- .../src/popup/services/services.module.ts | 73 ++++++++++++--- .../src/popup/vault/vault-filter.component.ts | 8 +- .../src/popup/vault/vault-items.component.ts | 4 +- ...te.service.ts => browser-state.service.ts} | 3 +- apps/browser/src/services/autofill.service.ts | 4 +- ...r.service.ts => browser-folder.service.ts} | 8 +- .../services/browser-organization.service.ts | 12 +++ .../src/services/browser-policy.service.ts | 12 +++ .../src/services/browser-settings.service.ts | 11 +++ ....spec.ts => browser-state.service.spec.ts} | 47 +++++----- ...te.service.ts => browser-state.service.ts} | 32 ++++--- libs/angular/test-utils.ts | 3 + libs/common/spec/misc/utils.spec.ts | 68 ++++++++++++++ libs/common/src/misc/utils.ts | 59 ++++++++++++ libs/common/src/models/domain/account.ts | 8 +- libs/common/src/models/domain/organization.ts | 13 +++ libs/common/src/models/domain/state.spec.ts | 15 ++-- libs/common/src/models/domain/state.ts | 14 +-- libs/common/src/models/view/send-file.view.ts | 9 ++ libs/common/src/models/view/send-text.view.ts | 9 ++ libs/common/src/models/view/send.view.ts | 23 +++++ libs/common/src/services/cipher.service.ts | 2 +- .../organization/organization.service.ts | 2 +- .../src/services/policy/policy.service.ts | 2 +- libs/common/src/services/settings.service.ts | 2 +- libs/common/src/services/state.service.ts | 67 +++----------- 54 files changed, 671 insertions(+), 241 deletions(-) rename apps/browser/src/services/abstractions/{state.service.ts => browser-state.service.ts} (91%) rename apps/browser/src/services/{folders/folder.service.ts => browser-folder.service.ts} (57%) create mode 100644 apps/browser/src/services/browser-organization.service.ts create mode 100644 apps/browser/src/services/browser-policy.service.ts create mode 100644 apps/browser/src/services/browser-settings.service.ts rename apps/browser/src/services/{state.service.spec.ts => browser-state.service.spec.ts} (77%) rename apps/browser/src/services/{state.service.ts => browser-state.service.ts} (81%) create mode 100644 libs/angular/test-utils.ts diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 56e2e2dd7e1..1a8c5ae5c50 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -1,7 +1,7 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; const IdleInterval = 60 * 5; // 5 minutes @@ -12,7 +12,7 @@ export default class IdleBackground { constructor( private vaultTimeoutService: VaultTimeoutService, - private stateService: StateService, + private stateService: BrowserStateService, private notificationsService: NotificationsService ) { this.idle = chrome.idle || (browser != null ? browser.idle : null); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 581da587e45..2d370c91bb7 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -61,14 +61,11 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service"; import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; import { ProviderService } from "@bitwarden/common/services/provider.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { SendService } from "@bitwarden/common/services/send.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service"; import { SyncService } from "@bitwarden/common/services/sync/sync.service"; import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service"; @@ -89,19 +86,22 @@ import { UpdateBadge } from "../listeners/update-badge"; import { Account } from "../models/account"; import { PopupUtilsService } from "../popup/services/popup-utils.service"; import { AutofillService as AutofillServiceAbstraction } from "../services/abstractions/autofill.service"; -import { StateService as StateServiceAbstraction } from "../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../services/abstractions/browser-state.service"; import AutofillService from "../services/autofill.service"; import { BrowserEnvironmentService } from "../services/browser-environment.service"; +import { BrowserFolderService } from "../services/browser-folder.service"; +import { BrowserOrganizationService } from "../services/browser-organization.service"; +import { BrowserPolicyService } from "../services/browser-policy.service"; +import { BrowserSettingsService } from "../services/browser-settings.service"; +import { BrowserStateService } from "../services/browser-state.service"; import { BrowserCryptoService } from "../services/browserCrypto.service"; import BrowserLocalStorageService from "../services/browserLocalStorage.service"; import BrowserMessagingService from "../services/browserMessaging.service"; import BrowserMessagingPrivateModeBackgroundService from "../services/browserMessagingPrivateModeBackground.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; -import { FolderService } from "../services/folders/folder.service"; import I18nService from "../services/i18n.service"; import { KeyGenerationService } from "../services/keyGeneration.service"; import { LocalBackedSessionStorageService } from "../services/localBackedSessionStorage.service"; -import { StateService } from "../services/state.service"; import { VaultFilterService } from "../services/vaultFilter.service"; import VaultTimeoutService from "../services/vaultTimeout/vaultTimeout.service"; @@ -227,7 +227,7 @@ export default class MainBackground { this.secureStorageService, new StateFactory(GlobalState, Account) ); - this.stateService = new StateService( + this.stateService = new BrowserStateService( this.storageService, this.secureStorageService, this.memoryStorageService, @@ -282,7 +282,7 @@ export default class MainBackground { this.appIdService, (expired: boolean) => this.logout(expired) ); - this.settingsService = new SettingsService(this.stateService); + this.settingsService = new BrowserSettingsService(this.stateService); this.fileUploadService = new FileUploadService(this.logService, this.apiService); this.cipherService = new CipherService( this.cryptoService, @@ -295,7 +295,7 @@ export default class MainBackground { this.stateService, this.encryptService ); - this.folderService = new FolderService( + this.folderService = new BrowserFolderService( this.cryptoService, this.i18nService, this.cipherService, @@ -317,8 +317,8 @@ export default class MainBackground { this.stateService ); this.syncNotifierService = new SyncNotifierService(); - this.organizationService = new OrganizationService(this.stateService); - this.policyService = new PolicyService(this.stateService, this.organizationService); + this.organizationService = new BrowserOrganizationService(this.stateService); + this.policyService = new BrowserPolicyService(this.stateService, this.organizationService); this.policyApiService = new PolicyApiService( this.policyService, this.apiService, diff --git a/apps/browser/src/background/notification.background.ts b/apps/browser/src/background/notification.background.ts index 127fc5203d7..2da4791857d 100644 --- a/apps/browser/src/background/notification.background.ts +++ b/apps/browser/src/background/notification.background.ts @@ -15,7 +15,7 @@ import { LoginView } from "@bitwarden/common/models/view/login.view"; import { BrowserApi } from "../browser/browserApi"; import { AutofillService } from "../services/abstractions/autofill.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import AddChangePasswordQueueMessage from "./models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "./models/addLoginQueueMessage"; @@ -33,7 +33,7 @@ export default class NotificationBackground { private authService: AuthService, private policyService: PolicyService, private folderService: FolderService, - private stateService: StateService + private stateService: BrowserStateService ) {} async init() { diff --git a/apps/browser/src/background/service_factories/folder-service.factory.ts b/apps/browser/src/background/service_factories/folder-service.factory.ts index a7c90d234b1..bb35970325f 100644 --- a/apps/browser/src/background/service_factories/folder-service.factory.ts +++ b/apps/browser/src/background/service_factories/folder-service.factory.ts @@ -1,6 +1,6 @@ import { FolderService as AbstractFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; -import { FolderService } from "../../services/folders/folder.service"; +import { BrowserFolderService } from "../../services/browser-folder.service"; import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; import { cryptoServiceFactory, CryptoServiceInitOptions } from "./crypto-service.factory"; @@ -28,7 +28,7 @@ export function folderServiceFactory( "folderService", opts, async () => - new FolderService( + new BrowserFolderService( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cipherServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service_factories/organization-service.factory.ts b/apps/browser/src/background/service_factories/organization-service.factory.ts index ea11d32e26a..4f2eaee8058 100644 --- a/apps/browser/src/background/service_factories/organization-service.factory.ts +++ b/apps/browser/src/background/service_factories/organization-service.factory.ts @@ -1,5 +1,6 @@ import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; + +import { BrowserOrganizationService } from "../../services/browser-organization.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -17,6 +18,6 @@ export function organizationServiceFactory( cache, "organizationService", opts, - async () => new OrganizationService(await stateServiceFactory(cache, opts)) + async () => new BrowserOrganizationService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/policy-service.factory.ts b/apps/browser/src/background/service_factories/policy-service.factory.ts index d4940bef259..d20bca3c62e 100644 --- a/apps/browser/src/background/service_factories/policy-service.factory.ts +++ b/apps/browser/src/background/service_factories/policy-service.factory.ts @@ -1,5 +1,6 @@ import { PolicyService as AbstractPolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; + +import { BrowserPolicyService } from "../../services/browser-policy.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { @@ -26,7 +27,7 @@ export function policyServiceFactory( "policyService", opts, async () => - new PolicyService( + new BrowserPolicyService( await stateServiceFactory(cache, opts), await organizationServiceFactory(cache, opts) ) diff --git a/apps/browser/src/background/service_factories/settings-service.factory.ts b/apps/browser/src/background/service_factories/settings-service.factory.ts index 745a6d08d60..73e0ae52032 100644 --- a/apps/browser/src/background/service_factories/settings-service.factory.ts +++ b/apps/browser/src/background/service_factories/settings-service.factory.ts @@ -1,5 +1,6 @@ import { SettingsService as AbstractSettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; + +import { BrowserSettingsService } from "../../services/browser-settings.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -16,6 +17,6 @@ export function settingsServiceFactory( cache, "settingsService", opts, - async () => new SettingsService(await stateServiceFactory(cache, opts)) + async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/state-service.factory.ts b/apps/browser/src/background/service_factories/state-service.factory.ts index 1b81567ac52..6d2c5cb4fa7 100644 --- a/apps/browser/src/background/service_factories/state-service.factory.ts +++ b/apps/browser/src/background/service_factories/state-service.factory.ts @@ -2,7 +2,7 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { Account } from "../../models/account"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; @@ -34,15 +34,15 @@ export type StateServiceInitOptions = StateServiceFactoryOptions & StateMigrationServiceInitOptions; export async function stateServiceFactory( - cache: { stateService?: StateService } & CachedServices, + cache: { stateService?: BrowserStateService } & CachedServices, opts: StateServiceInitOptions -): Promise { +): Promise { const service = await factory( cache, "stateService", opts, async () => - await new StateService( + await new BrowserStateService( await diskStorageServiceFactory(cache, opts), await secureStorageServiceFactory(cache, opts), await memoryStorageServiceFactory(cache, opts), diff --git a/apps/browser/src/clipboard/clipboard-state.ts b/apps/browser/src/clipboard/clipboard-state.ts index a1c15addc0a..cfa2f9459f8 100644 --- a/apps/browser/src/clipboard/clipboard-state.ts +++ b/apps/browser/src/clipboard/clipboard-state.ts @@ -1,10 +1,10 @@ -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; const clearClipboardStorageKey = "clearClipboardTime"; -export const getClearClipboardTime = async (stateService: StateService) => { +export const getClearClipboardTime = async (stateService: BrowserStateService) => { return await stateService.getFromSessionMemory(clearClipboardStorageKey); }; -export const setClearClipboardTime = async (stateService: StateService, time: number) => { +export const setClearClipboardTime = async (stateService: BrowserStateService, time: number) => { await stateService.setInSessionMemory(clearClipboardStorageKey, time); }; diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts index e9c2141211f..5ab36b06fef 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts @@ -3,7 +3,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import { setClearClipboardTime } from "./clipboard-state"; import { GeneratePasswordToClipboardCommand } from "./generate-password-to-clipboard-command"; @@ -19,13 +19,13 @@ const setClearClipboardTimeMock = setClearClipboardTime as jest.Mock; describe("GeneratePasswordToClipboardCommand", () => { let passwordGenerationService: MockProxy; - let stateService: MockProxy; + let stateService: MockProxy; let sut: GeneratePasswordToClipboardCommand; beforeEach(() => { passwordGenerationService = mock(); - stateService = mock(); + stateService = mock(); passwordGenerationService.getOptions.mockResolvedValue([{ length: 8 }, {} as any]); diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts index ca92d2c686f..e6d4d6b8b0c 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts @@ -1,6 +1,6 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import { setClearClipboardTime } from "./clipboard-state"; import { copyToClipboard } from "./copy-to-clipboard-command"; @@ -8,7 +8,7 @@ import { copyToClipboard } from "./copy-to-clipboard-command"; export class GeneratePasswordToClipboardCommand { constructor( private passwordGenerationService: PasswordGenerationService, - private stateService: StateService + private stateService: BrowserStateService ) {} async generatePasswordToClipboard(tab: chrome.tabs.Tab) { diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts index cc8a5618760..92c5dfb0170 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from "rxjs"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { browserSession } from "./browser-session.decorator"; import { SessionStorable } from "./session-storable"; @@ -22,25 +22,25 @@ describe("browserSession decorator", () => { }); it("should create if StateService is a constructor argument", () => { - const stateService = Object.create(StateService.prototype, {}); + const stateService = Object.create(BrowserStateService.prototype, {}); @browserSession class TestClass { - constructor(private stateService: StateService) {} + constructor(private stateService: BrowserStateService) {} } expect(new TestClass(stateService)).toBeDefined(); }); describe("interaction with @sessionSync decorator", () => { - let stateService: StateService; + let stateService: BrowserStateService; @browserSession class TestClass { @sessionSync({ initializer: (s: string) => s }) private behaviorSubject = new BehaviorSubject(""); - constructor(private stateService: StateService) {} + constructor(private stateService: BrowserStateService) {} fromJSON(json: any) { this.behaviorSubject.next(json); @@ -48,7 +48,7 @@ describe("browserSession decorator", () => { } beforeEach(() => { - stateService = Object.create(StateService.prototype, {}) as StateService; + stateService = Object.create(BrowserStateService.prototype, {}) as BrowserStateService; }); it("should create a session syncer", () => { diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts index 73cdf767357..5d9d56c1d71 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts @@ -1,6 +1,6 @@ import { Constructor } from "type-fest"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { SessionStorable } from "./session-storable"; import { SessionSyncer } from "./session-syncer"; @@ -22,7 +22,13 @@ export function browserSession>(constructor: TCto super(...args); // Require state service to be injected - const stateService = args.find((arg) => arg instanceof StateService); + const stateService: BrowserStateService = [this as any] + .concat(args) + .find( + (arg) => + typeof arg.setInSessionMemory === "function" && + typeof arg.getFromSessionMemory === "function" + ); if (!stateService) { throw new Error( `Cannot decorate ${constructor.name} with browserSession, Browser's StateService must be injected` @@ -38,7 +44,7 @@ export function browserSession>(constructor: TCto ); } - buildSyncer(metadata: SyncedItemMetadata, stateService: StateService) { + buildSyncer(metadata: SyncedItemMetadata, stateService: BrowserStateService) { const syncer = new SessionSyncer((this as any)[metadata.propertyKey], stateService, metadata); syncer.init(); return syncer; diff --git a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts index df0764528f7..071322900e1 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts @@ -1,11 +1,12 @@ import { Jsonify } from "type-fest"; import { SessionStorable } from "./session-storable"; +import { InitializeOptions } from "./sync-item-metadata"; -class BuildOptions { +class BuildOptions> { ctor?: new () => T; - initializer?: (keyValuePair: Jsonify) => T; - initializeAsArray? = false; + initializer?: (keyValuePair: TJson) => T; + initializeAs?: InitializeOptions; } /** @@ -20,10 +21,10 @@ class BuildOptions { * @param buildOptions * Builders for the value, requires either a constructor (ctor) for your BehaviorSubject type or an * initializer function that takes a key value pair representation of the BehaviorSubject data - * and returns your instantiated BehaviorSubject value. `initializeAsArray can optionally be used to indicate + * and returns your instantiated BehaviorSubject value. `initializeAs can optionally be used to indicate * the provided initializer function should be used to build an array of values. For example, * ```ts - * \@sessionSync({ initializer: Foo.fromJSON, initializeAsArray: true }) + * \@sessionSync({ initializer: Foo.fromJSON, initializeAs: 'array' }) * ``` * is equivalent to * ``` @@ -46,7 +47,7 @@ export function sessionSync(buildOptions: BuildOptions) { sessionKey: `${prototype.constructor.name}_${propertyKey}`, ctor: buildOptions.ctor, initializer: buildOptions.initializer, - initializeAsArray: buildOptions.initializeAsArray, + initializeAs: buildOptions.initializeAs ?? "object", }); }; } diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts index 5286cece1bb..00a0da433a5 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts @@ -1,8 +1,9 @@ +import { awaitAsync as flushAsyncObservables } from "@bitwarden/angular/../test-utils"; import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, ReplaySubject } from "rxjs"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SessionSyncer } from "./session-syncer"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -10,8 +11,13 @@ import { SyncedItemMetadata } from "./sync-item-metadata"; describe("session syncer", () => { const propertyKey = "behaviorSubject"; const sessionKey = "Test__" + propertyKey; - const metaData = { propertyKey, sessionKey, initializer: (s: string) => s }; - let stateService: MockProxy; + const metaData: SyncedItemMetadata = { + propertyKey, + sessionKey, + initializer: (s: string) => s, + initializeAs: "object", + }; + let stateService: MockProxy; let sut: SessionSyncer; let behaviorSubject: BehaviorSubject; @@ -23,7 +29,7 @@ describe("session syncer", () => { manifest_version: 3, }); - stateService = mock(); + stateService = mock(); sut = new SessionSyncer(behaviorSubject, stateService, metaData); }); @@ -34,53 +40,85 @@ describe("session syncer", () => { }); describe("constructor", () => { - it("should throw if behaviorSubject is not an instance of BehaviorSubject", () => { + it("should throw if subject is not an instance of Subject", () => { expect(() => { new SessionSyncer({} as any, stateService, null); - }).toThrowError("behaviorSubject must be an instance of BehaviorSubject"); + }).toThrowError("subject must inherit from Subject"); }); it("should create if either ctor or initializer is provided", () => { expect( - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, ctor: String }) + new SessionSyncer(behaviorSubject, stateService, { + propertyKey, + sessionKey, + ctor: String, + initializeAs: "object", + }) ).toBeDefined(); expect( new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, initializer: (s: any) => s, + initializeAs: "object", }) ).toBeDefined(); }); it("should throw if neither ctor or initializer is provided", () => { expect(() => { - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey }); + new SessionSyncer(behaviorSubject, stateService, { + propertyKey, + sessionKey, + initializeAs: "object", + }); }).toThrowError("ctor or initializer must be provided"); }); }); - describe("manifest v2 init", () => { - let observeSpy: jest.SpyInstance; - let listenForUpdatesSpy: jest.SpyInstance; - - beforeEach(() => { - observeSpy = jest.spyOn(behaviorSubject, "subscribe").mockReturnThis(); - listenForUpdatesSpy = jest.spyOn(BrowserApi, "messageListener").mockReturnValue(); - jest.spyOn(chrome.runtime, "getManifest").mockReturnValue({ - name: "bitwarden-test", - version: "0.0.0", - manifest_version: 2, - }); + describe("init", () => { + it("should ignore all updates currently in a ReplaySubject's buffer", () => { + const replaySubject = new ReplaySubject(Infinity); + replaySubject.next("1"); + replaySubject.next("2"); + replaySubject.next("3"); + sut = new SessionSyncer(replaySubject, stateService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(3); }); - it("should not start observing", () => { - expect(observeSpy).not.toHaveBeenCalled(); + it("should ignore BehaviorSubject's initial value", () => { + const behaviorSubject = new BehaviorSubject("initial"); + sut = new SessionSyncer(behaviorSubject, stateService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); + + sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(1); }); - it("should not start listening", () => { - expect(listenForUpdatesSpy).not.toHaveBeenCalled(); + it("should grab an initial value from storage if it exists", () => { + stateService.hasInSessionMemory.mockResolvedValue(true); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + + expect(updateSpy).toHaveBeenCalledWith(); + }); + + it("should not grab an initial value from storage if it does not exist", () => { + stateService.hasInSessionMemory.mockResolvedValue(false); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + + expect(updateSpy).toHaveBeenCalledWith(); }); }); @@ -146,6 +184,7 @@ describe("session syncer", () => { stateService.getFromSessionMemory.mockResolvedValue("test"); await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" }); + await flushAsyncObservables(); expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1); expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey, builder); diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts index 2acfed2954f..68294b68c3d 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts @@ -1,9 +1,9 @@ -import { BehaviorSubject, concatMap, Subscription } from "rxjs"; +import { BehaviorSubject, concatMap, ReplaySubject, Subject, Subscription } from "rxjs"; import { Utils } from "@bitwarden/common/misc/utils"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -11,16 +11,16 @@ export class SessionSyncer { subscription: Subscription; id = Utils.newGuid(); - // everyone gets the same initial values - private ignoreNextUpdate = true; + // ignore initial values + private ignoreNUpdates = 0; constructor( - private behaviorSubject: BehaviorSubject, - private stateService: StateService, + private subject: Subject, + private stateService: BrowserStateService, private metaData: SyncedItemMetadata ) { - if (!(behaviorSubject instanceof BehaviorSubject)) { - throw new Error("behaviorSubject must be an instance of BehaviorSubject"); + if (!(subject instanceof Subject)) { + throw new Error("subject must inherit from Subject"); } if (metaData.ctor == null && metaData.initializer == null) { @@ -29,11 +29,23 @@ export class SessionSyncer { } init() { - if (BrowserApi.manifestVersion !== 3) { - return; + switch (this.subject.constructor) { + case ReplaySubject: + // ignore all updates currently in the buffer + this.ignoreNUpdates = (this.subject as any)._buffer.length; + break; + case BehaviorSubject: + this.ignoreNUpdates = 1; + break; + default: + break; } this.observe(); + if (this.stateService.hasInSessionMemory(this.metaData.sessionKey)) { + this.update(); + } + this.listenForUpdates(); } @@ -41,11 +53,11 @@ export class SessionSyncer { // This may be a memory leak. // There is no good time to unsubscribe from this observable. Hopefully Manifest V3 clears memory from temporary // contexts. If so, this is handled by destruction of the context. - this.subscription = this.behaviorSubject + this.subscription = this.subject .pipe( concatMap(async (next) => { - if (this.ignoreNextUpdate) { - this.ignoreNextUpdate = false; + if (this.ignoreNUpdates > 0) { + this.ignoreNUpdates -= 1; return; } await this.updateSession(next); @@ -66,10 +78,14 @@ export class SessionSyncer { if (message.command != this.updateMessageCommand || message.id === this.id) { return; } + this.update(); + } + + async update() { const builder = SyncedItemMetadata.builder(this.metaData); const value = await this.stateService.getFromSessionMemory(this.metaData.sessionKey, builder); - this.ignoreNextUpdate = true; - this.behaviorSubject.next(value); + this.ignoreNUpdates = 1; + this.subject.next(value); } private async updateSession(value: any) { diff --git a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts index 2b3f4715d46..facfda32fd8 100644 --- a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts +++ b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts @@ -1,17 +1,27 @@ +export type InitializeOptions = "array" | "record" | "object"; + export class SyncedItemMetadata { propertyKey: string; sessionKey: string; ctor?: new () => any; initializer?: (keyValuePair: any) => any; - initializeAsArray?: boolean; + initializeAs: InitializeOptions; static builder(metadata: SyncedItemMetadata): (o: any) => any { const itemBuilder = metadata.initializer != null ? metadata.initializer : (o: any) => Object.assign(new metadata.ctor(), o); - if (metadata.initializeAsArray) { + if (metadata.initializeAs === "array") { return (keyValuePair: any) => keyValuePair.map((o: any) => itemBuilder(o)); + } else if (metadata.initializeAs === "record") { + return (keyValuePair: any) => { + const record: Record = {}; + for (const key in keyValuePair) { + record[key] = itemBuilder(keyValuePair[key]); + } + return record; + }; } else { return (keyValuePair: any) => itemBuilder(keyValuePair); } diff --git a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts index 5cd869a5b67..12b0b57d523 100644 --- a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts @@ -8,32 +8,60 @@ describe("builder", () => { const ctor = TestClass; it("should use initializer if provided", () => { - const metadata = { propertyKey, sessionKey: key, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should use ctor if initializer is not provided", () => { - const metadata = { propertyKey, sessionKey: key, ctor }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBeInstanceOf(TestClass); }); it("should prefer initializer over ctor", () => { - const metadata = { propertyKey, sessionKey: key, ctor, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should honor initialize as array", () => { - const metadata = { + const metadata: SyncedItemMetadata = { propertyKey, sessionKey: key, initializer: initializer, - initializeAsArray: true, + initializeAs: "array", }; const builder = SyncedItemMetadata.builder(metadata); expect(builder([{}])).toBeInstanceOf(Array); expect(builder([{}])[0]).toBe("used initializer"); }); + + it("should honor initialize as record", () => { + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer: initializer, + initializeAs: "record", + }; + const builder = SyncedItemMetadata.builder(metadata); + expect(builder({ key: "" })).toBeInstanceOf(Object); + expect(builder({ key: "" })).toStrictEqual({ key: "used initializer" }); + }); }); diff --git a/apps/browser/src/listeners/update-badge.ts b/apps/browser/src/listeners/update-badge.ts index 9c7c122a45a..8762a15ab2a 100644 --- a/apps/browser/src/listeners/update-badge.ts +++ b/apps/browser/src/listeners/update-badge.ts @@ -15,7 +15,7 @@ import { searchServiceFactory } from "../background/service_factories/search-ser import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; import { Account } from "../models/account"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; export type BadgeOptions = { @@ -25,7 +25,7 @@ export type BadgeOptions = { export class UpdateBadge { private authService: AuthService; - private stateService: StateService; + private stateService: BrowserStateService; private cipherService: CipherService; private badgeAction: typeof chrome.action; private sidebarAction: OperaSidebarAction | FirefoxSidebarAction; diff --git a/apps/browser/src/models/account.ts b/apps/browser/src/models/account.ts index f49c55d2909..cfbcbecf979 100644 --- a/apps/browser/src/models/account.ts +++ b/apps/browser/src/models/account.ts @@ -1,3 +1,5 @@ +import { Jsonify } from "type-fest"; + import { Account as BaseAccount, AccountSettings as BaseAccountSettings, @@ -9,6 +11,14 @@ import { BrowserSendComponentState } from "./browserSendComponentState"; export class AccountSettings extends BaseAccountSettings { vaultTimeout = -1; // On Restart + + static fromJSON(json: Jsonify): AccountSettings { + if (json == null) { + return null; + } + + return Object.assign(new AccountSettings(), json, super.fromJSON(json)); + } } export class Account extends BaseAccount { @@ -29,4 +39,18 @@ export class Account extends BaseAccount { this.ciphers = init?.ciphers ?? new BrowserComponentState(); this.sendType = init?.sendType ?? new BrowserComponentState(); } + + static fromJSON(json: Jsonify): Account { + if (json == null) { + return null; + } + + return Object.assign(new Account({}), json, super.fromJSON(json), { + settings: AccountSettings.fromJSON(json.settings), + groupings: BrowserGroupingsComponentState.fromJSON(json.groupings), + send: BrowserSendComponentState.fromJSON(json.send), + ciphers: BrowserComponentState.fromJSON(json.ciphers), + sendType: BrowserComponentState.fromJSON(json.sendType), + }); + } } diff --git a/apps/browser/src/models/browserComponentState.ts b/apps/browser/src/models/browserComponentState.ts index d968726c413..c5540d088ff 100644 --- a/apps/browser/src/models/browserComponentState.ts +++ b/apps/browser/src/models/browserComponentState.ts @@ -1,4 +1,14 @@ +import { Jsonify } from "type-fest"; + export class BrowserComponentState { scrollY: number; searchText: string; + + static fromJSON(json: Jsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserComponentState(), json); + } } diff --git a/apps/browser/src/models/browserGroupingsComponentState.ts b/apps/browser/src/models/browserGroupingsComponentState.ts index 63eb4aaa88f..f406e3d8271 100644 --- a/apps/browser/src/models/browserGroupingsComponentState.ts +++ b/apps/browser/src/models/browserGroupingsComponentState.ts @@ -1,7 +1,9 @@ import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { FolderView } from "@bitwarden/common/models/view/folder.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; @@ -15,4 +17,28 @@ export class BrowserGroupingsComponentState extends BrowserComponentState { folders: FolderView[]; collections: CollectionView[]; deletedCount: number; + + toJSON() { + return Utils.merge(this, { + collectionCounts: Utils.mapToRecord(this.collectionCounts), + folderCounts: Utils.mapToRecord(this.folderCounts), + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserGroupingsComponentState(), json, { + favoriteCiphers: json.favoriteCiphers?.map((c) => CipherView.fromJSON(c)), + noFolderCiphers: json.noFolderCiphers?.map((c) => CipherView.fromJSON(c)), + ciphers: json.ciphers?.map((c) => CipherView.fromJSON(c)), + collectionCounts: Utils.recordToMap(json.collectionCounts), + folderCounts: Utils.recordToMap(json.folderCounts), + typeCounts: Utils.recordToMap(json.typeCounts), + folders: json.folders?.map((f) => FolderView.fromJSON(f)), + }); + } } diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts index e2bf4eaa5d6..99508737ab1 100644 --- a/apps/browser/src/models/browserSendComponentState.ts +++ b/apps/browser/src/models/browserSendComponentState.ts @@ -1,9 +1,28 @@ import { SendType } from "@bitwarden/common/enums/sendType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { SendView } from "@bitwarden/common/models/view/send.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; export class BrowserSendComponentState extends BrowserComponentState { sends: SendView[]; typeCounts: Map; + + toJSON() { + return Utils.merge(this, { + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserSendComponentState(), json, { + sends: json.sends?.map((s) => SendView.fromJSON(s)), + typeCounts: Utils.recordToMap(json.typeCounts), + }); + } } diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index ec094cbe945..da7b0062b8b 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -19,7 +19,7 @@ import { MessagingService } from "@bitwarden/common/abstractions/messaging.servi import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import { routerTransition } from "./app-routing.animations"; @@ -43,7 +43,7 @@ export class AppComponent implements OnInit, OnDestroy { private authService: AuthService, private i18nService: I18nService, private router: Router, - private stateService: StateService, + private stateService: BrowserStateService, private messagingService: MessagingService, private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, diff --git a/apps/browser/src/popup/send/send-add-edit.component.ts b/apps/browser/src/popup/send/send-add-edit.component.ts index 2fb45996ad1..0355b764c30 100644 --- a/apps/browser/src/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/popup/send/send-add-edit.component.ts @@ -12,7 +12,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { SendService } from "@bitwarden/common/abstractions/send.service"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @Component({ @@ -33,7 +33,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { constructor( i18nService: I18nService, platformUtilsService: PlatformUtilsService, - stateService: StateService, + stateService: BrowserStateService, messagingService: MessagingService, policyService: PolicyService, environmentService: EnvironmentService, diff --git a/apps/browser/src/popup/send/send-groupings.component.ts b/apps/browser/src/popup/send/send-groupings.component.ts index 1af715ada0f..a5d63eb9d58 100644 --- a/apps/browser/src/popup/send/send-groupings.component.ts +++ b/apps/browser/src/popup/send/send-groupings.component.ts @@ -15,7 +15,7 @@ import { SendType } from "@bitwarden/common/enums/sendType"; import { SendView } from "@bitwarden/common/models/view/send.view"; import { BrowserSendComponentState } from "../../models/browserSendComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; const ComponentId = "SendComponent"; @@ -42,7 +42,7 @@ export class SendGroupingsComponent extends BaseSendComponent { policyService: PolicyService, searchService: SearchService, private popupUtils: PopupUtilsService, - private stateService: StateService, + private stateService: BrowserStateService, private router: Router, private syncService: SyncService, private changeDetectorRef: ChangeDetectorRef, @@ -165,12 +165,12 @@ export class SendGroupingsComponent extends BaseSendComponent { } private async saveState() { - this.state = { + this.state = Object.assign(new BrowserSendComponentState(), { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, sends: this.sends, typeCounts: this.typeCounts, - }; + }); await this.stateService.setBrowserSendComponentState(this.state); } diff --git a/apps/browser/src/popup/send/send-type.component.ts b/apps/browser/src/popup/send/send-type.component.ts index afd3daeeda5..e899ab9f00f 100644 --- a/apps/browser/src/popup/send/send-type.component.ts +++ b/apps/browser/src/popup/send/send-type.component.ts @@ -16,7 +16,7 @@ import { SendType } from "@bitwarden/common/enums/sendType"; import { SendView } from "@bitwarden/common/models/view/send.view"; import { BrowserComponentState } from "../../models/browserComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; const ComponentId = "SendTypeComponent"; @@ -41,7 +41,7 @@ export class SendTypeComponent extends BaseSendComponent { policyService: PolicyService, searchService: SearchService, private popupUtils: PopupUtilsService, - private stateService: StateService, + private stateService: BrowserStateService, private route: ActivatedRoute, private location: Location, private changeDetectorRef: ChangeDetectorRef, diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index a73792cc10f..8008f6c88ce 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -5,7 +5,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "./popup-utils.service"; diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 236ff4e5b18..0acd8c785d1 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -38,6 +38,7 @@ import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abs import { SendService } from "@bitwarden/common/abstractions/send.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; +import { StateMigrationService } from "@bitwarden/common/abstractions/stateMigration.service"; import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; @@ -47,6 +48,8 @@ import { UserVerificationService } from "@bitwarden/common/abstractions/userVeri import { UsernameGenerationService } from "@bitwarden/common/abstractions/usernameGeneration.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { AuthService } from "@bitwarden/common/services/auth.service"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { LoginService } from "@bitwarden/common/services/login.service"; @@ -54,9 +57,14 @@ import { SearchService } from "@bitwarden/common/services/search.service"; import MainBackground from "../../background/main.background"; import { BrowserApi } from "../../browser/browserApi"; +import { Account } from "../../models/account"; import { AutofillService } from "../../services/abstractions/autofill.service"; -import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../../services/abstractions/browser-state.service"; import { BrowserEnvironmentService } from "../../services/browser-environment.service"; +import { BrowserOrganizationService } from "../../services/browser-organization.service"; +import { BrowserPolicyService } from "../../services/browser-policy.service"; +import { BrowserSettingsService } from "../../services/browser-settings.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { BrowserFileDownloadService } from "../../services/browserFileDownloadService"; import BrowserMessagingService from "../../services/browserMessaging.service"; import BrowserMessagingPrivateModePopupService from "../../services/browserMessagingPrivateModePopup.service"; @@ -190,8 +198,13 @@ function getBgService(service: keyof MainBackground) { { provide: EventService, useFactory: getBgService("eventService"), deps: [] }, { provide: PolicyService, - useFactory: getBgService("policyService"), - deps: [], + useFactory: ( + stateService: StateServiceAbstraction, + organizationService: OrganizationService + ) => { + return new BrowserPolicyService(stateService, organizationService); + }, + deps: [StateServiceAbstraction, OrganizationService], }, { provide: PolicyApiServiceAbstraction, @@ -212,8 +225,10 @@ function getBgService(service: keyof MainBackground) { { provide: SyncService, useFactory: getBgService("syncService"), deps: [] }, { provide: SettingsService, - useFactory: getBgService("settingsService"), - deps: [], + useFactory: (stateService: StateServiceAbstraction) => { + return new BrowserSettingsService(stateService); + }, + deps: [StateServiceAbstraction], }, { provide: AbstractStorageService, @@ -261,8 +276,10 @@ function getBgService(service: keyof MainBackground) { { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, { provide: OrganizationService, - useFactory: getBgService("organizationService"), - deps: [], + useFactory: (stateService: StateServiceAbstraction) => { + return new BrowserOrganizationService(stateService); + }, + deps: [StateServiceAbstraction], }, { provide: VaultFilterService, @@ -293,10 +310,36 @@ function getBgService(service: keyof MainBackground) { useFactory: getBgService("memoryStorageService"), }, { - provide: StateServiceAbstraction, - useFactory: getBgService("stateService"), + provide: StateMigrationService, + useFactory: getBgService("stateMigrationService"), deps: [], }, + { + provide: StateServiceAbstraction, + useFactory: ( + storageService: AbstractStorageService, + secureStorageService: AbstractStorageService, + memoryStorageService: AbstractStorageService, + logService: LogServiceAbstraction, + stateMigrationService: StateMigrationService + ) => { + return new BrowserStateService( + storageService, + secureStorageService, + memoryStorageService, + logService, + stateMigrationService, + new StateFactory(GlobalState, Account) + ); + }, + deps: [ + AbstractStorageService, + SECURE_STORAGE, + MEMORY_STORAGE, + LogServiceAbstraction, + StateMigrationService, + ], + }, { provide: UsernameGenerationService, useFactory: getBgService("usernameGenerationService"), @@ -317,17 +360,19 @@ function getBgService(service: keyof MainBackground) { }, { provide: AbstractThemingService, - useFactory: () => { + useFactory: ( + stateService: StateServiceAbstraction, + platformUtilsService: PlatformUtilsService + ) => { return new ThemingService( - getBgService("stateService")(), + stateService, // Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light. // In Safari we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed. - getBgService("platformUtilsService")().isSafari() - ? getBgService("backgroundWindow")() - : window, + platformUtilsService.isSafari() ? getBgService("backgroundWindow")() : window, document ); }, + deps: [StateServiceAbstraction, PlatformUtilsService], }, ], }) diff --git a/apps/browser/src/popup/vault/vault-filter.component.ts b/apps/browser/src/popup/vault/vault-filter.component.ts index f8a6f14081d..e67fe69f601 100644 --- a/apps/browser/src/popup/vault/vault-filter.component.ts +++ b/apps/browser/src/popup/vault/vault-filter.component.ts @@ -18,7 +18,7 @@ import { FolderView } from "@bitwarden/common/models/view/folder.view"; import { BrowserApi } from "../../browser/browserApi"; import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { VaultFilterService } from "../../services/vaultFilter.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -83,7 +83,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private searchService: SearchService, private location: Location, - private browserStateService: StateService, + private browserStateService: BrowserStateService, private vaultFilterService: VaultFilterService ) { this.noFolderListSize = 100; @@ -373,7 +373,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { } private async saveState() { - this.state = { + this.state = Object.assign(new BrowserGroupingsComponentState(), { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, favoriteCiphers: this.favoriteCiphers, @@ -385,7 +385,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { folders: this.folders, collections: this.collections, deletedCount: this.deletedCount, - }; + }); await this.browserStateService.setBrowserGroupingComponentState(this.state); } diff --git a/apps/browser/src/popup/vault/vault-items.component.ts b/apps/browser/src/popup/vault/vault-items.component.ts index 232a29ea415..b7f63d6d71d 100644 --- a/apps/browser/src/popup/vault/vault-items.component.ts +++ b/apps/browser/src/popup/vault/vault-items.component.ts @@ -21,7 +21,7 @@ import { FolderView } from "@bitwarden/common/models/view/folder.view"; import { BrowserApi } from "../../browser/browserApi"; import { BrowserComponentState } from "../../models/browserComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { VaultFilterService } from "../../services/vaultFilter.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -60,7 +60,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn private ngZone: NgZone, private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef, - private stateService: StateService, + private stateService: BrowserStateService, private popupUtils: PopupUtilsService, private i18nService: I18nService, private folderService: FolderService, diff --git a/apps/browser/src/services/abstractions/state.service.ts b/apps/browser/src/services/abstractions/browser-state.service.ts similarity index 91% rename from apps/browser/src/services/abstractions/state.service.ts rename to apps/browser/src/services/abstractions/browser-state.service.ts index 53a1b883646..afe5e2ce694 100644 --- a/apps/browser/src/services/abstractions/state.service.ts +++ b/apps/browser/src/services/abstractions/browser-state.service.ts @@ -8,7 +8,8 @@ import { BrowserComponentState } from "../../models/browserComponentState"; import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState"; import { BrowserSendComponentState } from "../../models/browserSendComponentState"; -export abstract class StateService extends BaseStateServiceAbstraction { +export abstract class BrowserStateService extends BaseStateServiceAbstraction { + abstract hasInSessionMemory(key: string): Promise; abstract getFromSessionMemory(key: string, deserializer?: (obj: Jsonify) => T): Promise; abstract setInSessionMemory(key: string, value: any): Promise; getBrowserGroupingComponentState: ( diff --git a/apps/browser/src/services/autofill.service.ts b/apps/browser/src/services/autofill.service.ts index 470bd584cf1..f458279de4a 100644 --- a/apps/browser/src/services/autofill.service.ts +++ b/apps/browser/src/services/autofill.service.ts @@ -14,7 +14,6 @@ import { BrowserApi } from "../browser/browserApi"; import AutofillField from "../models/autofillField"; import AutofillPageDetails from "../models/autofillPageDetails"; import AutofillScript from "../models/autofillScript"; -import { StateService } from "../services/abstractions/state.service"; import { AutoFillOptions, @@ -22,6 +21,7 @@ import { PageDetail, FormData, } from "./abstractions/autofill.service"; +import { BrowserStateService } from "./abstractions/browser-state.service"; import { AutoFillConstants, CreditCardAutoFillConstants, @@ -39,7 +39,7 @@ export interface GenerateFillScriptOptions { export default class AutofillService implements AutofillServiceInterface { constructor( private cipherService: CipherService, - private stateService: StateService, + private stateService: BrowserStateService, private totpService: TotpService, private eventService: EventService, private logService: LogService diff --git a/apps/browser/src/services/folders/folder.service.ts b/apps/browser/src/services/browser-folder.service.ts similarity index 57% rename from apps/browser/src/services/folders/folder.service.ts rename to apps/browser/src/services/browser-folder.service.ts index e4fc19644da..a9573ab0f78 100644 --- a/apps/browser/src/services/folders/folder.service.ts +++ b/apps/browser/src/services/browser-folder.service.ts @@ -4,12 +4,12 @@ import { Folder } from "@bitwarden/common/models/domain/folder"; import { FolderView } from "@bitwarden/common/models/view/folder.view"; import { FolderService as BaseFolderService } from "@bitwarden/common/services/folder/folder.service"; -import { browserSession, sessionSync } from "../../decorators/session-sync-observable"; +import { browserSession, sessionSync } from "../decorators/session-sync-observable"; @browserSession -export class FolderService extends BaseFolderService { - @sessionSync({ initializer: Folder.fromJSON, initializeAsArray: true }) +export class BrowserFolderService extends BaseFolderService { + @sessionSync({ initializer: Folder.fromJSON, initializeAs: "array" }) protected _folders: BehaviorSubject; - @sessionSync({ initializer: FolderView.fromJSON, initializeAsArray: true }) + @sessionSync({ initializer: FolderView.fromJSON, initializeAs: "array" }) protected _folderViews: BehaviorSubject; } diff --git a/apps/browser/src/services/browser-organization.service.ts b/apps/browser/src/services/browser-organization.service.ts new file mode 100644 index 00000000000..63f2848e2e8 --- /dev/null +++ b/apps/browser/src/services/browser-organization.service.ts @@ -0,0 +1,12 @@ +import { BehaviorSubject } from "rxjs"; + +import { Organization } from "@bitwarden/common/models/domain/organization"; +import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; + +import { browserSession, sessionSync } from "../decorators/session-sync-observable"; + +@browserSession +export class BrowserOrganizationService extends OrganizationService { + @sessionSync({ initializer: Organization.fromJSON, initializeAs: "array" }) + protected _organizations: BehaviorSubject; +} diff --git a/apps/browser/src/services/browser-policy.service.ts b/apps/browser/src/services/browser-policy.service.ts new file mode 100644 index 00000000000..613e5c39cf4 --- /dev/null +++ b/apps/browser/src/services/browser-policy.service.ts @@ -0,0 +1,12 @@ +import { BehaviorSubject } from "rxjs"; + +import { Policy } from "@bitwarden/common/models/domain/policy"; +import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; + +import { browserSession, sessionSync } from "../decorators/session-sync-observable"; + +@browserSession +export class BrowserPolicyService extends PolicyService { + @sessionSync({ ctor: Policy, initializeAs: "array" }) + protected _policies: BehaviorSubject; +} diff --git a/apps/browser/src/services/browser-settings.service.ts b/apps/browser/src/services/browser-settings.service.ts new file mode 100644 index 00000000000..78b4f76ea6a --- /dev/null +++ b/apps/browser/src/services/browser-settings.service.ts @@ -0,0 +1,11 @@ +import { BehaviorSubject } from "rxjs"; + +import { AccountSettingsSettings } from "@bitwarden/common/models/domain/account"; +import { SettingsService } from "@bitwarden/common/services/settings.service"; + +import { sessionSync } from "../decorators/session-sync-observable"; + +export class BrowserSettingsService extends SettingsService { + @sessionSync({ initializer: (obj: string[][]) => obj }) + protected _settings: BehaviorSubject; +} diff --git a/apps/browser/src/services/state.service.spec.ts b/apps/browser/src/services/browser-state.service.spec.ts similarity index 77% rename from apps/browser/src/services/state.service.spec.ts rename to apps/browser/src/services/browser-state.service.spec.ts index 5bc4e6ad99b..7d6f8456314 100644 --- a/apps/browser/src/services/state.service.spec.ts +++ b/apps/browser/src/services/browser-state.service.spec.ts @@ -1,6 +1,6 @@ -// eslint-disable-next-line no-restricted-imports -import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; +import { mock, MockProxy } from "jest-mock-extended"; +import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MemoryStorageServiceInterface, @@ -18,28 +18,29 @@ import { BrowserComponentState } from "../models/browserComponentState"; import { BrowserGroupingsComponentState } from "../models/browserGroupingsComponentState"; import { BrowserSendComponentState } from "../models/browserSendComponentState"; +import { AbstractKeyGenerationService } from "./abstractions/abstractKeyGeneration.service"; +import { BrowserStateService } from "./browser-state.service"; import { LocalBackedSessionStorageService } from "./localBackedSessionStorage.service"; -import { StateService } from "./state.service"; describe("Browser State Service", () => { - let secureStorageService: SubstituteOf; - let diskStorageService: SubstituteOf; - let logService: SubstituteOf; - let stateMigrationService: SubstituteOf; - let stateFactory: SubstituteOf>; + let secureStorageService: MockProxy; + let diskStorageService: MockProxy; + let logService: MockProxy; + let stateMigrationService: MockProxy; + let stateFactory: MockProxy>; let useAccountCache: boolean; let state: State; const userId = "userId"; - let sut: StateService; + let sut: BrowserStateService; beforeEach(() => { - secureStorageService = Substitute.for(); - diskStorageService = Substitute.for(); - logService = Substitute.for(); - stateMigrationService = Substitute.for(); - stateFactory = Substitute.for(); + secureStorageService = mock(); + diskStorageService = mock(); + logService = mock(); + stateMigrationService = mock(); + stateFactory = mock(); useAccountCache = true; state = new State(new GlobalState()); @@ -54,9 +55,12 @@ describe("Browser State Service", () => { beforeEach(() => { // We need `AbstractCachedStorageService` in the prototype chain to correctly test cache bypass. - memoryStorageService = Object.create(LocalBackedSessionStorageService.prototype); + memoryStorageService = new LocalBackedSessionStorageService( + mock(), + mock() + ); - sut = new StateService( + sut = new BrowserStateService( diskStorageService, secureStorageService, memoryStorageService, @@ -80,14 +84,14 @@ describe("Browser State Service", () => { }); describe("state methods", () => { - let memoryStorageService: SubstituteOf; + let memoryStorageService: MockProxy; beforeEach(() => { - memoryStorageService = Substitute.for(); - const stateGetter = (key: string) => Promise.resolve(JSON.parse(JSON.stringify(state))); - memoryStorageService.get("state", Arg.any()).mimicks(stateGetter); + memoryStorageService = mock(); + const stateGetter = (key: string) => Promise.resolve(state); + memoryStorageService.get.mockImplementation(stateGetter); - sut = new StateService( + sut = new BrowserStateService( diskStorageService, secureStorageService, memoryStorageService, @@ -128,6 +132,7 @@ describe("Browser State Service", () => { [SendType.Text, 5], ]); state.accounts[userId].send = sendState; + (global as any)["watch"] = state; const actual = await sut.getBrowserSendComponentState(); expect(actual).toBeInstanceOf(BrowserSendComponentState); diff --git a/apps/browser/src/services/state.service.ts b/apps/browser/src/services/browser-state.service.ts similarity index 81% rename from apps/browser/src/services/state.service.ts rename to apps/browser/src/services/browser-state.service.ts index 3f4161f5a6b..24630dcbaef 100644 --- a/apps/browser/src/services/state.service.ts +++ b/apps/browser/src/services/browser-state.service.ts @@ -1,24 +1,40 @@ +import { BehaviorSubject } from "rxjs"; import { Jsonify } from "type-fest"; import { AbstractCachedStorageService } from "@bitwarden/common/abstractions/storage.service"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { StorageOptions } from "@bitwarden/common/models/domain/storage-options"; -import { - StateService as BaseStateService, - withPrototype, -} from "@bitwarden/common/services/state.service"; +import { StateService as BaseStateService } from "@bitwarden/common/services/state.service"; +import { browserSession, sessionSync } from "../decorators/session-sync-observable"; import { Account } from "../models/account"; import { BrowserComponentState } from "../models/browserComponentState"; import { BrowserGroupingsComponentState } from "../models/browserGroupingsComponentState"; import { BrowserSendComponentState } from "../models/browserSendComponentState"; -import { StateService as StateServiceAbstraction } from "./abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "./abstractions/browser-state.service"; -export class StateService +@browserSession +export class BrowserStateService extends BaseStateService implements StateServiceAbstraction { + @sessionSync({ + initializer: Account.fromJSON as any, // TODO: Remove this any when all any types are removed from Account + initializeAs: "record", + }) + protected accountsSubject: BehaviorSubject<{ [userId: string]: Account }>; + @sessionSync({ ctor: String }) + protected activeAccountSubject: BehaviorSubject; + @sessionSync({ ctor: Boolean }) + protected activeAccountUnlockedSubject: BehaviorSubject; + + protected accountDeserializer = Account.fromJSON; + + async hasInSessionMemory(key: string): Promise { + return await this.memoryStorageService.has(key); + } + async getFromSessionMemory(key: string, deserializer?: (obj: Jsonify) => T): Promise { return this.memoryStorageService instanceof AbstractCachedStorageService ? await this.memoryStorageService.getBypassCache(key, { deserializer: deserializer }) @@ -44,7 +60,6 @@ export class StateService ); } - @withPrototype(BrowserGroupingsComponentState) async getBrowserGroupingComponentState( options?: StorageOptions ): Promise { @@ -67,7 +82,6 @@ export class StateService ); } - @withPrototype(BrowserComponentState) async getBrowserVaultItemsComponentState( options?: StorageOptions ): Promise { @@ -90,7 +104,6 @@ export class StateService ); } - @withPrototype(BrowserSendComponentState) async getBrowserSendComponentState(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) @@ -111,7 +124,6 @@ export class StateService ); } - @withPrototype(BrowserComponentState) async getBrowserSendTypeComponentState(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) diff --git a/libs/angular/test-utils.ts b/libs/angular/test-utils.ts new file mode 100644 index 00000000000..a2422e698fd --- /dev/null +++ b/libs/angular/test-utils.ts @@ -0,0 +1,3 @@ +export async function awaitAsync(ms = 0) { + await new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/libs/common/spec/misc/utils.spec.ts b/libs/common/spec/misc/utils.spec.ts index e1de2655322..fb57c33fff1 100644 --- a/libs/common/spec/misc/utils.spec.ts +++ b/libs/common/spec/misc/utils.spec.ts @@ -241,4 +241,72 @@ describe("Utils Service", () => { expect(Utils.fromByteStringToArray(null)).toEqual(null); }); }); + + describe("mapToRecord", () => { + it("should handle null", () => { + expect(Utils.mapToRecord(null)).toEqual(null); + }); + + it("should handle empty map", () => { + expect(Utils.mapToRecord(new Map())).toEqual({}); + }); + + it("should handle convert a Map to a Record", () => { + const map = new Map([ + ["key1", "value1"], + ["key2", "value2"], + ]); + expect(Utils.mapToRecord(map)).toEqual({ key1: "value1", key2: "value2" }); + }); + + it("should handle convert a Map to a Record with non-string keys", () => { + const map = new Map([ + [1, "value1"], + [2, "value2"], + ]); + const result = Utils.mapToRecord(map); + expect(result).toEqual({ 1: "value1", 2: "value2" }); + expect(Utils.recordToMap(result)).toEqual(map); + }); + + it("should not convert an object if it's not a map", () => { + const obj = { key1: "value1", key2: "value2" }; + expect(Utils.mapToRecord(obj as any)).toEqual(obj); + }); + }); + + describe("recordToMap", () => { + it("should handle null", () => { + expect(Utils.recordToMap(null)).toEqual(null); + }); + + it("should handle empty record", () => { + expect(Utils.recordToMap({})).toEqual(new Map()); + }); + + it("should handle convert a Record to a Map", () => { + const record = { key1: "value1", key2: "value2" }; + expect(Utils.recordToMap(record)).toEqual(new Map(Object.entries(record))); + }); + + it("should handle convert a Record to a Map with non-string keys", () => { + const record = { 1: "value1", 2: "value2" }; + const result = Utils.recordToMap(record); + expect(result).toEqual( + new Map([ + [1, "value1"], + [2, "value2"], + ]) + ); + expect(Utils.mapToRecord(result)).toEqual(record); + }); + + it("should not convert an object if already a map", () => { + const map = new Map([ + ["key1", "value1"], + ["key2", "value2"], + ]); + expect(Utils.recordToMap(map as any)).toEqual(map); + }); + }); }); diff --git a/libs/common/src/misc/utils.ts b/libs/common/src/misc/utils.ts index ff21a919f1f..ead692d6663 100644 --- a/libs/common/src/misc/utils.ts +++ b/libs/common/src/misc/utils.ts @@ -1,5 +1,6 @@ /* eslint-disable no-useless-escape */ import { getHostname, parse } from "tldts"; +import { Merge } from "type-fest"; import { CryptoService } from "../abstractions/crypto.service"; import { EncryptService } from "../abstractions/encrypt.service"; @@ -55,6 +56,10 @@ export class Utils { } static fromB64ToArray(str: string): Uint8Array { + if (str == null) { + return null; + } + if (Utils.isNode) { return new Uint8Array(Buffer.from(str, "base64")); } else { @@ -108,6 +113,9 @@ export class Utils { } static fromBufferToB64(buffer: ArrayBuffer): string { + if (buffer == null) { + return null; + } if (Utils.isNode) { return Buffer.from(buffer).toString("base64"); } else { @@ -423,6 +431,57 @@ export class Utils { return this.global.bitwardenContainerService; } + /** + * Converts map to a Record with the same data. Inverse of recordToMap + * Useful in toJSON methods, since Maps are not serializable + * @param map + * @returns + */ + static mapToRecord(map: Map): Record { + if (map == null) { + return null; + } + if (!(map instanceof Map)) { + return map; + } + return Object.fromEntries(map); + } + + /** + * Converts record to a Map with the same data. Inverse of mapToRecord + * Useful in fromJSON methods, since Maps are not serializable + * + * Warning: If the record has string keys that are numbers, they will be converted to numbers in the map + * @param record + * @returns + */ + static recordToMap(record: Record): Map { + if (record == null) { + return null; + } else if (record instanceof Map) { + return record; + } + + const entries = Object.entries(record); + if (entries.length === 0) { + return new Map(); + } + + if (isNaN(Number(entries[0][0]))) { + return new Map(entries) as Map; + } else { + return new Map(entries.map((e) => [Number(e[0]), e[1]])) as Map; + } + } + + /** Applies Object.assign, but converts the type nicely using Type-Fest Merge */ + static merge( + destination: Destination, + source: Source + ): Merge { + return Object.assign(destination, source) as unknown as Merge; + } + private static isMobile(win: Window) { let mobile = false; ((a) => { diff --git a/libs/common/src/models/domain/account.ts b/libs/common/src/models/domain/account.ts index 75a1eebfebb..0715b0c49b6 100644 --- a/libs/common/src/models/domain/account.ts +++ b/libs/common/src/models/domain/account.ts @@ -1,4 +1,4 @@ -import { Except, Jsonify } from "type-fest"; +import { Jsonify } from "type-fest"; import { AuthenticationStatus } from "../../enums/authenticationStatus"; import { KdfType } from "../../enums/kdfType"; @@ -40,7 +40,7 @@ export class EncryptionPair { } static fromJSON( - obj: Jsonify, Jsonify>>, + obj: { encrypted?: Jsonify; decrypted?: string | Jsonify }, decryptedFromJson?: (decObj: Jsonify | string) => TDecrypted, encryptedFromJson?: (encObj: Jsonify) => TEncrypted ) { @@ -123,7 +123,7 @@ export class AccountKeys { apiKeyClientSecret?: string; toJSON() { - return Object.assign(this as Except, { + return Utils.merge(this, { publicKey: Utils.fromBufferToByteString(this.publicKey), }); } @@ -251,7 +251,7 @@ export class AccountSettings { } export type AccountSettingsSettings = { - equivalentDomains?: { [id: string]: any }; + equivalentDomains?: string[][]; }; export class AccountTokens { diff --git a/libs/common/src/models/domain/organization.ts b/libs/common/src/models/domain/organization.ts index faf1ee1f978..9e1f63ccb0e 100644 --- a/libs/common/src/models/domain/organization.ts +++ b/libs/common/src/models/domain/organization.ts @@ -1,3 +1,5 @@ +import { Jsonify } from "type-fest"; + import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; import { OrganizationUserType } from "../../enums/organizationUserType"; import { ProductType } from "../../enums/productType"; @@ -201,4 +203,15 @@ export class Organization { get hasProvider() { return this.providerId != null || this.providerName != null; } + + static fromJSON(json: Jsonify) { + if (json == null) { + return null; + } + + return Object.assign(new Organization(), json, { + familySponsorshipLastSyncDate: new Date(json.familySponsorshipLastSyncDate), + familySponsorshipValidUntil: new Date(json.familySponsorshipValidUntil), + }); + } } diff --git a/libs/common/src/models/domain/state.spec.ts b/libs/common/src/models/domain/state.spec.ts index 64e71d7cb2c..aa4f36549c2 100644 --- a/libs/common/src/models/domain/state.spec.ts +++ b/libs/common/src/models/domain/state.spec.ts @@ -4,22 +4,25 @@ import { State } from "./state"; describe("state", () => { describe("fromJSON", () => { it("should deserialize to an instance of itself", () => { - expect(State.fromJSON({})).toBeInstanceOf(State); + expect(State.fromJSON({}, () => new Account({}))).toBeInstanceOf(State); }); it("should always assign an object to accounts", () => { - const state = State.fromJSON({}); + const state = State.fromJSON({}, () => new Account({})); expect(state.accounts).not.toBeNull(); expect(state.accounts).toEqual({}); }); it("should build an account map", () => { const accountsSpy = jest.spyOn(Account, "fromJSON"); - const state = State.fromJSON({ - accounts: { - userId: {}, + const state = State.fromJSON( + { + accounts: { + userId: {}, + }, }, - }); + Account.fromJSON + ); expect(state.accounts["userId"]).toBeInstanceOf(Account); expect(accountsSpy).toHaveBeenCalled(); diff --git a/libs/common/src/models/domain/state.ts b/libs/common/src/models/domain/state.ts index be99d9bdabe..c08e6fe70f6 100644 --- a/libs/common/src/models/domain/state.ts +++ b/libs/common/src/models/domain/state.ts @@ -19,26 +19,28 @@ export class State< // TODO, make Jsonify work. It currently doesn't because Globals doesn't implement Jsonify. static fromJSON( - obj: any + obj: any, + accountDeserializer: (json: Jsonify) => TAccount ): State { if (obj == null) { return null; } return Object.assign(new State(null), obj, { - accounts: State.buildAccountMapFromJSON(obj?.accounts), + accounts: State.buildAccountMapFromJSON(obj?.accounts, accountDeserializer), }); } - private static buildAccountMapFromJSON( - jsonAccounts: Jsonify<{ [userId: string]: Jsonify }> + private static buildAccountMapFromJSON( + jsonAccounts: { [userId: string]: Jsonify }, + accountDeserializer: (json: Jsonify) => TAccount ) { if (!jsonAccounts) { return {}; } - const accounts: { [userId: string]: Account } = {}; + const accounts: { [userId: string]: TAccount } = {}; for (const userId in jsonAccounts) { - accounts[userId] = Account.fromJSON(jsonAccounts[userId]); + accounts[userId] = accountDeserializer(jsonAccounts[userId]); } return accounts; } diff --git a/libs/common/src/models/view/send-file.view.ts b/libs/common/src/models/view/send-file.view.ts index 7ed291a2b73..3ac12b8203d 100644 --- a/libs/common/src/models/view/send-file.view.ts +++ b/libs/common/src/models/view/send-file.view.ts @@ -1,3 +1,4 @@ +import { DeepJsonify } from "../../types/deep-jsonify"; import { SendFile } from "../domain/send-file"; import { View } from "./view"; @@ -28,4 +29,12 @@ export class SendFileView implements View { } return 0; } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new SendFileView(), json); + } } diff --git a/libs/common/src/models/view/send-text.view.ts b/libs/common/src/models/view/send-text.view.ts index cd10c799b64..638f66ad661 100644 --- a/libs/common/src/models/view/send-text.view.ts +++ b/libs/common/src/models/view/send-text.view.ts @@ -1,3 +1,4 @@ +import { DeepJsonify } from "../../types/deep-jsonify"; import { SendText } from "../domain/send-text"; import { View } from "./view"; @@ -17,4 +18,12 @@ export class SendTextView implements View { get maskedText(): string { return this.text != null ? "••••••••" : null; } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new SendTextView(), json); + } } diff --git a/libs/common/src/models/view/send.view.ts b/libs/common/src/models/view/send.view.ts index 3ef6bf9f0e3..c5da1d2f683 100644 --- a/libs/common/src/models/view/send.view.ts +++ b/libs/common/src/models/view/send.view.ts @@ -1,5 +1,6 @@ import { SendType } from "../../enums/sendType"; import { Utils } from "../../misc/utils"; +import { DeepJsonify } from "../../types/deep-jsonify"; import { Send } from "../domain/send"; import { SymmetricCryptoKey } from "../domain/symmetric-crypto-key"; @@ -65,4 +66,26 @@ export class SendView implements View { get pendingDelete(): boolean { return this.deletionDate <= new Date(); } + + toJSON() { + return Utils.merge(this, { + key: Utils.fromBufferToB64(this.key), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new SendView(), json, { + key: Utils.fromB64ToArray(json.key)?.buffer, + cryptoKey: SymmetricCryptoKey.fromJSON(json.cryptoKey), + text: SendTextView.fromJSON(json.text), + file: SendFileView.fromJSON(json.file), + revisionDate: json.revisionDate == null ? null : new Date(json.revisionDate), + deletionDate: json.deletionDate == null ? null : new Date(json.deletionDate), + expirationDate: json.expirationDate == null ? null : new Date(json.expirationDate), + }); + } } diff --git a/libs/common/src/services/cipher.service.ts b/libs/common/src/services/cipher.service.ts index a7b54942e2c..b93c60d6228 100644 --- a/libs/common/src/services/cipher.service.ts +++ b/libs/common/src/services/cipher.service.ts @@ -412,7 +412,7 @@ export class CipherService implements CipherServiceAbstraction { : firstValueFrom(this.settingsService.settings$).then( (settings: AccountSettingsSettings) => { let matches: any[] = []; - settings.equivalentDomains?.forEach((eqDomain: any) => { + settings?.equivalentDomains?.forEach((eqDomain: any) => { if (eqDomain.length && eqDomain.indexOf(domain) >= 0) { matches = matches.concat(eqDomain); } diff --git a/libs/common/src/services/organization/organization.service.ts b/libs/common/src/services/organization/organization.service.ts index 5d936c53ae7..b0d7791ec26 100644 --- a/libs/common/src/services/organization/organization.service.ts +++ b/libs/common/src/services/organization/organization.service.ts @@ -6,7 +6,7 @@ import { OrganizationData } from "../../models/data/organization.data"; import { Organization } from "../../models/domain/organization"; export class OrganizationService implements InternalOrganizationServiceAbstraction { - private _organizations = new BehaviorSubject([]); + protected _organizations = new BehaviorSubject([]); organizations$ = this._organizations.asObservable(); diff --git a/libs/common/src/services/policy/policy.service.ts b/libs/common/src/services/policy/policy.service.ts index 757c3b3e05f..c20682184bc 100644 --- a/libs/common/src/services/policy/policy.service.ts +++ b/libs/common/src/services/policy/policy.service.ts @@ -16,7 +16,7 @@ import { ListResponse } from "../../models/response/list.response"; import { PolicyResponse } from "../../models/response/policy.response"; export class PolicyService implements InternalPolicyServiceAbstraction { - private _policies: BehaviorSubject = new BehaviorSubject([]); + protected _policies: BehaviorSubject = new BehaviorSubject([]); policies$ = this._policies.asObservable(); diff --git a/libs/common/src/services/settings.service.ts b/libs/common/src/services/settings.service.ts index 923bd8970dd..4a6986e37f1 100644 --- a/libs/common/src/services/settings.service.ts +++ b/libs/common/src/services/settings.service.ts @@ -6,7 +6,7 @@ import { Utils } from "../misc/utils"; import { AccountSettingsSettings } from "../models/domain/account"; export class SettingsService implements SettingsServiceAbstraction { - private _settings: BehaviorSubject = new BehaviorSubject({}); + protected _settings: BehaviorSubject = new BehaviorSubject({}); settings$ = this._settings.asObservable(); diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index 0c1c3b725ab..fe787f21c0e 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -1,4 +1,5 @@ import { BehaviorSubject, concatMap } from "rxjs"; +import { Jsonify } from "type-fest"; import { LogService } from "../abstractions/log.service"; import { StateService as StateServiceAbstraction } from "../abstractions/state.service"; @@ -13,6 +14,7 @@ import { StorageLocation } from "../enums/storageLocation"; import { ThemeType } from "../enums/themeType"; import { UriMatchType } from "../enums/uriMatchType"; import { StateFactory } from "../factories/stateFactory"; +import { Utils } from "../misc/utils"; import { CipherData } from "../models/data/cipher.data"; import { CollectionData } from "../models/data/collection.data"; import { EncryptedOrganizationKeyData } from "../models/data/encrypted-organization-key.data"; @@ -65,13 +67,13 @@ export class StateService< TAccount extends Account = Account > implements StateServiceAbstraction { - private accountsSubject = new BehaviorSubject<{ [userId: string]: TAccount }>({}); + protected accountsSubject = new BehaviorSubject<{ [userId: string]: TAccount }>({}); accounts$ = this.accountsSubject.asObservable(); - private activeAccountSubject = new BehaviorSubject(null); + protected activeAccountSubject = new BehaviorSubject(null); activeAccount$ = this.activeAccountSubject.asObservable(); - private activeAccountUnlockedSubject = new BehaviorSubject(false); + protected activeAccountUnlockedSubject = new BehaviorSubject(false); activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable(); private hasBeenInited = false; @@ -79,6 +81,9 @@ export class StateService< private accountDiskCache = new Map(); + // default account serializer, must be overridden by child class + protected accountDeserializer = Account.fromJSON as (json: Jsonify) => TAccount; + constructor( protected storageService: AbstractStorageService, protected secureStorageService: AbstractStorageService, @@ -676,7 +681,7 @@ export class StateService< const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); - return this.recordToMap(account?.keys?.organizationKeys?.decrypted); + return Utils.recordToMap(account?.keys?.organizationKeys?.decrypted); } async setDecryptedOrganizationKeys( @@ -686,7 +691,7 @@ export class StateService< const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); - account.keys.organizationKeys.decrypted = this.mapToRecord(value); + account.keys.organizationKeys.decrypted = Utils.mapToRecord(value); await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) @@ -774,7 +779,7 @@ export class StateService< const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); - return this.recordToMap(account?.keys?.providerKeys?.decrypted); + return Utils.recordToMap(account?.keys?.providerKeys?.decrypted); } async setDecryptedProviderKeys( @@ -784,7 +789,7 @@ export class StateService< const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); - account.keys.providerKeys.decrypted = this.mapToRecord(value); + account.keys.providerKeys.decrypted = Utils.mapToRecord(value); await this.saveAccount( account, this.reconcileOptions(options, await this.defaultInMemoryOptions()) @@ -2744,7 +2749,7 @@ export class StateService< protected async state(): Promise> { const state = await this.memoryStorageService.get>(keys.state, { - deserializer: (s) => State.fromJSON(s), + deserializer: (s) => State.fromJSON(s, this.accountDeserializer), }); return state; } @@ -2765,50 +2770,6 @@ export class StateService< await this.setState(updatedState); }); } - - private mapToRecord(map: Map): Record { - return map == null ? null : Object.fromEntries(map); - } - - private recordToMap(record: Record): Map { - return record == null ? null : new Map(Object.entries(record)); - } -} - -export function withPrototype( - constructor: new (...args: any[]) => T, - converter: (input: any) => T = (i) => i -): ( - target: any, - propertyKey: string | symbol, - descriptor: PropertyDescriptor -) => { value: (...args: any[]) => Promise } { - return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { - const originalMethod = descriptor.value; - - return { - value: function (...args: any[]) { - const originalResult: Promise = originalMethod.apply(this, args); - - if (!(originalResult instanceof Promise)) { - throw new Error( - `Error applying prototype to stored value -- result is not a promise for method ${String( - propertyKey - )}` - ); - } - - return originalResult.then((result) => { - return result == null || - result.constructor.name === constructor.prototype.constructor.name - ? converter(result as T) - : converter( - Object.create(constructor.prototype, Object.getOwnPropertyDescriptors(result)) as T - ); - }); - }, - }; - }; } function withPrototypeForArrayMembers( @@ -2847,7 +2808,7 @@ function withPrototypeForArrayMembers( return result.map((r) => { return r == null || r.constructor.name === memberConstructor.prototype.constructor.name - ? memberConverter(r) + ? r : memberConverter( Object.create(memberConstructor.prototype, Object.getOwnPropertyDescriptors(r)) ); From 175eef53763d47899fdc7c6f59e0f27144050aa1 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Thu, 24 Nov 2022 14:55:24 +0100 Subject: [PATCH 02/40] [TDL-192] Rename email forwarders per ADR #12 (#4107) --- .github/whitelist-capital-letters.txt | 7 ------- .../anon-addy-forwarder.ts} | 2 +- .../duck-duck-go-forwarder.ts} | 2 +- .../fastmail-forwarder.ts} | 2 +- .../firefox-relay-forwarder.ts} | 2 +- .../forwarder-options.ts} | 0 .../forwarder.ts | 2 +- .../simple-login-forwarder.ts} | 2 +- .../src/services/usernameGeneration.service.ts | 14 +++++++------- 9 files changed, 13 insertions(+), 20 deletions(-) rename libs/common/src/{emailForwarders/anonAddyForwarder.ts => email-forwarders/anon-addy-forwarder.ts} (96%) rename libs/common/src/{emailForwarders/duckDuckGoForwarder.ts => email-forwarders/duck-duck-go-forwarder.ts} (95%) rename libs/common/src/{emailForwarders/fastmailForwarder.ts => email-forwarders/fastmail-forwarder.ts} (98%) rename libs/common/src/{emailForwarders/firefoxRelayForwarder.ts => email-forwarders/firefox-relay-forwarder.ts} (95%) rename libs/common/src/{emailForwarders/forwarderOptions.ts => email-forwarders/forwarder-options.ts} (100%) rename libs/common/src/{emailForwarders => email-forwarders}/forwarder.ts (75%) rename libs/common/src/{emailForwarders/simpleLoginForwarder.ts => email-forwarders/simple-login-forwarder.ts} (96%) diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 5dd8cd621ba..1b5a775d835 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -11,7 +11,6 @@ ./libs/common/src/abstractions/fileDownload ./libs/common/src/abstractions/userVerification ./libs/common/src/abstractions/vaultTimeout -./libs/common/src/emailForwarders ./libs/common/src/misc/logInStrategies ./libs/common/src/services/userVerification ./libs/common/src/services/vaultTimeout @@ -112,12 +111,6 @@ ./libs/common/src/enums/nativeMessagingVersion.ts ./libs/common/src/enums/cipherRepromptType.ts ./libs/common/src/enums/organizationUserType.ts -./libs/common/src/emailForwarders/fastmailForwarder.ts -./libs/common/src/emailForwarders/duckDuckGoForwarder.ts -./libs/common/src/emailForwarders/firefoxRelayForwarder.ts -./libs/common/src/emailForwarders/anonAddyForwarder.ts -./libs/common/src/emailForwarders/simpleLoginForwarder.ts -./libs/common/src/emailForwarders/forwarderOptions.ts ./libs/common/src/factories/accountFactory.ts ./libs/common/src/factories/globalStateFactory.ts ./libs/common/src/factories/stateFactory.ts diff --git a/libs/common/src/emailForwarders/anonAddyForwarder.ts b/libs/common/src/email-forwarders/anon-addy-forwarder.ts similarity index 96% rename from libs/common/src/emailForwarders/anonAddyForwarder.ts rename to libs/common/src/email-forwarders/anon-addy-forwarder.ts index af4e3c9bce0..c4657f8372d 100644 --- a/libs/common/src/emailForwarders/anonAddyForwarder.ts +++ b/libs/common/src/email-forwarders/anon-addy-forwarder.ts @@ -1,7 +1,7 @@ import { ApiService } from "../abstractions/api.service"; import { Forwarder } from "./forwarder"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export class AnonAddyForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { diff --git a/libs/common/src/emailForwarders/duckDuckGoForwarder.ts b/libs/common/src/email-forwarders/duck-duck-go-forwarder.ts similarity index 95% rename from libs/common/src/emailForwarders/duckDuckGoForwarder.ts rename to libs/common/src/email-forwarders/duck-duck-go-forwarder.ts index a4730733874..a600c904bc7 100644 --- a/libs/common/src/emailForwarders/duckDuckGoForwarder.ts +++ b/libs/common/src/email-forwarders/duck-duck-go-forwarder.ts @@ -1,7 +1,7 @@ import { ApiService } from "../abstractions/api.service"; import { Forwarder } from "./forwarder"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export class DuckDuckGoForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { diff --git a/libs/common/src/emailForwarders/fastmailForwarder.ts b/libs/common/src/email-forwarders/fastmail-forwarder.ts similarity index 98% rename from libs/common/src/emailForwarders/fastmailForwarder.ts rename to libs/common/src/email-forwarders/fastmail-forwarder.ts index 19b7d043cf2..dc24c623a34 100644 --- a/libs/common/src/emailForwarders/fastmailForwarder.ts +++ b/libs/common/src/email-forwarders/fastmail-forwarder.ts @@ -1,7 +1,7 @@ import { ApiService } from "../abstractions/api.service"; import { Forwarder } from "./forwarder"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export class FastmailForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { diff --git a/libs/common/src/emailForwarders/firefoxRelayForwarder.ts b/libs/common/src/email-forwarders/firefox-relay-forwarder.ts similarity index 95% rename from libs/common/src/emailForwarders/firefoxRelayForwarder.ts rename to libs/common/src/email-forwarders/firefox-relay-forwarder.ts index bdfaecc117c..f64dc4b06a4 100644 --- a/libs/common/src/emailForwarders/firefoxRelayForwarder.ts +++ b/libs/common/src/email-forwarders/firefox-relay-forwarder.ts @@ -1,7 +1,7 @@ import { ApiService } from "../abstractions/api.service"; import { Forwarder } from "./forwarder"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export class FirefoxRelayForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { diff --git a/libs/common/src/emailForwarders/forwarderOptions.ts b/libs/common/src/email-forwarders/forwarder-options.ts similarity index 100% rename from libs/common/src/emailForwarders/forwarderOptions.ts rename to libs/common/src/email-forwarders/forwarder-options.ts diff --git a/libs/common/src/emailForwarders/forwarder.ts b/libs/common/src/email-forwarders/forwarder.ts similarity index 75% rename from libs/common/src/emailForwarders/forwarder.ts rename to libs/common/src/email-forwarders/forwarder.ts index 05b84c913af..da3d1bb485c 100644 --- a/libs/common/src/emailForwarders/forwarder.ts +++ b/libs/common/src/email-forwarders/forwarder.ts @@ -1,6 +1,6 @@ import { ApiService } from "../abstractions/api.service"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export interface Forwarder { generate(apiService: ApiService, options: ForwarderOptions): Promise; diff --git a/libs/common/src/emailForwarders/simpleLoginForwarder.ts b/libs/common/src/email-forwarders/simple-login-forwarder.ts similarity index 96% rename from libs/common/src/emailForwarders/simpleLoginForwarder.ts rename to libs/common/src/email-forwarders/simple-login-forwarder.ts index adc8a13aefc..ef1eecb59ba 100644 --- a/libs/common/src/emailForwarders/simpleLoginForwarder.ts +++ b/libs/common/src/email-forwarders/simple-login-forwarder.ts @@ -1,7 +1,7 @@ import { ApiService } from "../abstractions/api.service"; import { Forwarder } from "./forwarder"; -import { ForwarderOptions } from "./forwarderOptions"; +import { ForwarderOptions } from "./forwarder-options"; export class SimpleLoginForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { diff --git a/libs/common/src/services/usernameGeneration.service.ts b/libs/common/src/services/usernameGeneration.service.ts index 72eb11b3430..ce5857f37cc 100644 --- a/libs/common/src/services/usernameGeneration.service.ts +++ b/libs/common/src/services/usernameGeneration.service.ts @@ -2,13 +2,13 @@ import { ApiService } from "../abstractions/api.service"; import { CryptoService } from "../abstractions/crypto.service"; import { StateService } from "../abstractions/state.service"; import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service"; -import { AnonAddyForwarder } from "../emailForwarders/anonAddyForwarder"; -import { DuckDuckGoForwarder } from "../emailForwarders/duckDuckGoForwarder"; -import { FastmailForwarder } from "../emailForwarders/fastmailForwarder"; -import { FirefoxRelayForwarder } from "../emailForwarders/firefoxRelayForwarder"; -import { Forwarder } from "../emailForwarders/forwarder"; -import { ForwarderOptions } from "../emailForwarders/forwarderOptions"; -import { SimpleLoginForwarder } from "../emailForwarders/simpleLoginForwarder"; +import { AnonAddyForwarder } from "../email-forwarders/anon-addy-forwarder"; +import { DuckDuckGoForwarder } from "../email-forwarders/duck-duck-go-forwarder"; +import { FastmailForwarder } from "../email-forwarders/fastmail-forwarder"; +import { FirefoxRelayForwarder } from "../email-forwarders/firefox-relay-forwarder"; +import { Forwarder } from "../email-forwarders/forwarder"; +import { ForwarderOptions } from "../email-forwarders/forwarder-options"; +import { SimpleLoginForwarder } from "../email-forwarders/simple-login-forwarder"; import { EFFLongWordList } from "../misc/wordlist"; const DefaultOptions = { From db2d8aaa7eb86392149790e1fff01ae73ff6d6c6 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Thu, 24 Nov 2022 15:05:45 +0100 Subject: [PATCH 03/40] Rename files and folders per ADR #12 (#4106) Renamed folders: ./apps/desktop/src/models/nativeMessaging ./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads ./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses Renamed all files Cleaned up entries in whitelist-capital-letters.txt --- .github/whitelist-capital-letters.txt | 36 ------------------- .../src/commands/bw-credential-create.ts | 6 ++-- .../src/commands/bw-credential-retrieval.ts | 4 +-- .../src/commands/bw-credential-update.ts | 6 ++-- .../src/commands/bw-generate-password.ts | 4 +-- .../src/commands/bw-handshake.ts | 4 +-- .../src/commands/bw-status.ts | 4 +-- .../src/{ipcService.ts => ipc.service.ts} | 4 +-- .../src/{logUtils.ts => log-utils.ts} | 0 ...geService.ts => native-message.service.ts} | 18 +++++----- ...ce.ts => desktop-file-download.service.ts} | 0 apps/desktop/src/app/services/init.service.ts | 2 +- .../src/app/services/services.module.ts | 10 +++--- apps/desktop/src/main.ts | 6 ++-- ...=> desktop-credential-storage-listener.ts} | 0 ...aging.main.ts => native-messaging.main.ts} | 0 ...rMonitor.main.ts => power-monitor.main.ts} | 0 .../decrypted-command-data.ts} | 2 +- .../encrypted-command.ts} | 0 .../credential-create-payload.ts} | 0 .../credential-retrieve-payload.ts} | 0 .../credential-update-payload.ts} | 0 .../password-generate-payload.ts} | 0 .../encrypted-message-response.ts} | 2 +- .../account-status-response.ts} | 0 .../cannot-decrypt-error-response.ts} | 0 .../cipher-response.ts} | 0 .../encrypted-message-response.ts | 16 +++++++++ .../failure-status-response.ts} | 0 .../generate-response.ts} | 0 .../success-status-response.ts} | 0 .../user-status-error-response.ts} | 0 .../encrypted-message.ts} | 2 +- .../src/models/native-messaging/index.ts | 25 +++++++++++++ .../legacy-message-wrapper.ts} | 2 +- .../legacy-message.ts} | 0 .../message-common.ts} | 0 .../src/models/native-messaging/message.ts | 4 +++ .../unencrypted-command.ts} | 0 .../unencrypted-message-response.ts} | 2 +- .../unencrypted-message.ts} | 4 +-- .../encryptedMessageResponse.ts | 16 --------- .../src/models/nativeMessaging/index.ts | 25 ------------- .../src/models/nativeMessaging/message.ts | 4 --- ...s => encrypted-message-handler.service.ts} | 24 ++++++------- ...e.ts => native-message-handler.service.ts} | 14 ++++---- ...service.ts => native-messaging.service.ts} | 8 ++--- ...ervice.ts => password-reprompt.service.ts} | 0 48 files changed, 109 insertions(+), 145 deletions(-) rename apps/desktop/native-messaging-test-runner/src/{ipcService.ts => ipc.service.ts} (97%) rename apps/desktop/native-messaging-test-runner/src/{logUtils.ts => log-utils.ts} (100%) rename apps/desktop/native-messaging-test-runner/src/{nativeMessageService.ts => native-message.service.ts} (91%) rename apps/desktop/src/app/services/{desktopFileDownloadService.ts => desktop-file-download.service.ts} (100%) rename apps/desktop/src/main/{desktopCredentialStorageListener.ts => desktop-credential-storage-listener.ts} (100%) rename apps/desktop/src/main/{nativeMessaging.main.ts => native-messaging.main.ts} (100%) rename apps/desktop/src/main/{powerMonitor.main.ts => power-monitor.main.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/decryptedCommandData.ts => native-messaging/decrypted-command-data.ts} (60%) rename apps/desktop/src/models/{nativeMessaging/encryptedCommand.ts => native-messaging/encrypted-command.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts => native-messaging/encrypted-message-payloads/credential-create-payload.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts => native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts => native-messaging/encrypted-message-payloads/credential-update-payload.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts => native-messaging/encrypted-message-payloads/password-generate-payload.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponse.ts => native-messaging/encrypted-message-response.ts} (76%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts => native-messaging/encrypted-message-responses/account-status-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts => native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/cipherResponse.ts => native-messaging/encrypted-message-responses/cipher-response.ts} (100%) create mode 100644 apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts => native-messaging/encrypted-message-responses/failure-status-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/generateResponse.ts => native-messaging/encrypted-message-responses/generate-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/successStatusResponse.ts => native-messaging/encrypted-message-responses/success-status-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts => native-messaging/encrypted-message-responses/user-status-error-response.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/encryptedMessage.ts => native-messaging/encrypted-message.ts} (80%) create mode 100644 apps/desktop/src/models/native-messaging/index.ts rename apps/desktop/src/models/{nativeMessaging/legacyMessageWrapper.ts => native-messaging/legacy-message-wrapper.ts} (77%) rename apps/desktop/src/models/{nativeMessaging/legacyMessage.ts => native-messaging/legacy-message.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/messageCommon.ts => native-messaging/message-common.ts} (100%) create mode 100644 apps/desktop/src/models/native-messaging/message.ts rename apps/desktop/src/models/{nativeMessaging/unencryptedCommand.ts => native-messaging/unencrypted-command.ts} (100%) rename apps/desktop/src/models/{nativeMessaging/unencryptedMessageResponse.ts => native-messaging/unencrypted-message-response.ts} (85%) rename apps/desktop/src/models/{nativeMessaging/unencryptedMessage.ts => native-messaging/unencrypted-message.ts} (58%) delete mode 100644 apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts delete mode 100644 apps/desktop/src/models/nativeMessaging/index.ts delete mode 100644 apps/desktop/src/models/nativeMessaging/message.ts rename apps/desktop/src/services/{encryptedMessageHandlerService.ts => encrypted-message-handler.service.ts} (84%) rename apps/desktop/src/services/{nativeMessageHandler.service.ts => native-message-handler.service.ts} (92%) rename apps/desktop/src/services/{nativeMessaging.service.ts => native-messaging.service.ts} (95%) rename apps/desktop/src/services/{passwordReprompt.service.ts => password-reprompt.service.ts} (100%) diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 1b5a775d835..613e70ee2c0 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -4,9 +4,6 @@ ./apps/browser/src/safari/desktop/Base.lproj ./apps/browser/src/services/vaultTimeout ./apps/browser/store/windows/Assets -./apps/desktop/src/models/nativeMessaging -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses ./libs/common/spec/misc/logInStrategies ./libs/common/src/abstractions/fileDownload ./libs/common/src/abstractions/userVerification @@ -179,42 +176,9 @@ ./apps/desktop/resources/appx/StoreLogo.png ./apps/desktop/resources/appx/Wide310x150Logo.png ./apps/desktop/resources/appx/Square44x44Logo.png -./apps/desktop/native-messaging-test-runner/src/ipcService.ts -./apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts -./apps/desktop/native-messaging-test-runner/src/logUtils.ts ./apps/desktop/README.md ./apps/desktop/desktop_native/Cargo.toml ./apps/desktop/desktop_native/Cargo.lock -./apps/desktop/src/app/services/desktopFileDownloadService.ts -./apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts -./apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts -./apps/desktop/src/models/nativeMessaging/messageCommon.ts -./apps/desktop/src/models/nativeMessaging/legacyMessage.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts -./apps/desktop/src/main/desktopCredentialStorageListener.ts -./apps/desktop/src/main/powerMonitor.main.ts -./apps/desktop/src/main/nativeMessaging.main.ts -./apps/desktop/src/services/passwordReprompt.service.ts -./apps/desktop/src/services/encryptedMessageHandlerService.ts -./apps/desktop/src/services/nativeMessaging.service.ts -./apps/desktop/src/services/nativeMessageHandler.service.ts ./apps/cli/stores/chocolatey/tools/VERIFICATION.txt ./apps/cli/README.md ./apps/browser/README.md diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts index a017b4c72cb..5598f4e25ed 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts @@ -5,9 +5,9 @@ import { hideBin } from "yargs/helpers"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { CredentialCreatePayload } from "../../../src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { CredentialCreatePayload } from "../../../src/models/native-messaging/encrypted-message-payloads/credential-create-payload"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("name", { diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts index 17244623b55..553b5f8ca95 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts @@ -5,8 +5,8 @@ import { hideBin } from "yargs/helpers"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("uri", { diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts index ecfbd3f5bb8..9de17ef63d2 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts @@ -5,9 +5,9 @@ import { hideBin } from "yargs/helpers"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { CredentialUpdatePayload } from "../../../src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { CredentialUpdatePayload } from "../../../src/models/native-messaging/encrypted-message-payloads/credential-update-payload"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; // Command line arguments diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts index 34bb41abb95..eb711de9cb8 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts @@ -5,8 +5,8 @@ import { hideBin } from "yargs/helpers"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("userId", { diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts index f3098062c46..e22e9bc6b5e 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts @@ -2,8 +2,8 @@ import "module-alias/register"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; (async () => { diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts index 7782e203cb8..b2d520ff0c8 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts @@ -2,8 +2,8 @@ import "module-alias/register"; import { NativeMessagingVersion } from "@bitwarden/common/enums/nativeMessagingVersion"; -import { LogUtils } from "../logUtils"; -import NativeMessageService from "../nativeMessageService"; +import { LogUtils } from "../log-utils"; +import NativeMessageService from "../native-message.service"; import * as config from "../variables"; (async () => { diff --git a/apps/desktop/native-messaging-test-runner/src/ipcService.ts b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts similarity index 97% rename from apps/desktop/native-messaging-test-runner/src/ipcService.ts rename to apps/desktop/native-messaging-test-runner/src/ipc.service.ts index eadc69a3513..b1c1b9d16b6 100644 --- a/apps/desktop/native-messaging-test-runner/src/ipcService.ts +++ b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts @@ -2,8 +2,8 @@ import { homedir } from "os"; import * as NodeIPC from "node-ipc"; -import { MessageCommon } from "../../src/models/nativeMessaging/messageCommon"; -import { UnencryptedMessageResponse } from "../../src/models/nativeMessaging/unencryptedMessageResponse"; +import { MessageCommon } from "../../src/models/native-messaging/message-common"; +import { UnencryptedMessageResponse } from "../../src/models/native-messaging/unencrypted-message-response"; import Deferred from "./deferred"; import { race } from "./race"; diff --git a/apps/desktop/native-messaging-test-runner/src/logUtils.ts b/apps/desktop/native-messaging-test-runner/src/log-utils.ts similarity index 100% rename from apps/desktop/native-messaging-test-runner/src/logUtils.ts rename to apps/desktop/native-messaging-test-runner/src/log-utils.ts diff --git a/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts similarity index 91% rename from apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts rename to apps/desktop/native-messaging-test-runner/src/native-message.service.ts index d1f29493fc0..8ba6526384e 100644 --- a/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts +++ b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts @@ -9,16 +9,16 @@ import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; -import { DecryptedCommandData } from "../../src/models/nativeMessaging/decryptedCommandData"; -import { EncryptedMessage } from "../../src/models/nativeMessaging/encryptedMessage"; -import { CredentialCreatePayload } from "../../src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload"; -import { CredentialUpdatePayload } from "../../src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload"; -import { EncryptedMessageResponse } from "../../src/models/nativeMessaging/encryptedMessageResponse"; -import { MessageCommon } from "../../src/models/nativeMessaging/messageCommon"; -import { UnencryptedMessage } from "../../src/models/nativeMessaging/unencryptedMessage"; -import { UnencryptedMessageResponse } from "../../src/models/nativeMessaging/unencryptedMessageResponse"; +import { DecryptedCommandData } from "../../src/models/native-messaging/decrypted-command-data"; +import { EncryptedMessage } from "../../src/models/native-messaging/encrypted-message"; +import { CredentialCreatePayload } from "../../src/models/native-messaging/encrypted-message-payloads/credential-create-payload"; +import { CredentialUpdatePayload } from "../../src/models/native-messaging/encrypted-message-payloads/credential-update-payload"; +import { EncryptedMessageResponse } from "../../src/models/native-messaging/encrypted-message-response"; +import { MessageCommon } from "../../src/models/native-messaging/message-common"; +import { UnencryptedMessage } from "../../src/models/native-messaging/unencrypted-message"; +import { UnencryptedMessageResponse } from "../../src/models/native-messaging/unencrypted-message-response"; -import IPCService, { IPCOptions } from "./ipcService"; +import IPCService, { IPCOptions } from "./ipc.service"; import * as config from "./variables"; type HandshakeResponse = { diff --git a/apps/desktop/src/app/services/desktopFileDownloadService.ts b/apps/desktop/src/app/services/desktop-file-download.service.ts similarity index 100% rename from apps/desktop/src/app/services/desktopFileDownloadService.ts rename to apps/desktop/src/app/services/desktop-file-download.service.ts diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 525d593911d..c706136a758 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -18,7 +18,7 @@ import { EventService } from "@bitwarden/common/services/event.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vaultTimeout.service"; import { I18nService } from "../../services/i18n.service"; -import { NativeMessagingService } from "../../services/nativeMessaging.service"; +import { NativeMessagingService } from "../../services/native-messaging.service"; @Injectable() export class InitService { diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 6dc9b8b0dbb..cd07ce93d42 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -47,17 +47,17 @@ import { ElectronRendererSecureStorageService } from "@bitwarden/electron/servic import { ElectronRendererStorageService } from "@bitwarden/electron/services/electronRendererStorage.service"; import { Account } from "../../models/account"; -import { EncryptedMessageHandlerService } from "../../services/encryptedMessageHandlerService"; +import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service"; import { I18nService } from "../../services/i18n.service"; -import { NativeMessageHandlerService } from "../../services/nativeMessageHandler.service"; -import { NativeMessagingService } from "../../services/nativeMessaging.service"; -import { PasswordRepromptService } from "../../services/passwordReprompt.service"; +import { NativeMessageHandlerService } from "../../services/native-message-handler.service"; +import { NativeMessagingService } from "../../services/native-messaging.service"; +import { PasswordRepromptService } from "../../services/password-reprompt.service"; import { StateService } from "../../services/state.service"; import { LoginGuard } from "../guards/login.guard"; import { SearchBarService } from "../layout/search/search-bar.service"; +import { DesktopFileDownloadService } from "./desktop-file-download.service"; import { DesktopThemingService } from "./desktop-theming.service"; -import { DesktopFileDownloadService } from "./desktopFileDownloadService"; import { InitService } from "./init.service"; const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index d54d46487a2..8c99b81911a 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -14,11 +14,11 @@ import { UpdaterMain } from "@bitwarden/electron/updater.main"; import { WindowMain } from "@bitwarden/electron/window.main"; import { BiometricMain } from "./main/biometric/biometric.main"; -import { DesktopCredentialStorageListener } from "./main/desktopCredentialStorageListener"; +import { DesktopCredentialStorageListener } from "./main/desktop-credential-storage-listener"; import { MenuMain } from "./main/menu/menu.main"; import { MessagingMain } from "./main/messaging.main"; -import { NativeMessagingMain } from "./main/nativeMessaging.main"; -import { PowerMonitorMain } from "./main/powerMonitor.main"; +import { NativeMessagingMain } from "./main/native-messaging.main"; +import { PowerMonitorMain } from "./main/power-monitor.main"; import { Account } from "./models/account"; import { I18nService } from "./services/i18n.service"; diff --git a/apps/desktop/src/main/desktopCredentialStorageListener.ts b/apps/desktop/src/main/desktop-credential-storage-listener.ts similarity index 100% rename from apps/desktop/src/main/desktopCredentialStorageListener.ts rename to apps/desktop/src/main/desktop-credential-storage-listener.ts diff --git a/apps/desktop/src/main/nativeMessaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts similarity index 100% rename from apps/desktop/src/main/nativeMessaging.main.ts rename to apps/desktop/src/main/native-messaging.main.ts diff --git a/apps/desktop/src/main/powerMonitor.main.ts b/apps/desktop/src/main/power-monitor.main.ts similarity index 100% rename from apps/desktop/src/main/powerMonitor.main.ts rename to apps/desktop/src/main/power-monitor.main.ts diff --git a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts similarity index 60% rename from apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts rename to apps/desktop/src/models/native-messaging/decrypted-command-data.ts index 0aea170f6e6..eeab234d7a4 100644 --- a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts +++ b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts @@ -1,4 +1,4 @@ -import { EncryptedCommand } from "./encryptedCommand"; +import { EncryptedCommand } from "./encrypted-command"; export type DecryptedCommandData = { command: EncryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedCommand.ts b/apps/desktop/src/models/native-messaging/encrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedCommand.ts rename to apps/desktop/src/models/native-messaging/encrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts similarity index 76% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-response.ts index bed85b65e96..5f8deb05a30 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessageResponse = MessageCommon & { encryptedPayload: EncString; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts diff --git a/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts new file mode 100644 index 00000000000..a0d35a7adbd --- /dev/null +++ b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts @@ -0,0 +1,16 @@ +import { AccountStatusResponse } from "./account-status-response"; +import { CannotDecryptErrorResponse } from "./cannot-decrypt-error-response"; +import { CipherResponse } from "./cipher-response"; +import { FailureStatusResponse } from "./failure-status-response"; +import { GenerateResponse } from "./generate-response"; +import { SuccessStatusResponse } from "./success-status-response"; +import { UserStatusErrorResponse } from "./user-status-error-response"; + +export type EncyptedMessageResponse = + | AccountStatusResponse[] + | CannotDecryptErrorResponse + | CipherResponse[] + | FailureStatusResponse + | GenerateResponse + | SuccessStatusResponse + | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts b/apps/desktop/src/models/native-messaging/encrypted-message.ts similarity index 80% rename from apps/desktop/src/models/nativeMessaging/encryptedMessage.ts rename to apps/desktop/src/models/native-messaging/encrypted-message.ts index bd0b5177a40..fc06214875e 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessage = MessageCommon & { // Will decrypt to a DecryptedCommandData object diff --git a/apps/desktop/src/models/native-messaging/index.ts b/apps/desktop/src/models/native-messaging/index.ts new file mode 100644 index 00000000000..7f6701886af --- /dev/null +++ b/apps/desktop/src/models/native-messaging/index.ts @@ -0,0 +1,25 @@ +export * from "./encrypted-message-payloads/credential-create-payload"; +export * from "./encrypted-message-payloads/credential-retrieve-payload"; +export * from "./encrypted-message-payloads/credential-update-payload"; +export * from "./encrypted-message-payloads/password-generate-payload"; + +export * from "./encrypted-message-responses/account-status-response"; +export * from "./encrypted-message-responses/cannot-decrypt-error-response"; +export * from "./encrypted-message-responses/cipher-response"; +export * from "./encrypted-message-responses/encrypted-message-response"; +export * from "./encrypted-message-responses/failure-status-response"; +export * from "./encrypted-message-responses/generate-response"; +export * from "./encrypted-message-responses/success-status-response"; +export * from "./encrypted-message-responses/user-status-error-response"; + +export * from "./decrypted-command-data"; +export * from "./encrypted-command"; +export * from "./encrypted-message"; +export * from "./encrypted-message-response"; +export * from "./legacy-message"; +export * from "./legacy-message-wrapper"; +export * from "./message"; +export * from "./message-common"; +export * from "./unencrypted-command"; +export * from "./unencrypted-message"; +export * from "./unencrypted-message-response"; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts similarity index 77% rename from apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts rename to apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts index 5ef10e73289..03068374069 100644 --- a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts +++ b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { LegacyMessage } from "./legacyMessage"; +import { LegacyMessage } from "./legacy-message"; export type LegacyMessageWrapper = { message: LegacyMessage | EncString; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessage.ts b/apps/desktop/src/models/native-messaging/legacy-message.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/legacyMessage.ts rename to apps/desktop/src/models/native-messaging/legacy-message.ts diff --git a/apps/desktop/src/models/nativeMessaging/messageCommon.ts b/apps/desktop/src/models/native-messaging/message-common.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/messageCommon.ts rename to apps/desktop/src/models/native-messaging/message-common.ts diff --git a/apps/desktop/src/models/native-messaging/message.ts b/apps/desktop/src/models/native-messaging/message.ts new file mode 100644 index 00000000000..b76666bf142 --- /dev/null +++ b/apps/desktop/src/models/native-messaging/message.ts @@ -0,0 +1,4 @@ +import { EncryptedMessage } from "./encrypted-message"; +import { UnencryptedMessage } from "./unencrypted-message"; + +export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts b/apps/desktop/src/models/native-messaging/unencrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts rename to apps/desktop/src/models/native-messaging/unencrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts similarity index 85% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message-response.ts index a564b1324e2..b4f71ec6253 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts @@ -1,4 +1,4 @@ -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type UnencryptedMessageResponse = MessageCommon & ( diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts b/apps/desktop/src/models/native-messaging/unencrypted-message.ts similarity index 58% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message.ts index a665f312a98..d4ddb83ad68 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message.ts @@ -1,5 +1,5 @@ -import { MessageCommon } from "./messageCommon"; -import { UnencryptedCommand } from "./unencryptedCommand"; +import { MessageCommon } from "./message-common"; +import { UnencryptedCommand } from "./unencrypted-command"; export type UnencryptedMessage = MessageCommon & { command: UnencryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts b/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts deleted file mode 100644 index a10da2d8571..00000000000 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AccountStatusResponse } from "./accountStatusResponse"; -import { CannotDecryptErrorResponse } from "./cannotDecryptErrorResponse"; -import { CipherResponse } from "./cipherResponse"; -import { FailureStatusResponse } from "./failureStatusResponse"; -import { GenerateResponse } from "./generateResponse"; -import { SuccessStatusResponse } from "./successStatusResponse"; -import { UserStatusErrorResponse } from "./userStatusErrorResponse"; - -export type EncyptedMessageResponse = - | AccountStatusResponse[] - | CannotDecryptErrorResponse - | CipherResponse[] - | FailureStatusResponse - | GenerateResponse - | SuccessStatusResponse - | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/index.ts b/apps/desktop/src/models/nativeMessaging/index.ts deleted file mode 100644 index 1b0148c3f90..00000000000 --- a/apps/desktop/src/models/nativeMessaging/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export * from "./encryptedMessagePayloads/credentialCreatePayload"; -export * from "./encryptedMessagePayloads/credentialRetrievePayload"; -export * from "./encryptedMessagePayloads/credentialUpdatePayload"; -export * from "./encryptedMessagePayloads/passwordGeneratePayload"; - -export * from "./encryptedMessageResponses/accountStatusResponse"; -export * from "./encryptedMessageResponses/cannotDecryptErrorResponse"; -export * from "./encryptedMessageResponses/cipherResponse"; -export * from "./encryptedMessageResponses/encryptedMessageResponse"; -export * from "./encryptedMessageResponses/failureStatusResponse"; -export * from "./encryptedMessageResponses/generateResponse"; -export * from "./encryptedMessageResponses/successStatusResponse"; -export * from "./encryptedMessageResponses/userStatusErrorResponse"; - -export * from "./decryptedCommandData"; -export * from "./encryptedCommand"; -export * from "./encryptedMessage"; -export * from "./encryptedMessageResponse"; -export * from "./legacyMessage"; -export * from "./legacyMessageWrapper"; -export * from "./message"; -export * from "./messageCommon"; -export * from "./unencryptedCommand"; -export * from "./unencryptedMessage"; -export * from "./unencryptedMessageResponse"; diff --git a/apps/desktop/src/models/nativeMessaging/message.ts b/apps/desktop/src/models/nativeMessaging/message.ts deleted file mode 100644 index 25d96aa218a..00000000000 --- a/apps/desktop/src/models/nativeMessaging/message.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { EncryptedMessage } from "./encryptedMessage"; -import { UnencryptedMessage } from "./unencryptedMessage"; - -export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/services/encryptedMessageHandlerService.ts b/apps/desktop/src/services/encrypted-message-handler.service.ts similarity index 84% rename from apps/desktop/src/services/encryptedMessageHandlerService.ts rename to apps/desktop/src/services/encrypted-message-handler.service.ts index f5ac1e5fcef..5f45118768e 100644 --- a/apps/desktop/src/services/encryptedMessageHandlerService.ts +++ b/apps/desktop/src/services/encrypted-message-handler.service.ts @@ -12,18 +12,18 @@ import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/models/view/login.view"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { CredentialCreatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload"; -import { CredentialRetrievePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload"; -import { CredentialUpdatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload"; -import { PasswordGeneratePayload } from "../models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload"; -import { AccountStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/accountStatusResponse"; -import { CipherResponse } from "../models/nativeMessaging/encryptedMessageResponses/cipherResponse"; -import { EncyptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse"; -import { FailureStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/failureStatusResponse"; -import { GenerateResponse } from "../models/nativeMessaging/encryptedMessageResponses/generateResponse"; -import { SuccessStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/successStatusResponse"; -import { UserStatusErrorResponse } from "../models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { CredentialCreatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-create-payload"; +import { CredentialRetrievePayload } from "../models/native-messaging/encrypted-message-payloads/credential-retrieve-payload"; +import { CredentialUpdatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-update-payload"; +import { PasswordGeneratePayload } from "../models/native-messaging/encrypted-message-payloads/password-generate-payload"; +import { AccountStatusResponse } from "../models/native-messaging/encrypted-message-responses/account-status-response"; +import { CipherResponse } from "../models/native-messaging/encrypted-message-responses/cipher-response"; +import { EncyptedMessageResponse } from "../models/native-messaging/encrypted-message-responses/encrypted-message-response"; +import { FailureStatusResponse } from "../models/native-messaging/encrypted-message-responses/failure-status-response"; +import { GenerateResponse } from "../models/native-messaging/encrypted-message-responses/generate-response"; +import { SuccessStatusResponse } from "../models/native-messaging/encrypted-message-responses/success-status-response"; +import { UserStatusErrorResponse } from "../models/native-messaging/encrypted-message-responses/user-status-error-response"; import { StateService } from "./state.service"; diff --git a/apps/desktop/src/services/nativeMessageHandler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts similarity index 92% rename from apps/desktop/src/services/nativeMessageHandler.service.ts rename to apps/desktop/src/services/native-message-handler.service.ts index e7c92082075..fd00bf33f8e 100644 --- a/apps/desktop/src/services/nativeMessageHandler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -12,14 +12,14 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/services/state.service"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { EncryptedMessage } from "../models/nativeMessaging/encryptedMessage"; -import { EncryptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponse"; -import { Message } from "../models/nativeMessaging/message"; -import { UnencryptedMessage } from "../models/nativeMessaging/unencryptedMessage"; -import { UnencryptedMessageResponse } from "../models/nativeMessaging/unencryptedMessageResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { EncryptedMessage } from "../models/native-messaging/encrypted-message"; +import { EncryptedMessageResponse } from "../models/native-messaging/encrypted-message-response"; +import { Message } from "../models/native-messaging/message"; +import { UnencryptedMessage } from "../models/native-messaging/unencrypted-message"; +import { UnencryptedMessageResponse } from "../models/native-messaging/unencrypted-message-response"; -import { EncryptedMessageHandlerService } from "./encryptedMessageHandlerService"; +import { EncryptedMessageHandlerService } from "./encrypted-message-handler.service"; const EncryptionAlgorithm = "sha1"; diff --git a/apps/desktop/src/services/nativeMessaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts similarity index 95% rename from apps/desktop/src/services/nativeMessaging.service.ts rename to apps/desktop/src/services/native-messaging.service.ts index 83e3705dc7d..0a2b443bfbf 100644 --- a/apps/desktop/src/services/nativeMessaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -15,11 +15,11 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { LegacyMessage } from "../models/nativeMessaging/legacyMessage"; -import { LegacyMessageWrapper } from "../models/nativeMessaging/legacyMessageWrapper"; -import { Message } from "../models/nativeMessaging/message"; +import { LegacyMessage } from "../models/native-messaging/legacy-message"; +import { LegacyMessageWrapper } from "../models/native-messaging/legacy-message-wrapper"; +import { Message } from "../models/native-messaging/message"; -import { NativeMessageHandlerService } from "./nativeMessageHandler.service"; +import { NativeMessageHandlerService } from "./native-message-handler.service"; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = "sha1"; diff --git a/apps/desktop/src/services/passwordReprompt.service.ts b/apps/desktop/src/services/password-reprompt.service.ts similarity index 100% rename from apps/desktop/src/services/passwordReprompt.service.ts rename to apps/desktop/src/services/password-reprompt.service.ts From 42638c544358d23a9993fa66ff75f6742aa91e50 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 24 Nov 2022 15:19:17 +0100 Subject: [PATCH 04/40] Deprecate the Api Service (#4099) --- libs/common/src/abstractions/api.service.ts | 5 +++++ libs/common/src/services/api.service.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index a8e2f45283c..d130f514134 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -163,6 +163,11 @@ import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key import { UserKeyResponse } from "../models/response/user-key.response"; import { SendAccessView } from "../models/view/send-access.view"; +/** + * @deprecated The `ApiService` class is deprecated and calls should be extracted into individual + * api services. The `send` method is still allowed to be used within api services. For background + * of this decision please read https://contributing.bitwarden.com/architecture/adr/refactor-api-service. + */ export abstract class ApiService { send: ( method: "GET" | "POST" | "PUT" | "DELETE", diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 6b660bb367e..d1bf0325b5c 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -172,6 +172,11 @@ import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key import { UserKeyResponse } from "../models/response/user-key.response"; import { SendAccessView } from "../models/view/send-access.view"; +/** + * @deprecated The `ApiService` class is deprecated and calls should be extracted into individual + * api services. The `send` method is still allowed to be used within api services. For background + * of this decision please read https://contributing.bitwarden.com/architecture/adr/refactor-api-service. + */ export class ApiService implements ApiServiceAbstraction { private device: DeviceType; private deviceType: string; From 448637243da6db3577732736483878d61bb01e3f Mon Sep 17 00:00:00 2001 From: "Patrick H. Lauke" Date: Thu, 24 Nov 2022 17:38:19 +0000 Subject: [PATCH 05/40] Fix inconsistent line height in cipher box footer (#3561) Closes #3559 --- apps/browser/src/popup/scss/misc.scss | 6 ++++++ apps/desktop/src/scss/misc.scss | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/apps/browser/src/popup/scss/misc.scss b/apps/browser/src/popup/scss/misc.scss index 2e832e92863..0a550915987 100644 --- a/apps/browser/src/popup/scss/misc.scss +++ b/apps/browser/src/popup/scss/misc.scss @@ -432,6 +432,12 @@ app-vault-view .box-footer { user-select: auto; } +/* tweak for inconsistent line heights in cipher view */ +.box-footer button, +.box-footer a { + line-height: 1; +} + // Workaround for slow performance on external monitors on Chrome + MacOS // See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64 @keyframes redraw { diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index 5818b194710..43da0d31bdf 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -534,3 +534,9 @@ img, app-vault-view .box-footer { user-select: auto; } + +/* tweak for inconsistent line heights in cipher view */ +.box-footer button, +.box-footer a { + line-height: 1; +} From b00c18a57e813314b2a8d6ca12a82190af53b311 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 01:18:05 +0100 Subject: [PATCH 06/40] Autosync the updated translations (#4116) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/cs/messages.json | 30 ++-- apps/browser/src/_locales/en_GB/messages.json | 8 +- apps/browser/src/_locales/sl/messages.json | 128 +++++++++--------- apps/browser/store/locales/en_GB/copy.resx | 2 +- 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index b3a8a004a29..655d9e3d58e 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -149,7 +149,7 @@ "message": "Změnit hlavní heslo" }, "fingerprintPhrase": { - "message": "Fráze otisku účtu", + "message": "Unikátní přístupová fráze", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { @@ -606,7 +606,7 @@ "message": "Zeptat se na aktualizaci existujícího přihlášení" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Dotázat se na aktualizaci hesla pro přihlášení, pokud je na webové stránce zjištěno použití jiného hesla." }, "notificationChangeDesc": { "message": "Chcete aktualizovat toto heslo v Bitwarden?" @@ -618,7 +618,7 @@ "message": "Zobrazit v kontextovém menu" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Použijte pravé tlačítko pro přístup k vytvoření hesla a odpovídajícímu přihlášení pro tuto stránku. " }, "defaultUriMatchDetection": { "message": "Výchozí zjišťování shody URI", @@ -1043,10 +1043,10 @@ "message": "Zobrazit rozeznatelný obrázek vedle každého přihlášení." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Zobrazovat počet uložených přihlašovacích údajů na stránce" }, "badgeCounterDesc": { - "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku." + "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku na ikoně rozšíření prohlížeče." }, "cardholderName": { "message": "Jméno držitele karty" @@ -1632,7 +1632,7 @@ "message": "Odstraněné heslo" }, "deletedSend": { - "message": "Smazaný Send", + "message": "Send odstraněn", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -1899,10 +1899,10 @@ "message": "Vypršel časový limit relace. Vraťte se prosím zpět a zkuste se znovu přihlásit." }, "exportingPersonalVaultTitle": { - "message": "Exporting individual vault" + "message": "Export mého trezoru" }, "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "message": "Budou exportovány pouze položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", "placeholders": { "email": { "content": "$1", @@ -1970,16 +1970,16 @@ "message": "API klíč" }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "Chyba Key Connector: ujistěte se, že je Key Connector k dispozici a funguje správně." }, "premiumSubcriptionRequired": { "message": "Vyžadováno prémiové předplatné" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "Organizace je deaktivována." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "K položkám v deaktivované organizaci nemáte přístup. Požádejte o pomoc vlastníka organizace." }, "cardBrandMir": { "message": "Mir" @@ -2000,19 +2000,19 @@ "message": "Klikněte zde" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "pro obnovení do přednastavených nastavení" }, "serverVersion": { "message": "Verze serveru" }, "selfHosted": { - "message": "Self-hosted" + "message": "Vlastní hosting" }, "thirdParty": { - "message": "Third-party" + "message": "Tretí strana" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Připojeno k serveru třetí strany $SERVERNAME$. Ověřte chyby připojením na oficiální server nebo nahlaste problém správci serveru.", "placeholders": { "servername": { "content": "$1", diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 293c6e0559c..810efccc664 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -59,7 +59,7 @@ "message": "My vault" }, "allVaults": { - "message": "All Vaults" + "message": "All vaults" }, "tools": { "message": "Tools" @@ -92,13 +92,13 @@ "message": "Auto-fill" }, "generatePasswordCopied": { - "message": "Generate Password (and Copy)" + "message": "Generate password (copied)" }, "copyElementIdentifier": { - "message": "Copy Custom Field Name" + "message": "Copy custom field name" }, "noMatchingLogins": { - "message": "No matching logins." + "message": "No matching logins" }, "unlockVaultMenu": { "message": "Unlock your vault" diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 746ec5540a9..35a1aa7a8fe 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -53,13 +53,13 @@ "message": "Zavihek" }, "vault": { - "message": "Sef" + "message": "Trezor" }, "myVault": { "message": "Moj trezor" }, "allVaults": { - "message": "All vaults" + "message": "Vsi trezorji" }, "tools": { "message": "Orodja" @@ -101,7 +101,7 @@ "message": "Nobenih ujemajočih prijav." }, "unlockVaultMenu": { - "message": "Unlock your vault" + "message": "Odkleni svoj trezor" }, "loginToVaultMenu": { "message": "Log in to your vault" @@ -131,10 +131,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send code" + "message": "Pošlji kodo" }, "codeSent": { - "message": "Code sent" + "message": "Koda poslana" }, "verificationCode": { "message": "Verifikacijska koda" @@ -242,10 +242,10 @@ "message": "Lowercase (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "Številke (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Posebni znaki (!@#$%^&*)" }, "numWords": { "message": "Število besed" @@ -864,7 +864,7 @@ "message": "To start the WebAuthn 2FA verification. Click the button below to open a new tab and follow the instructions provided in the new tab." }, "webAuthnNewTabOpen": { - "message": "Open new tab" + "message": "Odpri nov zavihek" }, "webAuthnAuthenticate": { "message": "Authenticate WebAuthn" @@ -879,10 +879,10 @@ "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "Možnosti dvostopenjske prijave" }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." + "message": "Ste izgubili dostop do vseh vaših ponudnikov dvostopenjske prijave? Uporabite svojo kodo za obnovitev in tako onemogočite dvostopenjsko prijavo v svoj račun." }, "recoveryCodeTitle": { "message": "Koda za obnovitev" @@ -927,7 +927,7 @@ "message": "Specify the base URL of your on-premises hosted Bitwarden installation." }, "customEnvironment": { - "message": "Custom environment" + "message": "Okolje po meri" }, "customEnvironmentFooter": { "message": "For advanced users. You can specify the base URL of each service independently." @@ -993,7 +993,7 @@ "message": "Generate and copy a new random password to the clipboard" }, "commandLockVaultDesc": { - "message": "Lock the vault" + "message": "Zakleni trezor" }, "privateModeWarning": { "message": "Private mode support is experimental and some features are limited." @@ -1002,10 +1002,10 @@ "message": "Custom fields" }, "copyValue": { - "message": "Copy value" + "message": "Kopiraj vrednost" }, "value": { - "message": "Value" + "message": "Vrednost" }, "newCustomField": { "message": "New custom field" @@ -1020,7 +1020,7 @@ "message": "Skrito" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "Logična vrednost" }, "cfTypeLinked": { "message": "Linked", @@ -1133,7 +1133,7 @@ "message": "Priimek" }, "fullName": { - "message": "Full name" + "message": "Polno ime" }, "identityName": { "message": "Ime identitete" @@ -1190,22 +1190,22 @@ "message": "Prijave" }, "typeSecureNote": { - "message": "Secure note" + "message": "Varni zapisek" }, "typeCard": { - "message": "Card" + "message": "Kartica" }, "typeIdentity": { - "message": "Identity" + "message": "Identiteta" }, "passwordHistory": { - "message": "Password history" + "message": "Zgodovina gesel" }, "back": { - "message": "Back" + "message": "Nazaj" }, "collections": { - "message": "Collections" + "message": "Zbirke" }, "favorites": { "message": "Priljubljeno" @@ -1226,7 +1226,7 @@ "message": "Prijave" }, "secureNotes": { - "message": "Secure notes" + "message": "Varni zapiski" }, "clear": { "message": "Počisti", @@ -1285,15 +1285,15 @@ "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { - "message": "Current URI", + "message": "Trenutni URI", "description": "The URI of one of the current open tabs in the browser." }, "organization": { - "message": "Organization", + "message": "Organizacija", "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Types" + "message": "Tipi" }, "allItems": { "message": "All items" @@ -1302,13 +1302,13 @@ "message": "There are no passwords to list." }, "remove": { - "message": "Remove" + "message": "Odstrani" }, "default": { - "message": "Default" + "message": "Privzeto" }, "dateUpdated": { - "message": "Updated", + "message": "Posodobljeno", "description": "ex. Date this item was updated" }, "dateCreated": { @@ -1329,25 +1329,25 @@ "message": "There are no collections to list." }, "ownership": { - "message": "Ownership" + "message": "Lastništvo" }, "whoOwnsThisItem": { "message": "Who owns this item?" }, "strong": { - "message": "Strong", + "message": "Močno", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Good", + "message": "Dobro", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "Weak", + "message": "Šibko", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Weak master password" + "message": "Šibko glavno geslo" }, "weakMasterPasswordDesc": { "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" @@ -1357,19 +1357,19 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Odkleni s PIN kodo" }, "setYourPinCode": { "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." }, "pinRequired": { - "message": "PIN code is required." + "message": "Potrebna je PIN koda." }, "invalidPin": { - "message": "Invalid PIN code." + "message": "Nepravilna PIN koda." }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Prijava z biometriko" }, "awaitDesktop": { "message": "Awaiting confirmation from desktop" @@ -1387,7 +1387,7 @@ "message": "Clone item" }, "clone": { - "message": "Clone" + "message": "Kloniraj" }, "passwordGeneratorPolicyInEffect": { "message": "One or more organization policies are affecting your generator settings." @@ -1396,15 +1396,15 @@ "message": "Vault timeout action" }, "lock": { - "message": "Lock", + "message": "Zakleni", "description": "Verb form: to make secure or inaccesible by" }, "trash": { - "message": "Trash", + "message": "Koš", "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search trash" + "message": "Preišči koš" }, "permanentlyDeleteItem": { "message": "Permanently delete item" @@ -1440,7 +1440,7 @@ "message": "Item auto-filled " }, "setMasterPassword": { - "message": "Set master password" + "message": "Nastavi glavno geslo" }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" @@ -1494,13 +1494,13 @@ "message": "Terms of Service" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "Pravilnik o zasebnosti" }, "hintEqualsPassword": { "message": "Your password hint cannot be the same as your password." }, "ok": { - "message": "Ok" + "message": "V redu" }, "desktopSyncVerificationTitle": { "message": "Desktop sync verification" @@ -1584,7 +1584,7 @@ } }, "send": { - "message": "Send", + "message": "Pošlji", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { @@ -1596,10 +1596,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { - "message": "Text" + "message": "Besedilo" }, "sendTypeFile": { - "message": "File" + "message": "Datoteka" }, "allSends": { "message": "All Sends", @@ -1610,7 +1610,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "expired": { - "message": "Expired" + "message": "Poteklo" }, "pendingDeletion": { "message": "Pending deletion" @@ -1626,7 +1626,7 @@ "message": "Remove Password" }, "delete": { - "message": "Delete" + "message": "Izbriši" }, "removedPassword": { "message": "Password removed" @@ -1636,11 +1636,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Send link", + "message": "Pošlji povezavo", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { - "message": "Disabled" + "message": "Onemogočeno" }, "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" @@ -1683,10 +1683,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { - "message": "1 day" + "message": "1 dan" }, "days": { - "message": "$DAYS$ days", + "message": "$DAYS$ dni", "placeholders": { "days": { "content": "$1", @@ -1835,10 +1835,10 @@ "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." }, "hours": { - "message": "Hours" + "message": "Ur" }, "minutes": { - "message": "Minutes" + "message": "Minut" }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", @@ -1911,7 +1911,7 @@ } }, "error": { - "message": "Error" + "message": "Napaka" }, "regenerateUsername": { "message": "Regenerate username" @@ -1936,13 +1936,13 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Naključno" }, "randomWord": { - "message": "Random word" + "message": "Naključna beseda" }, "websiteName": { - "message": "Website name" + "message": "Ime spletne strani" }, "whatWouldYouLikeToGenerate": { "message": "What would you like to generate?" @@ -1997,13 +1997,13 @@ "message": "Settings have been edited" }, "environmentEditedClick": { - "message": "Click here" + "message": "Kliknite tukaj" }, "environmentEditedReset": { "message": "to reset to pre-configured settings" }, "serverVersion": { - "message": "Server version" + "message": "Verzija strežnika" }, "selfHosted": { "message": "Self-hosted" @@ -2036,12 +2036,12 @@ "message": "Logging in as" }, "notYou": { - "message": "Not you?" + "message": "Niste vi?" }, "newAroundHere": { "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "Zapomni si e-pošto" } } diff --git a/apps/browser/store/locales/en_GB/copy.resx b/apps/browser/store/locales/en_GB/copy.resx index cfb4c5df101..191198691d4 100644 --- a/apps/browser/store/locales/en_GB/copy.resx +++ b/apps/browser/store/locales/en_GB/copy.resx @@ -139,7 +139,7 @@ Bitwarden offers Teams and Enterprise plans for companies so you can securely sh Why Choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. Built-in Password Generator Generate strong, unique, and random passwords based on security requirements for every website you frequent. From 4181bdcbc971afa1311ed56336c8844fab69c963 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 01:19:13 +0100 Subject: [PATCH 07/40] Autosync the updated translations (#4117) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/en_GB/messages.json | 2 +- apps/desktop/src/locales/vi/messages.json | 86 ++++++++++---------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 12a4f27de17..084e90d0048 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -45,7 +45,7 @@ "message": "Share" }, "moveToOrganization": { - "message": "Move to Organisation" + "message": "Move to organisation" }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 28075bd45d5..0d7244c122d 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -48,7 +48,7 @@ "message": "Chuyển tới Tổ chức" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ đã di chuyển tới $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -805,7 +805,7 @@ "message": "Đồng bộ thất bại" }, "yourVaultIsLocked": { - "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chinhs của bạn để mở." + "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chính của bạn để mở." }, "unlock": { "message": "Mở khóa" @@ -1213,7 +1213,7 @@ "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Tên miền", "description": "Domain name. Ex. website.com" }, "host": { @@ -1662,7 +1662,7 @@ "message": "Văn bản" }, "deletionDate": { - "message": "Deletion date" + "message": "Ngày xóa" }, "deletionDateDesc": { "message": "Send sẽ được xóa vĩnh viễn vào ngày và giờ được chỉ định.", @@ -1703,7 +1703,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send link", + "message": "Gửi liên kết", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1752,14 +1752,14 @@ "message": "1 ngày" }, "custom": { - "message": "Custom" + "message": "Tùy chỉnh" }, "deleteSendConfirmation": { "message": "Bạn có chắc chắn muốn xóa Send này?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "Copy Send link to clipboard", + "message": "Sao chép liên kết tới Khay nhớ tạm", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { @@ -1777,58 +1777,58 @@ "message": "Sao chép liên kết" }, "disabled": { - "message": "Disabled" + "message": "Đã tắt" }, "maxAccessCountReached": { - "message": "Max access count reached" + "message": "Đã đạt đến số lượng truy cập tối đa" }, "expired": { - "message": "Expired" + "message": "Đã hết hạn" }, "pendingDeletion": { "message": "Đang chờ xóa" }, "webAuthnAuthenticate": { - "message": "Authenticate WebAuthn" + "message": "Xác thực WebAuthn" }, "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Ẩn địa chỉ email của tôi khỏi người nhận." }, "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." + "message": "Có một hoặc vài chính sách của tổ chức đang làm ảnh hưởng đến cài đặt tạo mật khẩu của bạn." }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "Yêu cầu xác nhận danh tính qua Email" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature." + "message": "Bạn phải xác minh email của mình để sử dụng tính năng này." }, "passwordPrompt": { - "message": "Master password re-prompt" + "message": "Nhắc lại mật khẩu chính" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "Xác nhận mật khẩu chính" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "Hành động này được bảo vệ. Để tiếp tục, vui lòng nhập lại mật khẩu chính của bạn để xác minh danh tính của bạn." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Mật khẩu chính đã được cập nhật" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Cập nhật Mật khẩu chính" }, "updateMasterPasswordWarning": { - "message": "Your master password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Mật khẩu chính của bạn gần đây đã được thay đổi bởi một quản trị viên trong tổ chức của bạn. Để truy cập Kho, bạn phải cập nhật nó ngay bây giờ. Tiếp tục sẽ đăng xuất bạn khỏi phiên hiện tại của bạn, yêu cầu bạn đăng nhập lại. Các phiên hoạt động trên các thiết bị khác có thể tiếp tục hoạt động trong tối đa một giờ." }, "hours": { - "message": "Hours" + "message": "Giờ" }, "minutes": { - "message": "Minutes" + "message": "Phút" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Chính sách tổ chức của bạn đang ảnh hưởng đến thời gian chờ Kho của bạn. Thời gian chờ kho tối đa được phép là $HOURS$ giờ và $MINUTES$ phút", "placeholders": { "hours": { "content": "$1", @@ -1841,10 +1841,10 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Thời gian chờ Kho của bạn vượt quá các hạn chế do tổ chức của bạn đặt ra." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Đăng ký tự động" }, "resetPasswordAutoEnrollInviteWarning": { "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." @@ -1856,13 +1856,13 @@ "message": "One or more organization policies prevents you from exporting your personal vault." }, "addAccount": { - "message": "Add account" + "message": "Thêm tài khoản" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Xóa mật khẩu chính" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Đã xóa mật khẩu chính" }, "convertOrganizationEncryptionDesc": { "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", @@ -1874,7 +1874,7 @@ } }, "leaveOrganization": { - "message": "Leave organization" + "message": "Rời khỏi tổ chức" }, "leaveOrganizationConfirmation": { "message": "Are you sure you want to leave this organization?" @@ -1892,7 +1892,7 @@ "message": "No more than 5 accounts may be logged in at the same time." }, "accountPreferences": { - "message": "Preferences" + "message": "Tuỳ chỉnh" }, "appPreferences": { "message": "App settings (all accounts)" @@ -1910,10 +1910,10 @@ } }, "switchAccount": { - "message": "Switch account" + "message": "Chuyển tài khoản" }, "options": { - "message": "Options" + "message": "Tùy chọn" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1937,19 +1937,19 @@ "message": "Đã mở khóa" }, "generator": { - "message": "Generator" + "message": "Tạo" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Bạn muốn tạo gì?" }, "passwordType": { - "message": "Password type" + "message": "Loại mật khẩu" }, "regenerateUsername": { "message": "Regenerate username" }, "generateUsername": { - "message": "Generate username" + "message": "Tạo tên tài khoản" }, "usernameType": { "message": "Username type" @@ -1968,22 +1968,22 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Ngẫu nhiên" }, "randomWord": { - "message": "Random word" + "message": "Từ ngẫu nhiên" }, "websiteName": { - "message": "Website name" + "message": "Tên website" }, "service": { - "message": "Service" + "message": "Dịch vụ" }, "allVaults": { - "message": "All vaults" + "message": "Tất cả kho" }, "searchOrganization": { - "message": "Search organization" + "message": "Tìm tổ chức" }, "searchMyVault": { "message": "Search my vault" From 8767cad4f9e81cf51050e915c367d6824464f5ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 01:23:38 +0100 Subject: [PATCH 08/40] Autosync the updated translations (#4118) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/da/messages.json | 32 ++-- apps/web/src/locales/en_GB/messages.json | 226 +++++++++++------------ apps/web/src/locales/ru/messages.json | 8 +- apps/web/src/locales/sv/messages.json | 26 +-- apps/web/src/locales/vi/messages.json | 32 ++-- apps/web/src/locales/zh_CN/messages.json | 56 +++--- 6 files changed, 190 insertions(+), 190 deletions(-) diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index bd53b87cb75..94a796b1fb3 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -3668,7 +3668,7 @@ "message": "Skjul adgangskoder" }, "countryPostalCodeRequiredDesc": { - "message": "Vi benytter udelukkende disse oplysninger til beregning af moms og finansiel rapportering." + "message": "Disse oplysninger benyttes udelukkende til momsberegning og finansiel rapportering." }, "includeVAT": { "message": "Inkludér momsoplysninger (valgfrit)" @@ -3731,31 +3731,31 @@ "message": "Tilknyt SSO" }, "singleOrg": { - "message": "Enkel organisation" + "message": "Enkeltorganisation" }, "singleOrgDesc": { - "message": "Begræns medlemmer fra at blive medlem af andre organisationer." + "message": "Begræns medlemmer i at tilmelde sig andre organisationer." }, "singleOrgBlockCreateMessage": { - "message": "Den nuværende organisationspolitik tillader dig ikke at deltage i mere end én organisation. Kontakt organisationsadministratorerne eller benyt en anden Bitwarden-konto under tilmelding." + "message": "Den nuværende organisationspolitik tillader dig ikke at deltage i mere end én organisation. Kontakt organisationens admins eller benyt en anden Bitwarden-konto under tilmelding." }, "singleOrgPolicyWarning": { - "message": "Organisationsmedlemmer, som ikke er ejere eller admins og allerede medlem af en anden organisation, fjernes fra organisationen." + "message": "Organisationsmedlemmer, undtagen ejere eller admins, som allerede er medlem af en anden organisation, fjernes fra organisationen." }, "requireSso": { "message": "Kræv single sign-on godkendelse" }, "requireSsoPolicyDesc": { - "message": "Kræv at medlemmer logger ind vha. Virksomheds single sign-on metoden." + "message": "Kræv, at medlemmer logger ind vha. Virksomheds single sign-on metoden." }, "prerequisite": { "message": "Forudsætning" }, "requireSsoPolicyReq": { - "message": "Enkel organisation virksomhedspolitikken skal være aktiveret, før denne politik aktiveres." + "message": "Enkeltorganisations Virksomhedspolitik skal være aktiveret, før denne politik aktiveres." }, "requireSsoPolicyReqError": { - "message": "Single organization-politik er ikke opsat." + "message": "Enkeltorganisationspolitik er ikke opsat." }, "requireSsoExemption": { "message": "Denne politik håndhæves ikke for organisationsejere og -admins." @@ -3791,7 +3791,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "Er du sikker på, at du vil slette denne Send?", + "message": "Sikker på, at denne Send skal slettes?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "whatTypeOfSend": { @@ -3802,7 +3802,7 @@ "message": "Sletningsdato" }, "deletionDateDesc": { - "message": "Send slettes permanent på den angivne dato og tidspunkt.", + "message": "Denne Send slettes permanent på den angivne dato og tidspunkt.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -3837,7 +3837,7 @@ "message": "Tilbagekaldt" }, "sendLink": { - "message": "Send link", + "message": "Send-link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLink": { @@ -3851,7 +3851,7 @@ "message": "Adgangskode fjernet" }, "removePasswordConfirmation": { - "message": "Er du sikker på, at du vil fjerne adgangskoden?" + "message": "Sikker på, at adgangskoden skal fjernes?" }, "hideEmail": { "message": "Skjul min e-mailadresse for modtagere." @@ -3908,7 +3908,7 @@ "message": "Nødadgang" }, "emergencyAccessDesc": { - "message": "Tildel og håndtér nødadgang for betroede kontakter. Betroede kontakter kan anmode om adgang til enten at se eller overtage din konto i nødstilfælde. Besøg vores hjælpeside for mere information og detaljer om, hvordan nul-videndeling fungerer." + "message": "Tildel og håndtér nødadgang for betroede kontakter. Betroede kontakter kan anmode om adgang til enten at se eller overtage din konto i nødstilfælde. Besøg vores hjælpeside for mere information og detaljer om, hvordan nul-vidensdeling fungerer." }, "emergencyAccessOwnerWarning": { "message": "Du er ejer af en eller flere organisationer. Tildeler du overtagelsesrettighed til en nødkontakt, kan vedkommende bruge alle dine tilladelser som ejer efter en overtagelse." @@ -3935,7 +3935,7 @@ "message": "Redigér nødkontakt" }, "inviteEmergencyContactDesc": { - "message": "Invitér en ny nødkontakt ved at angive e-mailadressen på vedkommendes Bitwarden-konto nedenfor. Har vedkommende ikke allerede en Bitwarden-konto, anmoders vedkommende om at oprette én." + "message": "Invitér en ny nødkontakt ved at angive e-mailadressen på vedkommendes Bitwarden-konto nedenfor. Har vedkommende endnu ikke en Bitwarden-konto, anmoders vedkommende om at oprette en." }, "emergencyAccessRecoveryInitiated": { "message": "Nødadgang igangsat" @@ -3944,7 +3944,7 @@ "message": "Nødadgang godkendt" }, "viewDesc": { - "message": "Kan se alle elementer i din egen boks." + "message": "Kan se alle emner i din egen boks." }, "takeover": { "message": "Overtagelse" @@ -4414,7 +4414,7 @@ "message": "Kræv at nye brugere automatisk indrulleres" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "Denne organisation har en virksomhedspolitik, der auto-opsætter dig til adgangskodenulstilling. Opsætningen giver organisationsadministratorer mulighed for at ændre din hovedadgangskode." + "message": "Denne organisation har en Virksomhedspolitik, der auto-tilmelder dig til adgangskodenulstilling. Tilmeldingen giver organisationsadministratorer mulighed for at ændre din hovedadgangskode." }, "resetPasswordOrgKeysError": { "message": "Organisationsnøgler svar er null" diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index aea05dbf573..32064e91bf3 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -191,7 +191,7 @@ "description": "Domain name. Example: website.com" }, "domainName": { - "message": "Domain Name", + "message": "Domain name", "description": "Domain name. Example: website.com" }, "host": { @@ -315,7 +315,7 @@ "message": "Identities" }, "typeSecureNotePlural": { - "message": "Secure Notes" + "message": "Secure notes" }, "folders": { "message": "Folders" @@ -333,7 +333,7 @@ "message": "Last name" }, "fullName": { - "message": "Full Name" + "message": "Full name" }, "address1": { "message": "Address 1" @@ -372,7 +372,7 @@ "message": "Edit item" }, "viewItem": { - "message": "View Item" + "message": "View item" }, "ex": { "message": "e.g.", @@ -385,7 +385,7 @@ "message": "Share" }, "moveToOrganization": { - "message": "Move to Organisation" + "message": "Move to organisation" }, "valueCopied": { "message": "$VALUE$ copied", @@ -428,7 +428,7 @@ "message": "My vault" }, "allVaults": { - "message": "All Vaults" + "message": "All vaults" }, "vault": { "message": "Vault" @@ -437,10 +437,10 @@ "message": "Vaults" }, "vaultItems": { - "message": "Vault Items" + "message": "Vault items" }, "moveSelectedToOrg": { - "message": "Move Selected to Organisation" + "message": "Move selected to organisation" }, "deleteSelected": { "message": "Delete selected" @@ -467,7 +467,7 @@ "message": "Are you sure you want to delete this attachment?" }, "attachmentSaved": { - "message": "The attachment has been saved." + "message": "Attachment saved" }, "file": { "message": "File" @@ -482,10 +482,10 @@ "message": "You cannot use this feature until you update your encryption key." }, "addedItem": { - "message": "Added item" + "message": "Item added" }, "editedItem": { - "message": "Edited item" + "message": "Item saved" }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", @@ -528,22 +528,22 @@ "message": "Items sent to bin" }, "movedItems": { - "message": "Moved items" + "message": "Items moved" }, "overwritePasswordConfirmation": { "message": "Are you sure you want to overwrite the current password?" }, "editedFolder": { - "message": "Edited folder" + "message": "Folder saved" }, "addedFolder": { - "message": "Added folder" + "message": "Folder added" }, "deleteFolderConfirmation": { "message": "Are you sure you want to delete this folder?" }, "deletedFolder": { - "message": "Deleted folder" + "message": "Folder deleted" }, "loggedOut": { "message": "Logged out" @@ -573,7 +573,7 @@ "message": "Log in with device" }, "loginWithDeviceEnabledInfo": { - "message": "Log in with device must be enabled in the settings of the Bitwarden mobile app. Need another option?" + "message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?" }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -585,7 +585,7 @@ "message": "New around here?" }, "startTrial": { - "message": "Start Trial" + "message": "Start trial" }, "logIn": { "message": "Log in" @@ -772,7 +772,7 @@ "message": "Login unavailable" }, "noTwoStepProviders": { - "message": "This account has two-step login enabled. However, none of the configured two-step providers are supported by this web browser." + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this web browser." }, "noTwoStepProviders2": { "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." @@ -781,7 +781,7 @@ "message": "Two-step login options" }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to disable all two-factor providers from your account." + "message": "Lost access to all of your two-step login providers? Use your recovery code to turn off all two-step login providers from your account." }, "recoveryCodeTitle": { "message": "Recovery code" @@ -808,7 +808,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "u2fDesc": { - "message": "Use any FIDO U2F enabled security key to access your account." + "message": "Use any FIDO U2F compatible security key to access your account." }, "u2fTitle": { "message": "FIDO U2F security key" @@ -817,7 +817,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Use any WebAuthn enabled security key to access your account." + "message": "Use any WebAuthn compatible security key to access your account." }, "webAuthnMigrated": { "message": "(Migrated from FIDO)" @@ -891,7 +891,7 @@ "message": "Warning" }, "confirmVaultExport": { - "message": "Confirm Vault Export" + "message": "Confirm vault export" }, "exportWarningDesc": { "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it." @@ -918,16 +918,16 @@ "message": "This password will be used to export and import this file" }, "confirmMasterPassword": { - "message": "Confirm Master Password" + "message": "Confirm master password" }, "confirmFormat": { - "message": "Confirm Format" + "message": "Confirm format" }, "filePassword": { - "message": "File Password" + "message": "File password" }, "confirmFilePassword": { - "message": "Confirm File Password" + "message": "Confirm file password" }, "accountBackupOptionDescription": { "message": "Use your account encryption key to encrypt the export and restrict import to only the current Bitwarden account." @@ -936,25 +936,25 @@ "message": "Set a password to encrypt the export and import it to any Bitwarden account using the password for decryption." }, "fileTypeHeading": { - "message": "File Type" + "message": "File type" }, "accountBackup": { - "message": "Account Backup" + "message": "Account backup" }, "passwordProtected": { - "message": "Password Protected" + "message": "Password protected" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { - "message": "“File password” and “Confirm File Password“ do not match." + "message": "“File password” and “Confirm file password“ do not match." }, "confirmVaultImport": { - "message": "Confirm Vault Import" + "message": "Confirm vault import" }, "confirmVaultImportDesc": { "message": "This file is password-protected. Please enter the file password to import data." }, "exportSuccess": { - "message": "Your vault data has been exported." + "message": "Vault data exported" }, "passwordGenerator": { "message": "Password generator" @@ -990,7 +990,7 @@ "message": "Numbers (0-9)" }, "specialCharacters": { - "message": "Special Characters (!@#$%^&*)" + "message": "Special characters (!@#$%^&*)" }, "numWords": { "message": "Number of words" @@ -1016,13 +1016,13 @@ "description": "To clear something out. Example: To clear browser history." }, "accountUpdated": { - "message": "Account updated" + "message": "Account saved" }, "changeEmail": { "message": "Change email" }, "changeEmailTwoFactorWarning": { - "message": "Proceeding will change your account email address. It will not change the email address used for two-factor authentication. You can change this email address in the Two-Step Login settings." + "message": "Proceeding will change your account email address. It will not change the email address used for two-step login authentication. You can change this email address in the two-step login settings." }, "newEmail": { "message": "New email" @@ -1043,7 +1043,7 @@ "message": "Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." }, "emailChanged": { - "message": "Email changed" + "message": "Email saved" }, "logBackIn": { "message": "Please log back in." @@ -1112,7 +1112,7 @@ "message": "Concerned your account is logged in on another device? Proceed below to deauthorise all computers or devices that you have previously used. This security step is recommended if you previously used a public computer or accidentally saved your password on a device that isn't yours. This step will also clear all previously remembered two-step login sessions." }, "deauthorizeSessionsWarning": { - "message": "Proceeding will also log you out of your current session, requiring you to log back in. You will also be prompted for two-step login again, if enabled. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Proceeding will also log you out of your current session, requiring you to log back in. You will also be prompted for two-step login again, if set up. Active sessions on other devices may continue to remain active for up to one hour." }, "sessionsDeauthorized": { "message": "All sessions deauthorised" @@ -1124,7 +1124,7 @@ "message": "Purged organisation vault." }, "vaultAccessedByProvider": { - "message": "Vault accessed by provider." + "message": "Vault accessed by Provider." }, "purgeVaultDesc": { "message": "Proceed below to delete all items and folders in your vault. Items that belong to an organisation that you share with will not be deleted." @@ -1136,7 +1136,7 @@ "message": "Purging your vault is permanent. It cannot be undone." }, "vaultPurged": { - "message": "Your vault has been purged." + "message": "Vault purged." }, "deleteAccount": { "message": "Delete account" @@ -1163,13 +1163,13 @@ "message": "Import data" }, "importError": { - "message": "Import Error" + "message": "Import error" }, "importErrorDesc": { "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." }, "importSuccess": { - "message": "Data has been successfully imported into your vault." + "message": "Data successfully imported" }, "importWarning": { "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organisation. Do you want to proceed?", @@ -1224,7 +1224,7 @@ "message": "Customize your web vault experience." }, "preferencesUpdated": { - "message": "Preferences updated" + "message": "Preferences saved" }, "language": { "message": "Language" @@ -1285,7 +1285,7 @@ } }, "domainsUpdated": { - "message": "Domains updated" + "message": "Domains saved" }, "twoStepLogin": { "message": "Two-step login" @@ -1310,7 +1310,7 @@ "message": "If you have setup SSO or plan to, Two-step Login may already be enforced through your Identity Provider." }, "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." + "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." }, "viewRecoveryCode": { "message": "View recovery code" @@ -1320,13 +1320,13 @@ "description": "Two-step login providers such as YubiKey, Duo, Authenticator apps, Email, etc." }, "enable": { - "message": "Enable" + "message": "Turn on" }, "enabled": { - "message": "Enabled" + "message": "Turned on" }, "restoreAccess": { - "message": "Restore Access" + "message": "Restore access" }, "premium": { "message": "Premium", @@ -1339,25 +1339,25 @@ "message": "Premium required" }, "premiumRequiredDesc": { - "message": "A premium membership is required to use this feature." + "message": "A Premium membership is required to use this feature." }, "youHavePremiumAccess": { - "message": "You have premium access" + "message": "You have Premium access" }, "alreadyPremiumFromOrg": { - "message": "You already have access to premium features because of an organisation of which you are a member." + "message": "You already have access to Premium features because of an organisation you are a member of." }, "manage": { "message": "Manage" }, "disable": { - "message": "Disable" + "message": "Turn off" }, "revokeAccess": { - "message": "Revoke Access" + "message": "Revoke access" }, "twoStepLoginProviderEnabled": { - "message": "This two-step login provider is enabled on your account." + "message": "This two-step login provider is active on your account." }, "twoStepLoginAuthDesc": { "message": "Enter your master password to modify two-step login settings." @@ -1396,10 +1396,10 @@ "message": "In case you need to add it to another device, below is the QR code (or key) required by your authenticator app." }, "twoStepDisableDesc": { - "message": "Are you sure you want to disable this two-step login provider?" + "message": "Are you sure you want to turn off this two-step login provider?" }, "twoStepDisabled": { - "message": "Two-step login provider disabled." + "message": "Two-step login provider turned off." }, "twoFactorYubikeyAdd": { "message": "Add a new YubiKey to your account" @@ -1417,7 +1417,7 @@ "message": "Save the form." }, "twoFactorYubikeyWarning": { - "message": "Due to platform limitations, YubiKeys cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when YubiKeys cannot be used. Supported platforms:" + "message": "Due to platform limitations, YubiKeys cannot be used on all Bitwarden applications. You should set up another two-step login provider so that you can access your account when YubiKeys cannot be used. Supported platforms:" }, "twoFactorYubikeySupportUsb": { "message": "Web vault, desktop application, CLI, and all browser extensions on a device with a USB port that can accept your YubiKey." @@ -1465,7 +1465,7 @@ "message": "YubiKeys updated" }, "disableAllKeys": { - "message": "Disable all keys" + "message": "Deactivate all keys" }, "twoFactorDuoDesc": { "message": "Enter the Bitwarden application information from your Duo Admin panel." @@ -1519,31 +1519,31 @@ "message": "Save the form." }, "twoFactorU2fWarning": { - "message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:" + "message": "Due to platform limitations, FIDO U2F cannot be used on all Bitwarden applications. You should set up another two-step login provider so that you can access your account when FIDO U2F cannot be used. Supported platforms:" }, "twoFactorU2fSupportWeb": { - "message": "Web vault and browser extensions on a desktop/laptop with a U2F enabled browser (Chrome, Opera, Vivaldi, or Firefox with FIDO U2F enabled)." + "message": "Web vault and browser extensions on a desktop/laptop with a U2F supported browser (Chrome, Opera, Vivaldi, or Firefox with FIDO U2F turned on)." }, "twoFactorU2fWaiting": { "message": "Waiting for you to touch the button on your security key" }, "twoFactorU2fClickSave": { - "message": "Click the \"Save\" button below to enable this security key for two-step login." + "message": "Use the \"Save\" button below to activate this security key for two-step login." }, "twoFactorU2fProblemReadingTryAgain": { "message": "There was a problem reading the security key. Try again." }, "twoFactorWebAuthnWarning": { - "message": "Due to platform limitations, WebAuthn cannot be used on all Bitwarden applications. You should enable another two-step login provider so that you can access your account when WebAuthn cannot be used. Supported platforms:" + "message": "Due to platform limitations, WebAuthn cannot be used on all Bitwarden applications. You should set up another two-step login provider so that you can access your account when WebAuthn cannot be used. Supported platforms:" }, "twoFactorWebAuthnSupportWeb": { - "message": "Web vault and browser extensions on a desktop/laptop with a WebAuthn enabled browser (Chrome, Opera, Vivaldi, or Firefox with FIDO U2F enabled)." + "message": "Web vault and browser extensions on a desktop/laptop with a WebAuthn supported browser (Chrome, Opera, Vivaldi, or Firefox with FIDO U2F turned on)." }, "twoFactorRecoveryYourCode": { "message": "Your Bitwarden two-step login recovery code" }, "twoFactorRecoveryNoCode": { - "message": "You have not enabled any two-step login providers yet. After you have enabled a two-step login provider you can check back here for your recovery code." + "message": "You have not set up any two-step login providers yet. After you have set up a two-step login provider you can check back here for your recovery code." }, "printCode": { "message": "Print code", @@ -1557,14 +1557,14 @@ "description": "Vault health reports can be used to evaluate the security of your Bitwarden individual or organization vault." }, "orgsReportsDesc": { - "message": "Identify and close security gaps in your organization's accounts by clicking the reports below.", + "message": "Identify and close security gaps in your organisation's accounts by clicking the reports below.", "description": "Vault health reports can be used to evaluate the security of your Bitwarden individual or organization Vault." }, "unsecuredWebsitesReport": { - "message": "Unsecure Websites" + "message": "Unsecure websites" }, "unsecuredWebsitesReportDesc": { - "message": "URLs that start with http:// don’t use the best available encryption. Change the Login URIs for these accounts to https:// for safer browsing." + "message": "URLs that start with http:// don’t use the best available encryption. Change the login URIs for these accounts to https:// for safer browsing." }, "unsecuredWebsitesFound": { "message": "Unsecured websites found" @@ -1582,13 +1582,13 @@ "message": "No items in your vault have unsecured URIs." }, "inactive2faReport": { - "message": "Inactive Two-step Login" + "message": "Inactive two-step login" }, "inactive2faReportDesc": { - "message": "Two-step Login adds a layer of protection to your accounts. Turn on Two-Step Login using Bitwarden Authenticator for these accounts or use an alternative method." + "message": "Two-step login adds a layer of protection to your accounts. Set up two-step login using Bitwarden authenticator for these accounts or use an alternative method." }, "inactive2faFound": { - "message": "Logins without 2FA found" + "message": "Logins without two-step login found" }, "inactive2faFoundDesc": { "message": "We found $COUNT$ website(s) in your vault that may not be configured with two-step login (according to 2fa.directory). To further protect these accounts, you should set up two-step login.", @@ -1600,13 +1600,13 @@ } }, "noInactive2fa": { - "message": "No websites were found in your vault with a missing two-factor authentication configuration." + "message": "No websites were found in your vault with a missing two-step login configuration." }, "instructions": { "message": "Instructions" }, "exposedPasswordsReport": { - "message": "Exposed Passwords" + "message": "Exposed passwords" }, "exposedPasswordsReportDesc": { "message": "Passwords exposed in a data breach are easy targets for attackers. Change these passwords to prevent potential break-ins." @@ -1639,10 +1639,10 @@ } }, "weakPasswordsReport": { - "message": "Weak Passwords" + "message": "Weak passwords" }, "weakPasswordsReportDesc": { - "message": "Weak passwords can be easily guessed by attackers. Change these passwords to strong ones using the Password Generator." + "message": "Weak passwords can be easily guessed by attackers. Change these passwords to strong ones using the password generator." }, "weakPasswordsFound": { "message": "Weak passwords found" @@ -1660,7 +1660,7 @@ "message": "No items in your vault have weak passwords." }, "reusedPasswordsReport": { - "message": "Reused Passwords" + "message": "Reused passwords" }, "reusedPasswordsReportDesc": { "message": "Reusing passwords makes it easier for attackers to break into multiple accounts. Change these passwords so that each is unique." @@ -1690,7 +1690,7 @@ } }, "dataBreachReport": { - "message": "Data Breach" + "message": "Data breach" }, "breachDesc": { "message": "Breached accounts can expose your personal information. Secure breached accounts by enabling 2FA or creating a stronger password." @@ -1752,10 +1752,10 @@ "message": "Billing" }, "billingPlanLabel": { - "message": "Billing Plan" + "message": "Billing plan" }, "paymentType": { - "message": "Payment Type" + "message": "Payment type" }, "accountCredit": { "message": "Account credit", @@ -1787,10 +1787,10 @@ "description": "Another way of saying \"Get a Premium membership\"" }, "premiumUpdated": { - "message": "You've upgraded to premium." + "message": "You've upgraded to Premium." }, "premiumUpgradeUnlockFeatures": { - "message": "Upgrade your account to a premium membership and unlock some great additional features." + "message": "Upgrade your account to a Premium membership and unlock some great additional features." }, "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." @@ -1799,7 +1799,7 @@ "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." }, "premiumSignUpEmergency": { - "message": "Emergency Access" + "message": "Emergency access" }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." @@ -1811,7 +1811,7 @@ "message": "Priority customer support." }, "premiumSignUpFuture": { - "message": "All future premium features. More coming soon!" + "message": "All future Premium features. More coming soon!" }, "premiumPrice": { "message": "All for just $PRICE$ /year!", @@ -1829,7 +1829,7 @@ "message": "Premium access" }, "premiumAccessDesc": { - "message": "You can add premium access to all members of your organisation for $PRICE$ /$INTERVAL$.", + "message": "You can add Premium access to all members of your organisation for $PRICE$ /$INTERVAL$.", "placeholders": { "price": { "content": "$1", @@ -1902,7 +1902,7 @@ "message": "Payment information" }, "billingInformation": { - "message": "Billing Information" + "message": "Billing information" }, "billingTrialSubLabel": { "message": "Your payment method will not be charged during the 7 day free trial." @@ -1911,7 +1911,7 @@ "message": "Credit card" }, "paypalClickSubmit": { - "message": "Click the PayPal button to log into your PayPal account, then click the Submit button below to continue." + "message": "Select the PayPal button to log into your PayPal account, then click the Submit button below to continue." }, "cancelSubscription": { "message": "Cancel subscription" @@ -1938,7 +1938,7 @@ "message": "Are you sure you want to cancel? You will lose access to all of this subscription's features at the end of this billing cycle." }, "canceledSubscription": { - "message": "The subscription has been cancelled." + "message": "Subscription cancelled" }, "neverExpires": { "message": "Never expires" @@ -2058,7 +2058,7 @@ } }, "contactSupport": { - "message": "Contact Customer Support" + "message": "Contact customer support" }, "updatedPaymentMethod": { "message": "Updated payment method." @@ -2079,7 +2079,7 @@ } }, "uploadLicenseFilePremium": { - "message": "To upgrade your account to a premium membership you need to upload a valid licence file." + "message": "To upgrade your account to a Premium membership you need to upload a valid licence file." }, "uploadLicenseFileOrg": { "message": "To create an on-premises hosted organisation you need to upload a valid licence file." @@ -2237,7 +2237,7 @@ "message": "On-premise hosting (optional)" }, "usersGetPremium": { - "message": "Users get access to premium features" + "message": "Users get access to Premium features" }, "controlAccessWithGroups": { "message": "Control user access with groups" @@ -2303,7 +2303,7 @@ "message": "Your new organisation is ready to go!" }, "organizationUpgraded": { - "message": "Your organisation has been upgraded." + "message": "Organisation upgraded" }, "leave": { "message": "Leave" @@ -2312,7 +2312,7 @@ "message": "Are you sure you want to leave this organisation?" }, "leftOrganization": { - "message": "You have left the organisation." + "message": "You left the organisation" }, "defaultCollection": { "message": "Default collection" @@ -2336,7 +2336,7 @@ "message": "Policies" }, "singleSignOn": { - "message": "Single Sign-On" + "message": "Single sign-on" }, "editPolicy": { "message": "Edit policy" @@ -2366,10 +2366,10 @@ "message": "When a member is revoked, they no longer have access to organisation data. To quickly restore member access, go to the Revoked tab." }, "removeUserConfirmationKeyConnector": { - "message": "Warning! This user requires Key Connector to manage their encryption. Removing this user from your organisation will permanently disable their account. This action cannot be undone. Do you want to proceed?" + "message": "Warning! This user requires Key Connector to manage their encryption. Removing this user from your organisation will permanently deactivate their account. This action cannot be undone. Do you want to proceed?" }, "externalId": { - "message": "External ID" + "message": "External id" }, "externalIdDesc": { "message": "The external ID can be used as a reference or to link this resource to an external system such as a user directory." @@ -2438,7 +2438,7 @@ "message": "Confirmed" }, "clientOwnerEmail": { - "message": "Client Owner Email" + "message": "Client owner email" }, "owner": { "message": "Owner" @@ -2501,16 +2501,16 @@ "message": "Web vault" }, "loggedIn": { - "message": "Logged in." + "message": "Logged in" }, "changedPassword": { - "message": "Changed account password." + "message": "Changed account password" }, "enabledUpdated2fa": { - "message": "Enabled/updated two-step login." + "message": "Two-step login saved" }, "disabled2fa": { - "message": "Disabled two-step login." + "message": "Two-step login turned off" }, "recovered2fa": { "message": "Recovered account from two-step login." @@ -2522,7 +2522,7 @@ "message": "Login attempt failed with incorrect two-step login." }, "exportedVault": { - "message": "Exported vault." + "message": "Vault exported" }, "exportedOrganizationVault": { "message": "Exported organisation vault." @@ -2891,16 +2891,16 @@ "message": "Edit the groups that this user belongs to." }, "invitedUsers": { - "message": "Invited user(s)." + "message": "User(s) invited" }, "resendInvitation": { "message": "Resend invitation" }, "resendEmail": { - "message": "Resend Email" + "message": "Resend email" }, "hasBeenReinvited": { - "message": "$USER$ has been reinvited.", + "message": "$USER$ reinvited", "placeholders": { "user": { "content": "$1", @@ -2915,7 +2915,7 @@ "message": "Confirm user" }, "hasBeenConfirmed": { - "message": "$USER$ has been confirmed.", + "message": "$USER$ confirmed.", "placeholders": { "user": { "content": "$1", @@ -2948,13 +2948,13 @@ "message": "Check your email inbox for a verification link." }, "emailVerified": { - "message": "Your email has been verified." + "message": "Account email verified" }, "emailVerifiedFailed": { "message": "Unable to verify your email. Try sending a new verification email." }, "emailVerificationRequired": { - "message": "Email Verification Required" + "message": "Email verification required" }, "emailVerificationRequiredDesc": { "message": "You must verify your email to use this feature." @@ -2993,13 +2993,13 @@ "message": "Remember email" }, "recoverAccountTwoStepDesc": { - "message": "If you cannot access your account through your normal two-step login methods, you can use your two-step login recovery code to disable all two-step providers on your account." + "message": "If you cannot access your account through your normal two-step login methods, you can use your two-step login recovery code to turn off all two-step providers on your account." }, "recoverAccountTwoStep": { "message": "Recover account two-step login" }, "twoStepRecoverDisabled": { - "message": "Two-step login has been disabled on your account." + "message": "Two-step login turned off on your account." }, "learnMore": { "message": "Learn more" @@ -3011,13 +3011,13 @@ "message": "If your account exists, we've sent you an email with further instructions." }, "deleteRecoverConfirmDesc": { - "message": "You have requested to delete your Bitwarden account. Click the button below to confirm." + "message": "You have requested to delete your Bitwarden account. Use the button below to confirm." }, "myOrganization": { "message": "My organisation" }, "organizationInfo": { - "message": "Organization info" + "message": "Organisation info" }, "deleteOrganization": { "message": "Delete organisation" @@ -3050,7 +3050,7 @@ "message": "The organisation and all associated data has been deleted." }, "organizationUpdated": { - "message": "Organisation updated" + "message": "Organisation saved" }, "taxInformation": { "message": "Tax information" @@ -3063,7 +3063,7 @@ "description": "A billing plan/package. For example: Families, Teams, Enterprise, etc." }, "changeBillingPlan": { - "message": "Upgrade Plan", + "message": "Upgrade plan", "description": "A billing plan/package. For example: Families, Teams, Enterprise, etc." }, "changeBillingPlanUpgrade": { @@ -3096,10 +3096,10 @@ "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make two micro-deposits within the next 1-2 business days. Enter these amounts on the organisation's billing page to verify the bank account." }, "verifyBankAccountFailureWarning": { - "message": "Failure to verify the bank account will result in a missed payment and your subscription being disabled." + "message": "Failure to verify the bank account will result in a missed payment and your subscription being suspended." }, "verifiedBankAccount": { - "message": "Bank account has been verified." + "message": "Bank account verified" }, "bankAccount": { "message": "Bank account" diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 72d610f22b7..aad95307bda 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -1884,10 +1884,10 @@ "description": "Short abbreviation for 'month'" }, "paymentChargedAnnually": { - "message": "Ваш метод оплаты будет активирован немедленно и использоваться ежегодно. Вы можете отменить это в любой момент." + "message": "Необходимая сумма с вашего метода оплаты будет списана немедленно, а затем с периодичностью каждый год. Можно отменить в любой момент." }, "paymentCharged": { - "message": "Ваш метод оплаты будет активирован немедленно и использоваться каждый $INTERVAL$. Вы можете отменить это в любой момент.", + "message": "Необходимая сумма с вашего метода оплаты будет списана немедленно, а затем с периодичностью $INTERVAL$. Можно отменить в любой момент.", "placeholders": { "interval": { "content": "$1", @@ -2020,7 +2020,7 @@ "message": "Нет транзакций." }, "chargeNoun": { - "message": "Списание", + "message": "Платеж", "description": "Noun. A charge from a payment method." }, "refundNoun": { @@ -3278,7 +3278,7 @@ "message": "Загрузка" }, "upgrade": { - "message": "Обновить" + "message": "Перейти" }, "upgradeOrganization": { "message": "Обновить организацию" diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 029b078423b..74d273b2d21 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -181,7 +181,7 @@ "description": "This is the folder for uncategorized items" }, "addFolder": { - "message": "Skapa mapp" + "message": "Lägg till mapp" }, "editFolder": { "message": "Redigera mapp" @@ -1351,7 +1351,7 @@ "message": "Hantera" }, "disable": { - "message": "Inaktivera" + "message": "Stäng av" }, "revokeAccess": { "message": "Återkalla åtkomst" @@ -1639,7 +1639,7 @@ } }, "weakPasswordsReport": { - "message": "Rapport om svaga lösenord" + "message": "Svaga lösenord" }, "weakPasswordsReportDesc": { "message": "Svaga lösenord kan enkelt gissas av hackare och automatiserade verktyg som används för att knäcka lösenord. Bitwardens lösenordsgenerator kan hjälpa dig att skapa starka lösenord." @@ -2567,7 +2567,7 @@ } }, "viewAllLoginOptions": { - "message": "View all log in options" + "message": "Visa alla inloggningsalternativ" }, "viewedItemId": { "message": "Visade objektet $ID$.", @@ -2960,7 +2960,7 @@ "message": "Du måste verifiera din e-post för att använda den här funktionen." }, "updateBrowser": { - "message": "Uppdatera webbläsaren" + "message": "Uppdatera webbläsare" }, "updateBrowserDesc": { "message": "Du använder en webbläsare som inte stöds. Webbvalvet kanske inte fungerar som det ska." @@ -3345,7 +3345,7 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Skapad", "description": "ex. Date this item was created" }, "datePasswordUpdated": { @@ -4702,7 +4702,7 @@ "message": "One or more organization policies prevents you from exporting your individual vault." }, "selectType": { - "message": "Select SSO type" + "message": "Välj SSO-typ" }, "type": { "message": "Typ" @@ -5110,10 +5110,10 @@ "message": "Billing sync token" }, "active": { - "message": "Active" + "message": "Aktiv" }, "inactive": { - "message": "Inactive" + "message": "Inaktiv" }, "sentAwaitingSync": { "message": "Sent (awaiting sync)" @@ -5266,7 +5266,7 @@ "message": "You cannot redeem for the active account. Enter a different email." }, "revokeWhenExpired": { - "message": "Expires $DATE$", + "message": "Upphör $DATE$", "placeholders": { "date": { "content": "$1", @@ -5358,7 +5358,7 @@ "description": "the text, 'SCIM', is an acronymn and should not be translated." }, "scimEnabledCheckboxDesc": { - "message": "Enable SCIM", + "message": "Aktivera SCIM", "description": "the text, 'SCIM', is an acronymn and should not be translated." }, "scimEnabledCheckboxDescHelpText": { @@ -5440,7 +5440,7 @@ "message": "Turn on" }, "on": { - "message": "On" + "message": "På" }, "members": { "message": "Medlemmar" @@ -5467,7 +5467,7 @@ "message": "Retrieving options..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Inga objekt hittades" }, "multiSelectClearAll": { "message": "Clear all" diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 78301d7d0b0..ad826f0152e 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -434,7 +434,7 @@ "message": "Kho" }, "vaults": { - "message": "Vaults" + "message": "Kho" }, "vaultItems": { "message": "Vault items" @@ -936,22 +936,22 @@ "message": "Set a password to encrypt the export and import it to any Bitwarden account using the password for decryption." }, "fileTypeHeading": { - "message": "File type" + "message": "Loại tệp" }, "accountBackup": { - "message": "Account backup" + "message": "Sao lưu tài khoản" }, "passwordProtected": { - "message": "Password protected" + "message": "Mật khẩu đã được bảo vệ" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, "confirmVaultImport": { - "message": "Confirm vault import" + "message": "Xác nhận nhập kho lưu trữ" }, "confirmVaultImportDesc": { - "message": "This file is password-protected. Please enter the file password to import data." + "message": "Tập tin này được mật khẩu bảo vệ. Vui lòng nhập mật khẩu tệp để nhập dữ liệu." }, "exportSuccess": { "message": "Dữ liệu kho cảu bạn đã được trích xuất." @@ -979,15 +979,15 @@ "message": "Độ dài" }, "uppercase": { - "message": "Uppercase (A-Z)", + "message": "Chữ in hoa (A-Z)", "description": "Include uppercase letters in the password generator." }, "lowercase": { - "message": "Lowercase (a-z)", + "message": "Chữ in thường (a-z)", "description": "Include lowercase letters in the password generator." }, "numbers": { - "message": "Numbers (0-9)" + "message": "Số (0-9)" }, "specialCharacters": { "message": "Special characters (!@#$%^&*)" @@ -1787,10 +1787,10 @@ "description": "Another way of saying \"Get a Premium membership\"" }, "premiumUpdated": { - "message": "You've upgraded to Premium." + "message": "Bạn đã nâng cấp lên Cao Cấp." }, "premiumUpgradeUnlockFeatures": { - "message": "Upgrade your account to a Premium membership and unlock some great additional features." + "message": "Nâng cấp tài khoản của bạn lên thành viên Cao Cấp và mở khóa một số tính năng bổ sung tuyệt vời." }, "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." @@ -2990,7 +2990,7 @@ } }, "rememberEmail": { - "message": "Remember email" + "message": "Ghi nhớ đăng nhập" }, "recoverAccountTwoStepDesc": { "message": "If you cannot access your account through your normal two-step login methods, you can use your two-step login recovery code to turn off all two-step providers on your account." @@ -3278,13 +3278,13 @@ "message": "Loading" }, "upgrade": { - "message": "Upgrade" + "message": "Nâng cấp" }, "upgradeOrganization": { "message": "Upgrade organization" }, "upgradeOrganizationDesc": { - "message": "This feature is not available for free organizations. Switch to a paid plan to unlock more features." + "message": "Tính năng này không có sẵn cho các tổ chức miễn phí. Chuyển sang gói trả phí để mở khóa nhiều tính năng hơn." }, "createOrganizationStep1": { "message": "Create organization: Step 1" @@ -3311,7 +3311,7 @@ "message": "Privacy Policy" }, "filters": { - "message": "Filters" + "message": "Bộ lọc" }, "vaultTimeout": { "message": "Vault timeout" @@ -3486,7 +3486,7 @@ "message": "Minimum length" }, "clone": { - "message": "Clone" + "message": "Tạo bản sao" }, "masterPassPolicyTitle": { "message": "Master password requirements" diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 7145d3c606c..fcc48dbb4c8 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -1121,7 +1121,7 @@ "message": "清空密码库" }, "purgedOrganizationVault": { - "message": "组织密码库已清空。" + "message": "清空了组织密码库。" }, "vaultAccessedByProvider": { "message": "密码库被提供商访问。" @@ -2522,7 +2522,7 @@ "message": "登录失败,两步登录不正确。" }, "exportedVault": { - "message": "导出了密码库。" + "message": "导出了密码库" }, "exportedOrganizationVault": { "message": "导出了组织密码库。" @@ -2651,7 +2651,7 @@ } }, "createdCollectionId": { - "message": "已创建集合 $ID$。", + "message": "创建了集合 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2660,7 +2660,7 @@ } }, "editedCollectionId": { - "message": "已编辑集合 $ID$。", + "message": "编辑了集合 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2669,7 +2669,7 @@ } }, "deletedCollectionId": { - "message": "已删除集合 $ID$。", + "message": "删除了集合 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2678,7 +2678,7 @@ } }, "editedPolicyId": { - "message": "已编辑策略 $ID$。", + "message": "编辑了策略 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2687,7 +2687,7 @@ } }, "createdGroupId": { - "message": "已创建群组 $ID$。", + "message": "创建了群组 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2696,7 +2696,7 @@ } }, "editedGroupId": { - "message": "已编辑群组 $ID$。", + "message": "编辑了群组 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2705,7 +2705,7 @@ } }, "deletedGroupId": { - "message": "已删除群组 $ID$。", + "message": "删除了群组 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2714,7 +2714,7 @@ } }, "removedUserId": { - "message": "已移除用户 $ID$。", + "message": "移除了用户 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2723,7 +2723,7 @@ } }, "removeUserIdAccess": { - "message": "移除 $ID$ 的访问权限", + "message": "移除了 $ID$ 的访问权限", "placeholders": { "id": { "content": "$1", @@ -2732,7 +2732,7 @@ } }, "revokedUserId": { - "message": "已撤销 $ID$ 的组织访问权限。", + "message": "撤销了 $ID$ 的组织访问权限。", "placeholders": { "id": { "content": "$1", @@ -2741,7 +2741,7 @@ } }, "restoredUserId": { - "message": "已恢复 $ID$ 的组织访问权限。", + "message": "恢复了 $ID$ 的组织访问权限。", "placeholders": { "id": { "content": "$1", @@ -2759,7 +2759,7 @@ } }, "createdAttachmentForItem": { - "message": "已为项目 $ID$ 创建附件。", + "message": "为项目 $ID$ 创建了附件。", "placeholders": { "id": { "content": "$1", @@ -2768,7 +2768,7 @@ } }, "deletedAttachmentForItem": { - "message": "已删除项目 $ID$ 的附件。", + "message": "删除了项目 $ID$ 的附件。", "placeholders": { "id": { "content": "$1", @@ -2777,7 +2777,7 @@ } }, "editedCollectionsForItem": { - "message": "已为项目 $ID$ 编辑集合。", + "message": "编辑了项目 $ID$ 集合。", "placeholders": { "id": { "content": "$1", @@ -2786,7 +2786,7 @@ } }, "invitedUserId": { - "message": "已邀请用户 $ID$。", + "message": "邀请了用户 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2795,7 +2795,7 @@ } }, "confirmedUserId": { - "message": "已确认用户 $ID$。", + "message": "确认了用户 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2804,7 +2804,7 @@ } }, "editedUserId": { - "message": "已编辑用户 $ID$。", + "message": "编辑了用户 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2813,7 +2813,7 @@ } }, "editedGroupsForUser": { - "message": "已为用户 $ID$ 编辑群组。", + "message": "编辑了用户 $ID$ 的群组。", "placeholders": { "id": { "content": "$1", @@ -2831,7 +2831,7 @@ } }, "createdOrganizationId": { - "message": "已创建组织 $ID$。", + "message": "创建了组织 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2840,7 +2840,7 @@ } }, "addedOrganizationId": { - "message": "已添加组织 $ID$。", + "message": "添加了组织 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2849,7 +2849,7 @@ } }, "removedOrganizationId": { - "message": "已移除组织 $ID$。", + "message": "移除了组织 $ID$。", "placeholders": { "id": { "content": "$1", @@ -2858,7 +2858,7 @@ } }, "accessedClientVault": { - "message": "已访问 $ID$ 组织密码库。", + "message": "访问了 $ID$ 组织密码库。", "placeholders": { "id": { "content": "$1", @@ -3611,7 +3611,7 @@ } }, "permanentlyDeletedItemId": { - "message": "项目 $ID$ 已永久删除", + "message": "永久删除了项目 $ID$", "placeholders": { "id": { "content": "$1", @@ -3650,7 +3650,7 @@ } }, "restoredItemId": { - "message": "项目 $ID$ 已恢复", + "message": "恢复了项目 $ID$", "placeholders": { "id": { "content": "$1", @@ -4099,7 +4099,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "modifiedPolicyId": { - "message": "策略 $ID$ 已修改。", + "message": "修改了策略 $ID$。", "placeholders": { "id": { "content": "$1", @@ -4342,7 +4342,7 @@ } }, "eventAdminPasswordReset": { - "message": "已重置用户 $ID$ 的主密码。", + "message": "重置了用户 $ID$ 的主密码。", "placeholders": { "id": { "content": "$1", From 330423ecdecaddea8af2b2eb3073b9776abc7c9a Mon Sep 17 00:00:00 2001 From: githubdev592 <118946273+githubdev592@users.noreply.github.com> Date: Sat, 26 Nov 2022 10:12:49 -0600 Subject: [PATCH 09/40] Fix clients documentation link in README (#4123) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c426251905b..a80918cf251 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository houses all Bitwarden client applications except the [Mobile application](https://github.com/bitwarden/mobile). -Please refer to the [Clients section](https://contributing.bitwarden.com/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Clients section](https://contributing.bitwarden.com/docs/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. ## Related projects: From cdd9c1677810b0eded1e08e6de94b5f74363c535 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Sun, 27 Nov 2022 16:18:24 -0500 Subject: [PATCH 10/40] Enabled passwordless on cloud.json (#4127) --- apps/web/config/cloud.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 5bd5e6b0608..eb13f2febb2 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -17,6 +17,6 @@ }, "flags": { "showTrial": true, - "showPasswordless": false + "showPasswordless": true } } From 20eb585d2b003709e93dfc404d103ef87964105d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 28 Nov 2022 14:04:41 +0100 Subject: [PATCH 11/40] [SM-342] Password Toggle directive (#3850) --- .../src/button/button.component.html | 1 + .../components/src/button/button.component.ts | 9 ++ libs/components/src/button/button.stories.ts | 14 +++ .../src/form-field/form-field-control.ts | 3 + .../src/form-field/form-field.module.ts | 25 +++-- .../src/form-field/form-field.stories.ts | 18 +--- .../password-input-toggle.directive.ts | 54 ++++++++++ .../form-field/password-input-toggle.spec.ts | 100 ++++++++++++++++++ .../password-input-toggle.stories.ts | 77 ++++++++++++++ .../src/icon-button/icon-button.component.ts | 2 +- libs/components/src/input/input.directive.ts | 29 +++-- 11 files changed, 301 insertions(+), 31 deletions(-) create mode 100644 libs/components/src/form-field/password-input-toggle.directive.ts create mode 100644 libs/components/src/form-field/password-input-toggle.spec.ts create mode 100644 libs/components/src/form-field/password-input-toggle.stories.ts diff --git a/libs/components/src/button/button.component.html b/libs/components/src/button/button.component.html index ee4d150dfcc..836eb0f9655 100644 --- a/libs/components/src/button/button.component.html +++ b/libs/components/src/button/button.component.html @@ -1,5 +1,6 @@ + ({ + props: args, + template: ` + + + + `, +}); + +export const Icon = IconTemplate.bind({}); +Icon.args = { + icon: "bwi-eye", +}; diff --git a/libs/components/src/form-field/form-field-control.ts b/libs/components/src/form-field/form-field-control.ts index ee63407a8bd..5f57bdbbac5 100644 --- a/libs/components/src/form-field/form-field-control.ts +++ b/libs/components/src/form-field/form-field-control.ts @@ -5,4 +5,7 @@ export abstract class BitFormFieldControl { required: boolean; hasError: boolean; error: [string, any]; + type?: "text" | "password"; + spellcheck?: boolean; + focus?: () => void; } diff --git a/libs/components/src/form-field/form-field.module.ts b/libs/components/src/form-field/form-field.module.ts index ec2fe456df6..cc18cf4b77f 100644 --- a/libs/components/src/form-field/form-field.module.ts +++ b/libs/components/src/form-field/form-field.module.ts @@ -11,30 +11,33 @@ import { BitErrorComponent } from "./error.component"; import { BitFormFieldComponent } from "./form-field.component"; import { BitHintComponent } from "./hint.component"; import { BitLabel } from "./label.directive"; +import { BitPasswordInputToggleDirective } from "./password-input-toggle.directive"; import { BitPrefixDirective } from "./prefix.directive"; import { BitSuffixDirective } from "./suffix.directive"; @NgModule({ imports: [SharedModule, InputModule, MultiSelectModule], - exports: [ - BitErrorComponent, - BitErrorSummary, - BitFormFieldComponent, - BitHintComponent, - BitLabel, - BitPrefixDirective, - BitSuffixDirective, - BitInputDirective, - MultiSelectComponent, - ], declarations: [ BitErrorComponent, BitErrorSummary, BitFormFieldComponent, BitHintComponent, BitLabel, + BitPasswordInputToggleDirective, BitPrefixDirective, BitSuffixDirective, ], + exports: [ + BitErrorComponent, + BitErrorSummary, + BitFormFieldComponent, + BitHintComponent, + BitInputDirective, + BitLabel, + BitPasswordInputToggleDirective, + BitPrefixDirective, + BitSuffixDirective, + MultiSelectComponent, + ], }) export class FormFieldModule {} diff --git a/libs/components/src/form-field/form-field.stories.ts b/libs/components/src/form-field/form-field.stories.ts index 68d341e6922..1683f515e28 100644 --- a/libs/components/src/form-field/form-field.stories.ts +++ b/libs/components/src/form-field/form-field.stories.ts @@ -166,13 +166,9 @@ const ButtonGroupTemplate: Story = (args: BitFormFieldCom template: ` Label - - - + + + `, }); @@ -188,12 +184,8 @@ const DisabledButtonInputGroupTemplate: Story = ( Label - - + + `, }); diff --git a/libs/components/src/form-field/password-input-toggle.directive.ts b/libs/components/src/form-field/password-input-toggle.directive.ts new file mode 100644 index 00000000000..6189de636ea --- /dev/null +++ b/libs/components/src/form-field/password-input-toggle.directive.ts @@ -0,0 +1,54 @@ +import { + AfterContentInit, + Directive, + EventEmitter, + Host, + HostListener, + Input, + OnChanges, + Output, +} from "@angular/core"; + +import { ButtonComponent } from "../button"; + +import { BitFormFieldComponent } from "./form-field.component"; + +@Directive({ + selector: "[bitPasswordInputToggle]", +}) +export class BitPasswordInputToggleDirective implements AfterContentInit, OnChanges { + @Input() toggled = false; + @Output() toggledChange = new EventEmitter(); + + @HostListener("click") onClick() { + this.toggled = !this.toggled; + this.toggledChange.emit(this.toggled); + + this.update(); + + this.formField.input?.focus(); + } + + constructor(@Host() private button: ButtonComponent, private formField: BitFormFieldComponent) {} + + get icon() { + return this.toggled ? "bwi-eye-slash" : "bwi-eye"; + } + + ngOnChanges(): void { + this.update(); + } + + ngAfterContentInit(): void { + this.toggled = this.formField.input.type !== "password"; + this.button.icon = this.icon; + } + + private update() { + this.button.icon = this.icon; + if (this.formField.input?.type != null) { + this.formField.input.type = this.toggled ? "text" : "password"; + this.formField.input.spellcheck = this.toggled ? false : undefined; + } + } +} diff --git a/libs/components/src/form-field/password-input-toggle.spec.ts b/libs/components/src/form-field/password-input-toggle.spec.ts new file mode 100644 index 00000000000..5c6a9d48d00 --- /dev/null +++ b/libs/components/src/form-field/password-input-toggle.spec.ts @@ -0,0 +1,100 @@ +import { Component, DebugElement } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; + +import { ButtonComponent, ButtonModule } from "../button"; +import { InputModule } from "../input/input.module"; + +import { BitFormFieldControl } from "./form-field-control"; +import { BitFormFieldComponent } from "./form-field.component"; +import { FormFieldModule } from "./form-field.module"; +import { BitPasswordInputToggleDirective } from "./password-input-toggle.directive"; + +@Component({ + selector: "test-form-field", + template: ` +
+ + Password + + + +
+ `, +}) +class TestFormFieldComponent {} + +describe("PasswordInputToggle", () => { + let fixture: ComponentFixture; + let button: ButtonComponent; + let input: BitFormFieldControl; + let toggle: DebugElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormFieldModule, ButtonModule, InputModule], + declarations: [TestFormFieldComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(TestFormFieldComponent); + fixture.detectChanges(); + + toggle = fixture.debugElement.query(By.directive(BitPasswordInputToggleDirective)); + const buttonEl = fixture.debugElement.query(By.directive(ButtonComponent)); + button = buttonEl.componentInstance; + const formFieldEl = fixture.debugElement.query(By.directive(BitFormFieldComponent)); + const formField: BitFormFieldComponent = formFieldEl.componentInstance; + input = formField.input; + }); + + describe("initial state", () => { + it("has correct icon", () => { + expect(button.icon).toBe("bwi-eye"); + }); + + it("input is type password", () => { + expect(input.type).toBe("password"); + }); + + it("spellcheck is disabled", () => { + expect(input.spellcheck).toBe(undefined); + }); + }); + + describe("when toggled", () => { + beforeEach(() => { + toggle.triggerEventHandler("click"); + }); + + it("has correct icon", () => { + expect(button.icon).toBe("bwi-eye-slash"); + }); + + it("input is type text", () => { + expect(input.type).toBe("text"); + }); + + it("spellcheck is disabled", () => { + expect(input.spellcheck).toBe(false); + }); + }); + + describe("when toggled twice", () => { + beforeEach(() => { + toggle.triggerEventHandler("click"); + toggle.triggerEventHandler("click"); + }); + + it("has correct icon", () => { + expect(button.icon).toBe("bwi-eye"); + }); + + it("input is type password", () => { + expect(input.type).toBe("password"); + }); + + it("spellcheck is disabled", () => { + expect(input.spellcheck).toBe(undefined); + }); + }); +}); diff --git a/libs/components/src/form-field/password-input-toggle.stories.ts b/libs/components/src/form-field/password-input-toggle.stories.ts new file mode 100644 index 00000000000..ff6e14c0c91 --- /dev/null +++ b/libs/components/src/form-field/password-input-toggle.stories.ts @@ -0,0 +1,77 @@ +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { Meta, moduleMetadata, Story } from "@storybook/angular"; + +import { ButtonModule } from "../button"; +import { InputModule } from "../input/input.module"; + +import { FormFieldModule } from "./form-field.module"; +import { BitPasswordInputToggleDirective } from "./password-input-toggle.directive"; + +export default { + title: "Component Library/Form/Password Toggle", + component: BitPasswordInputToggleDirective, + decorators: [ + moduleMetadata({ + imports: [FormsModule, ReactiveFormsModule, FormFieldModule, InputModule, ButtonModule], + }), + ], + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/file/f32LSg3jaegICkMu7rPARm/Tailwind-Component-Library-Update?node-id=1881%3A17689", + }, + docs: { + description: { + component: + "Directive for toggling the visibility of a password input. Works by either having living inside a `bit-form-field` or by using the `toggled` two-way binding.", + }, + }, + }, +} as Meta; + +const Template: Story = ( + args: BitPasswordInputToggleDirective +) => ({ + props: { + ...args, + }, + template: ` +
+ + Password + + + +
+ `, +}); + +export const Default = Template.bind({}); +Default.props = {}; + +const TemplateBinding: Story = ( + args: BitPasswordInputToggleDirective +) => ({ + props: { + ...args, + }, + template: ` +
+ + Password + + + + + +
+ `, +}); + +export const Binding = TemplateBinding.bind({}); +Binding.props = { + toggled: false, +}; diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index ef9474bf98d..c7fe53695b6 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -79,7 +79,7 @@ const sizes: Record = { }; @Component({ - selector: "button[bitIconButton]", + selector: "button[bitIconButton]:not(button[bitButton])", templateUrl: "icon-button.component.html", providers: [{ provide: ButtonLikeAbstraction, useExisting: BitIconButtonComponent }], }) diff --git a/libs/components/src/input/input.directive.ts b/libs/components/src/input/input.directive.ts index 619b0229057..cb5c97fecc1 100644 --- a/libs/components/src/input/input.directive.ts +++ b/libs/components/src/input/input.directive.ts @@ -1,4 +1,4 @@ -import { Directive, HostBinding, Input, Optional, Self } from "@angular/core"; +import { Directive, ElementRef, HostBinding, Input, NgZone, Optional, Self } from "@angular/core"; import { NgControl, Validators } from "@angular/forms"; import { BitFormFieldControl } from "../form-field/form-field-control"; @@ -41,14 +41,14 @@ export class BitInputDirective implements BitFormFieldControl { @HostBinding("attr.aria-describedby") ariaDescribedBy: string; - get labelForId(): string { - return this.id; - } - @HostBinding("attr.aria-invalid") get ariaInvalid() { return this.hasError ? true : undefined; } + @HostBinding("attr.type") @Input() type?: "text" | "password"; + + @HostBinding("attr.spellcheck") @Input() spellcheck?: boolean; + @HostBinding() @Input() get required() { @@ -62,6 +62,10 @@ export class BitInputDirective implements BitFormFieldControl { @Input() hasPrefix = false; @Input() hasSuffix = false; + get labelForId(): string { + return this.id; + } + get hasError() { return this.ngControl?.status === "INVALID" && this.ngControl?.touched; } @@ -70,5 +74,18 @@ export class BitInputDirective implements BitFormFieldControl { const key = Object.keys(this.ngControl.errors)[0]; return [key, this.ngControl.errors[key]]; } - constructor(@Optional() @Self() private ngControl: NgControl) {} + + constructor( + @Optional() @Self() private ngControl: NgControl, + private ngZone: NgZone, + private elementRef: ElementRef + ) {} + + focus() { + this.ngZone.runOutsideAngular(() => { + const end = this.elementRef.nativeElement.value.length; + this.elementRef.nativeElement.setSelectionRange(end, end); + this.elementRef.nativeElement.focus(); + }); + } } From 35f87604355b7fefa7e6e8e29f6794261a693dcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:47:15 +0100 Subject: [PATCH 12/40] Autosync the updated translations (#4132) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/fi/messages.json | 2 +- apps/browser/store/locales/fi/copy.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index bc362924d83..e0dace731c5 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden – Ilmainen salasananhallinta", + "message": "Bitwarden - Free Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { diff --git a/apps/browser/store/locales/fi/copy.resx b/apps/browser/store/locales/fi/copy.resx index b14f5f8e5f1..fb02d78fe42 100644 --- a/apps/browser/store/locales/fi/copy.resx +++ b/apps/browser/store/locales/fi/copy.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Ilmainen salasanojen hallinta + Bitwarden – Free Password Manager Turvallinen ja ilmainen salasanojen hallinta kaikille laitteillesi From b312f6b925acce2e568db1d52e73f191b1379363 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:48:23 +0100 Subject: [PATCH 13/40] Autosync the updated translations (#4131) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/da/messages.json | 46 ++++----- apps/web/src/locales/en_GB/messages.json | 118 +++++++++++------------ apps/web/src/locales/tr/messages.json | 14 +-- 3 files changed, 89 insertions(+), 89 deletions(-) diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 94a796b1fb3..49765c8ee57 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -3878,7 +3878,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendProtectedPassword": { - "message": "Denne Send er beskyttet med en adgangskode. Indtast adgangskoden nedenfor for at fortsætte.", + "message": "Denne Send er adgangskodebeskyttet. Angiv adgangskoden nedenfor for at fortsætte.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendProtectedPasswordDontKnow": { @@ -3893,11 +3893,11 @@ "message": "Download fil" }, "sendAccessUnavailable": { - "message": "Den Send, du forsøger at tilgå, findes ikke eller er ikke længere tilgængelig.", + "message": "Den Send, der forsøges tilgået, findes ikke, eller er ikke længere tilgængelig.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "missingSendFile": { - "message": "Filen, der er knyttet til denne Send, blev ikke fundet.", + "message": "Filen tilknyttet denne Send kunne ikke findes.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "noSendsInList": { @@ -3917,7 +3917,7 @@ "message": "Betroede nødkontakter" }, "noTrustedContacts": { - "message": "Du har endnu ikke tilføjet nødkontakter. Invitér en betroet kontakt for at komme i gang." + "message": "Ingen nødkontakter tilføjet endnu. Invitér en betroet kontakt for at komme i gang." }, "addEmergencyContact": { "message": "Tilføj nødkontakt" @@ -3995,7 +3995,7 @@ "message": "Anmod om adgang" }, "requestAccessConfirmation": { - "message": "Er du sikker på, at du vil anmode om nødadgang? Du vil opnå adgang efter $WAITTIME$ dag(e) eller når brugeren godkender anmodningen.", + "message": "Sikker på, at du vil anmode om nødadgang? Du tildeles adgang efter $WAITTIME$ dag(e), eller når brugeren godkender anmodningen.", "placeholders": { "waittime": { "content": "$1", @@ -4019,7 +4019,7 @@ "message": "Afvis" }, "approveAccessConfirmation": { - "message": "Er du sikker på, at du vil godkende nødadgang? Dette vil tillade $USER$ at $ACTION$ din konto.", + "message": "Sikker på, at du vil godkende nødadgang? Dette vil tillade $USER$ at $ACTION$ din konto.", "placeholders": { "user": { "content": "$1", @@ -4035,7 +4035,7 @@ "message": "Nødadgang godkendt" }, "emergencyRejected": { - "message": "Nødadgang afvist" + "message": "Nødadgang nægtet" }, "passwordResetFor": { "message": "Adgangskode nulstillet for $USER$. Du kan nu logge ind med den nye adgangskode.", @@ -4050,7 +4050,7 @@ "message": "Fjern individuel boks" }, "personalOwnershipPolicyDesc": { - "message": "Kræv at medlemmer gemmer elementer i en organisation ved at fjerne muligheden for individuelle bokse." + "message": "Kræv, at medlemmer gemmer emner i en organisation ved at fjerne muligheden for individuelle bokse." }, "personalOwnershipExemption": { "message": "Organisationsejere og administratorer er undtaget fra denne politik." @@ -4066,7 +4066,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disableSendExemption": { - "message": "Organisationsbrugere, der kan håndtere organisationens politikker, er undtaget fra denne politiks håndhævelse." + "message": "Organisationsbrugere, som kan håndtere organisationens politikker, er undtaget fra håndhævelsen af denne politik." }, "sendDisabled": { "message": "Send fjernet", @@ -4085,7 +4085,7 @@ "description": "'Sends' is a plural noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptionsExemption": { - "message": "Organisationsbrugere, som kan håndtere organisationens politikker, er undtaget fra denne politiks håndhævelse." + "message": "Organisationsbrugere, som kan håndtere organisationens politikker, er undtaget fra håndhævelsen af denne politik." }, "disableHideEmail": { "message": "Vis altid medlemmets e-mailadresse med modtagere, når Sends oprettes eller redigeres.", @@ -4095,7 +4095,7 @@ "message": "Følgende organisationspolitikker er i øjeblikket gældende:" }, "sendDisableHideEmailInEffect": { - "message": "Brugere har ikke lov til at skjule deres e-mailadresser for modtagere når en Send oprettes eller redigeres.", + "message": "Brugere har ikke lov til at skjule deres e-mailadresser for modtagere, når en Send oprettes eller redigeres.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "modifiedPolicyId": { @@ -4108,7 +4108,7 @@ } }, "planPrice": { - "message": "Abonnementspris" + "message": "Abonnementstypepris" }, "estimatedTax": { "message": "Anslået moms" @@ -4202,17 +4202,17 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNameDesc": { - "message": "Et venligt navn til at beskrive denne Send.", + "message": "Et logisk navn til at beskrive denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { - "message": "Den tekst, du vil sende." + "message": "Teksten, du vil sende." }, "sendFileDesc": { - "message": "Den fil, du vil sende." + "message": "Filen, du vil sende." }, "copySendLinkOnSave": { - "message": "Kopier linket for at dele denne Send til min udklipsholder ved gem." + "message": "Kopiér linket for at dele denne Send til udklipsholden ved gem." }, "sendLinkLabel": { "message": "Send link", @@ -4266,7 +4266,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about Bitwarden Send or sign up to **try it today.**'" }, "sendCreatorIdentifier": { - "message": "Bitwarden bruger $USER_IDENTIFIER$ delte følgende med dig", + "message": "Bitwarden-bruger $USER_IDENTIFIER$ delte flg. med dig", "placeholders": { "user_identifier": { "content": "$1", @@ -4275,7 +4275,7 @@ } }, "viewSendHiddenEmailWarning": { - "message": "Bitwarden-brugeren, der oprettede denne Send, har valgt at skjule sin e-mailadresse. Du bør sikre dig, at du stoler på kilden til dette link, inden du bruger eller downloader dets indhold.", + "message": "Bitwarden-brugeren, der oprettede denne Send, har valgt at skjule sin e-mailadresse. Du bør sikre dig, at du stoler på kilden til dette link, inden dets indhold downloades/benyttes.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDateIsInvalid": { @@ -4285,13 +4285,13 @@ "message": "Den angivne sletningsdato er ugyldig." }, "expirationDateAndTimeRequired": { - "message": "Der kræves en udløbsdato og -tid." + "message": "Udløbsdato og -tidspunkt er obligatorisk." }, "deletionDateAndTimeRequired": { - "message": "En sletningsdato og -tid er påkrævet." + "message": "Sletningsdato og -tidspunkt er obligatorisk." }, "dateParsingError": { - "message": "Der opstod en fejl under forsøget på at gemme dine sletnings- og udløbsdatoer." + "message": "En fejl opstod under forsøget på at gemme sletnings- og udløbsdatoer." }, "webAuthnFallbackMsg": { "message": "Klik på knappen nedenfor for at bekræfte din 2FA." @@ -4324,7 +4324,7 @@ "message": "Afmelding lykkedes!" }, "eventEnrollPasswordReset": { - "message": "Bruger $ID$ tilmeldt hjælp til nulstilling af adgangskode.", + "message": "Bruger $ID$ tilmeldt adgangskodenulstilling.", "placeholders": { "id": { "content": "$1", @@ -4333,7 +4333,7 @@ } }, "eventWithdrawPasswordReset": { - "message": "Bruger $ID$ afmeldt hjælp til nulstilling af adgangskode.", + "message": "Bruger $ID$ afmeldt adgangskodenulstilling.", "placeholders": { "id": { "content": "$1", diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 32064e91bf3..773d55a2e94 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -3137,10 +3137,10 @@ "message": "Enter your installation ID" }, "limitSubscriptionDesc": { - "message": "Set a seat limit for your subscription. Once this limit is reached, you will not be able to invite new users." + "message": "Set a seat limit for your subscription. Once this limit is reached, you will not be able to invite new members." }, "maxSeatLimit": { - "message": "Maximum Seat Limit (optional)", + "message": "Seat Limit (optional)", "description": "Upper limit of seats to allow through autoscaling" }, "maxSeatCost": { @@ -3158,7 +3158,7 @@ "message": "Adjustments to your subscription will result in prorated changes to your billing totals. If newly invited users exceed your subscription seats, you will immediately receive a prorated charge for the additional users." }, "subscriptionUserSeats": { - "message": "Your subscription allows for a total of $COUNT$ users.", + "message": "Your subscription allows for a total of $COUNT$ members.", "placeholders": { "count": { "content": "$1", @@ -3167,25 +3167,25 @@ } }, "limitSubscription": { - "message": "Limit Subscription (Optional)" + "message": "Limit subscription (optional)" }, "subscriptionSeats": { - "message": "Subscription Seats" + "message": "Subscription seats" }, "subscriptionUpdated": { "message": "Subscription updated" }, "additionalOptions": { - "message": "Additional Options" + "message": "Additional options" }, "additionalOptionsDesc": { "message": "For additional help in managing your subscription, please contact Customer Support." }, "subscriptionUserSeatsUnlimitedAutoscale": { - "message": "Adjustments to your subscription will result in prorated changes to your billing totals. If newly invited users exceed your subscription seats, you will immediately receive a prorated charge for the additional users." + "message": "Adjustments to your subscription will result in pro-rated changes to your billing totals. If newly invited members exceed your subscription seats, you will immediately receive a pro-rated charge for the additional members." }, "subscriptionUserSeatsLimitedAutoscale": { - "message": "Adjustments to your subscription will result in prorated changes to your billing totals. If newly invited users exceed your subscription seats, you will immediately receive a prorated charge for the additional users until your $MAX$ seat limit is reached.", + "message": "Adjustments to your subscription will result in pro-rated changes to your billing totals. If newly invited members exceed your subscription seats, you will immediately receive a pro-rated charge for the additional members until your $MAX$ seat limit is reached.", "placeholders": { "max": { "content": "$1", @@ -3194,7 +3194,7 @@ } }, "subscriptionFreePlan": { - "message": "You cannot invite more than $COUNT$ users without upgrading your plan.", + "message": "You cannot invite more than $COUNT$ members without upgrading your plan.", "placeholders": { "count": { "content": "$1", @@ -3203,7 +3203,7 @@ } }, "subscriptionFamiliesPlan": { - "message": "You cannot invite more than $COUNT$ users without upgrading your plan. Please contact Customer Support to upgrade.", + "message": "You cannot invite more than $COUNT$ members without upgrading your plan. Please contact Customer Support to upgrade.", "placeholders": { "count": { "content": "$1", @@ -3212,7 +3212,7 @@ } }, "subscriptionSponsoredFamiliesPlan": { - "message": "Your subscription allows for a total of $COUNT$ users. Your plan is sponsored and billed to an external organisation.", + "message": "Your subscription allows for a total of $COUNT$ members. Your plan is sponsored and billed to an external organisation.", "placeholders": { "count": { "content": "$1", @@ -3221,7 +3221,7 @@ } }, "subscriptionMaxReached": { - "message": "Adjustments to your subscription will result in prorated changes to your billing totals. You cannot invite more than $COUNT$ users without increasing your subscription seats.", + "message": "Adjustments to your subscription will result in pro-rated changes to your billing totals. You cannot invite more than $COUNT$ members without increasing your subscription seats.", "placeholders": { "count": { "content": "$1", @@ -3287,7 +3287,7 @@ "message": "This feature is not available for free organisations. Switch to a paid plan to unlock more features." }, "createOrganizationStep1": { - "message": "Create organisation: step 1" + "message": "Create organisation: Step 1" }, "createOrganizationCreatePersonalAccount": { "message": "Before creating your organisation, you first need to create a free personal account." @@ -3353,10 +3353,10 @@ "description": "ex. Date this password was updated" }, "organizationIsDisabled": { - "message": "Organisation is disabled." + "message": "Organisation suspended" }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organisations cannot be accessed. Contact your Organisation owner for assistance." + "message": "Items in suspended organisations cannot be accessed. Contact your organisation owner for assistance." }, "licenseIsExpired": { "message": "Licence is expired." @@ -3432,7 +3432,7 @@ "message": "Fingerprint phrase" }, "dontAskFingerprintAgain": { - "message": "Never prompt to verify fingerprint phrases for invited users (Not recommended)", + "message": "Never prompt to verify fingerprint phrases for invited users (not recommended)", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "free": { @@ -3501,10 +3501,10 @@ "message": "Require members to set up two-step login." }, "twoStepLoginPolicyWarning": { - "message": "Organisation members who are not Owners or Administrators and do not have two-step login turned on for their account will be removed from the organisation and will receive an email notifying them about the change." + "message": "Organisation members who are not owners or admins and do not have two-step login setup for their account will be removed from the organisation and will receive an email notifying them about the change." }, "twoStepLoginPolicyUserWarning": { - "message": "You are a member of an organisation that requires two-step login to be enabled on your user account. If you disable all two-step login providers you will be automatically removed from these organisations." + "message": "You are a member of an organisation that requires two-step login to be setup on your user account. If you turn off all two-step login providers you will be automatically removed from these organisations." }, "passwordGeneratorPolicyDesc": { "message": "Set requirements for password generator." @@ -3596,10 +3596,10 @@ "message": "Are you sure you want to permanently delete this item?" }, "permanentlyDeletedItem": { - "message": "Permanently deleted item" + "message": "Item permanently deleted" }, "permanentlyDeletedItems": { - "message": "Permanently deleted items" + "message": "Items permanently deleted" }, "permanentlyDeleteSelectedItemsDesc": { "message": "You have selected $COUNT$ item(s) to permanently delete. Are you sure you want to permanently delete all of these items?", @@ -3611,7 +3611,7 @@ } }, "permanentlyDeletedItemId": { - "message": "Permanently deleted item $ID$.", + "message": "Item $ID$ permanently deleted", "placeholders": { "id": { "content": "$1", @@ -3629,10 +3629,10 @@ "message": "Restore item" }, "restoredItem": { - "message": "Restored item" + "message": "Item restored" }, "restoredItems": { - "message": "Restored items" + "message": "Items restored" }, "restoreItemConfirmation": { "message": "Are you sure you want to restore this item?" @@ -3650,7 +3650,7 @@ } }, "restoredItemId": { - "message": "Restored item $ID$.", + "message": "Item $ID$ restored", "placeholders": { "id": { "content": "$1", @@ -3692,7 +3692,7 @@ "message": "Organisation identifier" }, "ssoLogInWithOrgIdentifier": { - "message": "Log in using your organisation's single sign-on portal. Please enter your organisation's identifier to begin." + "message": "Log in using your organisation's single sign-on portal. Please enter your organisation's SSO identifier to begin." }, "enterpriseSingleSignOn": { "message": "Enterprise single sign-on" @@ -3713,7 +3713,7 @@ "message": "SSO validation failed" }, "ssoIdentifierRequired": { - "message": "Organisation identifier is required." + "message": "Organisation SSO identifier is required." }, "ssoIdentifier": { "message": "SSO identifier" @@ -3740,25 +3740,25 @@ "message": "Your current organisation has a policy that does not allow you to join more than one organisation. Please contact your organisation admins or sign up from a different Bitwarden account." }, "singleOrgPolicyWarning": { - "message": "Organisation members who are not Owners or Administrators and are already a member of another organisation will be removed from your organisation." + "message": "Organisation members who are not owners or admins and are already a member of another organisation will be removed from your organisation." }, "requireSso": { "message": "Require single sign-on authentication" }, "requireSsoPolicyDesc": { - "message": "Require members to log in with the Enterprise Single Sign-On method." + "message": "Require members to log in with the Enterprise single sign-on method." }, "prerequisite": { "message": "Prerequisite" }, "requireSsoPolicyReq": { - "message": "The Single Organization enterprise policy must be turned on before activating this policy." + "message": "The single organisation Enterprise policy must be turned on before activating this policy." }, "requireSsoPolicyReqError": { - "message": "Single Organisation policy not enabled." + "message": "Single organisation policy not set up." }, "requireSsoExemption": { - "message": "Organisation Owners and Administrators are exempt from this policy's enforcement." + "message": "Organisation owners and admins are exempt from this policy's enforcement." }, "sendTypeFile": { "message": "File" @@ -3767,7 +3767,7 @@ "message": "Text" }, "createSend": { - "message": "Create new send", + "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -3775,15 +3775,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Created send", + "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Edited send", + "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "Deleted send", + "message": "Send deleted", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSend": { @@ -3841,14 +3841,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLink": { - "message": "Copy send link", + "message": "Copy Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { "message": "Remove password" }, "removedPassword": { - "message": "Removed password" + "message": "Password removed" }, "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" @@ -3857,7 +3857,7 @@ "message": "Hide my email address from recipients." }, "disableThisSend": { - "message": "Disable this Send so that no one can access it.", + "message": "Deactivate this Send so that no one can access it.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "allSends": { @@ -3882,11 +3882,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendProtectedPasswordDontKnow": { - "message": "Don't know the password? Ask the sender for the password needed to access this send.", + "message": "Don't know the password? Ask the sender for the password needed to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendHiddenByDefault": { - "message": "This send is hidden by default. You can toggle its visibility using the button below.", + "message": "This Send is hidden by default. You can toggle its visibility using the button below.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "downloadFile": { @@ -3905,13 +3905,13 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "emergencyAccess": { - "message": "Emergency Access" + "message": "Emergency access" }, "emergencyAccessDesc": { "message": "Grant and manage emergency access for trusted contacts. Trusted contacts may request access to either View or Takeover your account in case of a emergency. Visit our help page for more information and details into how zero knowledge sharing works." }, "emergencyAccessOwnerWarning": { - "message": "You are an Owner of one or more organisations. If you give takeover access to an emergency contact, they will be able to use all your permissions as Owner after a takeover." + "message": "You are an owner of one or more organisations. If you give takeover access to an emergency contact, they will be able to use all your permissions as owner after a takeover." }, "trustedEmergencyContacts": { "message": "Trusted emergency contacts" @@ -3938,10 +3938,10 @@ "message": "Invite a new emergency contact by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account." }, "emergencyAccessRecoveryInitiated": { - "message": "Emergency Access Initiated" + "message": "Emergency access initiated" }, "emergencyAccessRecoveryApproved": { - "message": "Emergency Access Approved" + "message": "Emergency access approved" }, "viewDesc": { "message": "Can view all items in your own vault." @@ -3953,7 +3953,7 @@ "message": "Can reset your account with a new master password." }, "waitTime": { - "message": "Wait Time" + "message": "Wait time" }, "waitTimeDesc": { "message": "Time required before automatically granting access." @@ -4032,7 +4032,7 @@ } }, "emergencyApproved": { - "message": "Emergency access approved." + "message": "Emergency access approved" }, "emergencyRejected": { "message": "Emergency access rejected" @@ -4053,27 +4053,27 @@ "message": "Require members to save items to an organization by removing the individual vault option." }, "personalOwnershipExemption": { - "message": "Organisation Owners and Administrators are exempt from this policy's enforcement." + "message": "Organisation owners and administrators are exempt from this policy's enforcement." }, "personalOwnershipSubmitError": { - "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organisation and choose from available Collections." + "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organisation and choose from available collections." }, "disableSend": { "message": "Remove Send" }, "disableSendPolicyDesc": { - "message": "Do not allow members to create or edit sends.", + "message": "Do not allow members to create or edit Sends.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disableSendExemption": { - "message": "Organisation users that can manage the organisation's policies are exempt from this policy's enforcement." + "message": "Organisation members that can manage the organisation's policies are exempt from this policy's enforcement." }, "sendDisabled": { - "message": "Send disabled", + "message": "Send removed", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Due to an enterprise policy, you are only able to delete an existing Send.", + "message": "Due to an Enterprise policy, you are only able to delete an existing Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptions": { @@ -4085,10 +4085,10 @@ "description": "'Sends' is a plural noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptionsExemption": { - "message": "Organisation users that can manage the organisation's policies are exempt from this policy's enforcement." + "message": "Organisation members that can manage the organisation's policies are exempt from this policy's enforcement." }, "disableHideEmail": { - "message": "Always show member’s email address with recipients when creating or editing a send.", + "message": "Always show member’s email address with recipients when creating or editing a Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptionsPolicyInEffect": { @@ -4132,22 +4132,22 @@ "message": "Admin Permissions" }, "accessEventLogs": { - "message": "Access Event Logs" + "message": "Access event logs" }, "accessImportExport": { - "message": "Access Import/Export" + "message": "Access import/export" }, "accessReports": { - "message": "Access Reports" + "message": "Access reports" }, "missingPermissions": { "message": "You lack the necessary permissions to perform this action." }, "manageAllCollections": { - "message": "Manage All Collections" + "message": "Manage all collections" }, "createNewCollections": { - "message": "Create New Collections" + "message": "Create new collections" }, "editAnyCollection": { "message": "Edit Any Collection" diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 8624a02f8fd..1de248d2c5c 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -4390,13 +4390,13 @@ "message": "Parola başarıyla sıfırlandı." }, "resetPasswordEnrollmentWarning": { - "message": "Katılmak kuruluş yöneticilerinin ana parolanızı değiştirmesine izin verir. Katılmak istediğinize emin misiniz?" + "message": "Katılmak kuruluş yöneticilerinin ana parolanızı değiştirmesine izin verir" }, "resetPasswordPolicy": { "message": "Ana parola sıfırlama" }, "resetPasswordPolicyDescription": { - "message": "Kuruluş yöneticilerinin kuruluş kullanıcılarının ana parolalarını sıfırlamasına izin ver." + "message": "Kuruluş yöneticilerinin üyelerin ana parolalarını sıfırlamasına izin ver." }, "resetPasswordPolicyWarning": { "message": "Yöneticilerin kullanıcıların ana parolalarını sıfırlayabilmesi önce kuruluştaki kullanıcıların kendileri katılmaları veya otomatik eklenmeleri gerekir." @@ -4492,7 +4492,7 @@ "message": "Kuruluş erişimi başarıyla geri getirildi" }, "bulkFilteredMessage": { - "message": "İstisna. Bu eylem için geçerli değildir." + "message": "İstisna. Bu eylem için geçerli değildir" }, "fingerprint": { "message": "Parmak izi" @@ -4608,7 +4608,7 @@ "message": "Sağlayıcı devre dışı." }, "providerUpdated": { - "message": "Sağlayıcı güncellendi" + "message": "Sağlayıcı kaydedildi" }, "yourProviderIs": { "message": "Sağlayıcınız: $PROVIDER$. Kuruluşunuzun yönetim ve ödeme yetkileri sağlayıcınıza aittir.", @@ -4708,13 +4708,13 @@ "message": "Tür" }, "openIdConnectConfig": { - "message": "OpenID Connect Yapılandırması" + "message": "OpenID Connect yapılandırması" }, "samlSpConfig": { - "message": "SAML Servis Sağlayıcı Yapılandırması" + "message": "SAML servis sağlayıcı yapılandırması" }, "samlIdpConfig": { - "message": "SAML Kimlik Sağlayıcı Yapılandırması" + "message": "SAML kimlik sağlayıcı yapılandırması" }, "callbackPath": { "message": "Callback yolu" From d994faa8a606c7d5c773a6cd532582c5575857ac Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 28 Nov 2022 18:59:46 +0100 Subject: [PATCH 14/40] [SM-252] Enable strict templates (#3601) --- .../accounts/set-password.component.html | 11 ++---- .../popup/accounts/two-factor.component.html | 4 +- .../popup/generator/generator.component.html | 4 +- .../popup/send/send-add-edit.component.html | 7 +++- .../settings/folder-add-edit.component.html | 7 +++- .../src/popup/settings/premium.component.html | 6 +-- .../src/popup/settings/sync.component.html | 10 +++-- .../add-edit-custom-fields.component.html | 4 +- .../src/popup/vault/add-edit.component.html | 27 +++++++------ .../popup/vault/attachments.component.html | 6 +-- .../popup/vault/collections.component.html | 2 +- .../src/popup/vault/current-tab.component.ts | 2 +- .../popup/vault/vault-select.component.html | 8 +--- .../src/popup/vault/view.component.html | 10 ++--- apps/browser/tsconfig.json | 1 + .../src/app/accounts/premium.component.html | 8 ++-- .../app/accounts/two-factor.component.html | 2 +- .../src/app/components/avatar.component.ts | 9 +---- .../layout/account-switcher.component.html | 4 +- .../src/app/send/add-edit.component.html | 10 +++-- .../src/app/send/efflux-dates.component.html | 1 - .../add-edit-custom-fields.component.html | 4 +- .../src/app/vault/add-edit.component.html | 38 +++++++++++++------ .../src/app/vault/attachments.component.html | 6 +-- .../src/app/vault/collections.component.html | 2 +- .../app/vault/folder-add-edit.component.html | 6 +-- .../src/app/vault/generator.component.html | 4 +- .../organization-filter.component.html | 1 - .../desktop/src/app/vault/view.component.html | 10 ++--- apps/desktop/tsconfig.json | 1 + .../src/app/accounts/register.component.html | 2 +- .../vertical-stepper.component.ts | 6 ++- .../app/accounts/two-factor.component.html | 2 +- .../user-verification.component.html | 0 .../components/user-verification.component.ts | 23 +++++++++++ .../organization-subscription.component.html | 10 ++--- .../access-selector.component.html | 10 +++-- .../manage/collection-add-edit.component.html | 16 ++++---- .../manage/entity-events.component.html | 6 +-- .../manage/events.component.html | 2 +- .../manage/group-add-edit.component.html | 12 +++--- .../manage/user-add-edit.component.html | 12 +++--- .../manage/user-groups.component.html | 2 +- .../policies/policies.component.ts | 2 +- .../organization-billing.component.ts | 0 .../organizations/vault/add-edit.component.ts | 2 - .../vault/vault-items.component.ts | 5 +-- .../organizations/vault/vault.component.html | 2 +- .../app/organizations/vault/vault.module.ts | 3 +- .../pages/weak-passwords-report.component.ts | 5 ++- .../report-card/report-card.component.ts | 4 +- apps/web/src/app/send/access.component.html | 2 +- apps/web/src/app/send/add-edit.component.html | 17 +++------ .../src/app/send/efflux-dates.component.html | 3 -- apps/web/src/app/send/send.component.html | 2 +- .../emergency-access-add-edit.component.html | 6 +-- .../organization-plans.component.html | 19 ---------- .../sponsoring-org-row.component.html | 4 +- .../two-factor-authenticator.component.html | 2 +- .../settings/two-factor-duo.component.html | 2 +- .../settings/two-factor-email.component.html | 4 +- .../two-factor-recovery.component.html | 7 +--- .../two-factor-webauthn.component.html | 15 ++++---- .../two-factor-yubikey.component.html | 4 +- .../settings/user-subscription.component.html | 4 +- .../app/settings/verify-email.component.html | 2 +- .../src/app/shared/loose-components.module.ts | 12 +----- .../src/app/tools/generator.component.html | 2 +- .../tools/import-export/export.component.html | 3 +- .../tools/import-export/import.component.html | 2 + .../add-edit-custom-fields.component.html | 4 +- .../web/src/app/vault/add-edit.component.html | 12 +++--- .../src/app/vault/attachments.component.html | 16 +++++--- .../src/app/vault/bulk-share.component.html | 2 +- .../src/app/vault/collections.component.html | 2 +- .../app/vault/folder-add-edit.component.html | 6 +-- .../organization-filter.component.html | 1 - .../shared/vault-filter-shared.module.ts | 9 +++++ .../vault/vault-filter/vault-filter.module.ts | 10 +---- .../src/app/vault/vault-items.component.html | 2 +- .../src/app/vault/vault-items.component.ts | 4 ++ apps/web/src/app/vault/vault.component.html | 2 +- apps/web/tsconfig.json | 1 + .../organizations/manage/scim.component.html | 4 +- .../app/organizations/manage/sso.component.ts | 10 ++--- .../providers/manage/events.component.html | 2 +- .../providers/manage/people.component.html | 11 ------ .../manage/user-add-edit.component.html | 6 +-- .../app/providers/setup/setup.component.html | 3 -- .../src/components/add-edit.component.ts | 2 + .../src/components/attachments.component.ts | 4 ++ .../components/user-verification.component.ts | 20 ++-------- .../src/components/vault-items.component.ts | 3 ++ libs/angular/src/pipes/i18n.pipe.ts | 2 +- libs/common/src/abstractions/i18n.service.ts | 2 +- libs/common/src/services/i18n.service.ts | 8 ++-- libs/components/src/badge/badge.directive.ts | 2 +- 97 files changed, 302 insertions(+), 301 deletions(-) rename {libs/angular/src => apps/web/src/app}/components/user-verification.component.html (100%) create mode 100644 apps/web/src/app/components/user-verification.component.ts delete mode 100644 apps/web/src/app/organizations/settings/organization-billing.component.ts diff --git a/apps/browser/src/popup/accounts/set-password.component.html b/apps/browser/src/popup/accounts/set-password.component.html index 517374578c2..9eb551bf269 100644 --- a/apps/browser/src/popup/accounts/set-password.component.html +++ b/apps/browser/src/popup/accounts/set-password.component.html @@ -39,13 +39,10 @@
-
diff --git a/apps/desktop/src/app/vault/folder-add-edit.component.html b/apps/desktop/src/app/vault/folder-add-edit.component.html index ea7303dceb2..4e9adec8172 100644 --- a/apps/desktop/src/app/vault/folder-add-edit.component.html +++ b/apps/desktop/src/app/vault/folder-add-edit.component.html @@ -47,17 +47,17 @@ class="danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/desktop/src/app/vault/generator.component.html b/apps/desktop/src/app/vault/generator.component.html index 6111bcc3c6a..bddaa7d343b 100644 --- a/apps/desktop/src/app/vault/generator.component.html +++ b/apps/desktop/src/app/vault/generator.component.html @@ -52,11 +52,11 @@ appStopClick appA11yTitle="{{ 'regenerateUsername' | i18n }}" (click)="regenerate()" - [disabled]="form.loading" + [disabled]="$any(form).loading" > diff --git a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html index 3663b69fc46..dbba1353c9d 100644 --- a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html @@ -112,7 +112,6 @@ diff --git a/apps/desktop/src/app/vault/view.component.html b/apps/desktop/src/app/vault/view.component.html index 8b1b0445744..5b3896f68ed 100644 --- a/apps/desktop/src/app/vault/view.component.html +++ b/apps/desktop/src/app/vault/view.component.html @@ -64,16 +64,16 @@ appA11yTitle="{{ 'checkPassword' | i18n }}" (click)="checkPassword()" [appApiAction]="checkPasswordPromise" - [disabled]="checkPasswordBtn.loading" + [disabled]="$any(checkPasswordBtn).loading" > @@ -472,12 +472,12 @@ {{ attachment.sizeName }} diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index f2f28c73065..74b0670b3e7 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -16,6 +16,7 @@ } }, "angularCompilerOptions": { + "strictTemplates": true, "preserveWhitespaces": true }, "include": ["src", "../../libs/common/src/services/**/*.worker.ts"] diff --git a/apps/web/src/app/accounts/register.component.html b/apps/web/src/app/accounts/register.component.html index ed492a6bb89..8a68d32b039 100644 --- a/apps/web/src/app/accounts/register.component.html +++ b/apps/web/src/app/accounts/register.component.html @@ -55,7 +55,7 @@

- Start Your Teams
Teams
Enterprise Free Trial Now

diff --git a/apps/web/src/app/accounts/trial-initiation/vertical-stepper/vertical-stepper.component.ts b/apps/web/src/app/accounts/trial-initiation/vertical-stepper/vertical-stepper.component.ts index 2c66dae7be1..745bb5767fe 100644 --- a/apps/web/src/app/accounts/trial-initiation/vertical-stepper/vertical-stepper.component.ts +++ b/apps/web/src/app/accounts/trial-initiation/vertical-stepper/vertical-stepper.component.ts @@ -1,5 +1,7 @@ import { CdkStepper } from "@angular/cdk/stepper"; -import { Component, Input } from "@angular/core"; +import { Component, Input, QueryList } from "@angular/core"; + +import { VerticalStep } from "./vertical-step.component"; @Component({ selector: "app-vertical-stepper", @@ -7,6 +9,8 @@ import { Component, Input } from "@angular/core"; providers: [{ provide: CdkStepper, useExisting: VerticalStepperComponent }], }) export class VerticalStepperComponent extends CdkStepper { + readonly steps: QueryList; + @Input() activeClass = "active"; diff --git a/apps/web/src/app/accounts/two-factor.component.html b/apps/web/src/app/accounts/two-factor.component.html index 835fd12e785..94066d684b1 100644 --- a/apps/web/src/app/accounts/two-factor.component.html +++ b/apps/web/src/app/accounts/two-factor.component.html @@ -80,7 +80,7 @@
- +
{{ "reinstateSubscription" | i18n }} @@ -113,8 +113,8 @@ @@ -143,7 +143,7 @@ class="btn btn-outline-danger btn-submit" (click)="removeSponsorship()" [appApiAction]="removeSponsorshipPromise" - [disabled]="removeSponsorshipBtn.loading" + [disabled]="$any(removeSponsorshipBtn).loading" *ngIf="isSponsoredSubscription" > @@ -230,7 +230,7 @@ class="btn btn-outline-danger btn-submit ml-1" (click)="cancel()" [appApiAction]="cancelPromise" - [disabled]="cancelBtn.loading" + [disabled]="$any(cancelBtn).loading" *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel" > diff --git a/apps/web/src/app/organizations/components/access-selector/access-selector.component.html b/apps/web/src/app/organizations/components/access-selector/access-selector.component.html index 10390b3e053..cc4d1d7fa78 100644 --- a/apps/web/src/app/organizations/components/access-selector/access-selector.component.html +++ b/apps/web/src/app/organizations/components/access-selector/access-selector.component.html @@ -53,11 +53,13 @@
{{ item.labelName }} - + {{ "invited" | i18n }}
-
{{ item.email }}
+
+ {{ $any(item).email }} +

@@ -110,11 +112,11 @@ - {{ item.role | userType: "-" }} + {{ $any(item).role | userType: "-" }} - {{ item.viaGroupName ?? "-" }} + {{ $any(item).viaGroupName ?? "-" }} diff --git a/apps/web/src/app/organizations/manage/collection-add-edit.component.html b/apps/web/src/app/organizations/manage/collection-add-edit.component.html index 97c973a4195..41368d589a7 100644 --- a/apps/web/src/app/organizations/manage/collection-add-edit.component.html +++ b/apps/web/src/app/organizations/manage/collection-add-edit.component.html @@ -81,7 +81,7 @@ @@ -140,17 +140,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/entity-events.component.html b/apps/web/src/app/organizations/manage/entity-events.component.html index 122d0b81888..b41e6f3ba16 100644 --- a/apps/web/src/app/organizations/manage/entity-events.component.html +++ b/apps/web/src/app/organizations/manage/entity-events.component.html @@ -52,11 +52,11 @@ type="button" class="btn btn-sm btn-outline-primary ml-3" (click)="loadEvents(true)" - [disabled]="loaded && refreshBtn.loading" + [disabled]="loaded && $any(refreshBtn).loading" > {{ "refresh" | i18n }} @@ -101,7 +101,7 @@ type="button" class="btn btn-block btn-link btn-submit" (click)="loadEvents(false)" - [disabled]="loaded && moreBtn.loading" + [disabled]="loaded && $any(moreBtn).loading" *ngIf="continuationToken" > diff --git a/apps/web/src/app/organizations/manage/events.component.html b/apps/web/src/app/organizations/manage/events.component.html index a7468bcf378..e2c03f0dde5 100644 --- a/apps/web/src/app/organizations/manage/events.component.html +++ b/apps/web/src/app/organizations/manage/events.component.html @@ -95,7 +95,7 @@ bitButton buttonType="primary" (click)="loadEvents(false)" - [disabled]="loaded && moreBtn.loading" + [disabled]="loaded && $any(moreBtn).loading" *ngIf="continuationToken" > @@ -132,7 +132,7 @@ type="checkbox" [(ngModel)]="c.hidePasswords" name="Collection[{{ i }}].HidePasswords" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -140,7 +140,7 @@ type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{ i }}].ReadOnly" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -164,17 +164,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/user-add-edit.component.html b/apps/web/src/app/organizations/manage/user-add-edit.component.html index 7d15eaa22f3..458d1d0bab3 100644 --- a/apps/web/src/app/organizations/manage/user-add-edit.component.html +++ b/apps/web/src/app/organizations/manage/user-add-edit.component.html @@ -341,7 +341,7 @@ @@ -354,7 +354,7 @@ type="checkbox" [(ngModel)]="c.hidePasswords" name="Collection[{{ i }}].HidePasswords" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -362,7 +362,7 @@ type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{ i }}].ReadOnly" - [disabled]="!c.checked" + [disabled]="!$any(c).checked" /> @@ -416,17 +416,17 @@ class="btn btn-outline-danger" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" [appApiAction]="deletePromise" > diff --git a/apps/web/src/app/organizations/manage/user-groups.component.html b/apps/web/src/app/organizations/manage/user-groups.component.html index 347125e9825..217af6ea44b 100644 --- a/apps/web/src/app/organizations/manage/user-groups.component.html +++ b/apps/web/src/app/organizations/manage/user-groups.component.html @@ -34,7 +34,7 @@ diff --git a/apps/web/src/app/organizations/policies/policies.component.ts b/apps/web/src/app/organizations/policies/policies.component.ts index 0a316f5597d..717e35ac968 100644 --- a/apps/web/src/app/organizations/policies/policies.component.ts +++ b/apps/web/src/app/organizations/policies/policies.component.ts @@ -29,7 +29,7 @@ export class PoliciesComponent implements OnInit { organization: Organization; private orgPolicies: PolicyResponse[]; - private policiesEnabledMap: Map = new Map(); + protected policiesEnabledMap: Map = new Map(); constructor( private route: ActivatedRoute, diff --git a/apps/web/src/app/organizations/settings/organization-billing.component.ts b/apps/web/src/app/organizations/settings/organization-billing.component.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/web/src/app/organizations/vault/add-edit.component.ts b/apps/web/src/app/organizations/vault/add-edit.component.ts index e8138b1abc7..27a2b9a11dd 100644 --- a/apps/web/src/app/organizations/vault/add-edit.component.ts +++ b/apps/web/src/app/organizations/vault/add-edit.component.ts @@ -18,7 +18,6 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; import { CipherData } from "@bitwarden/common/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/models/domain/cipher"; -import { Organization } from "@bitwarden/common/models/domain/organization"; import { CipherCreateRequest } from "@bitwarden/common/models/request/cipher-create.request"; import { CipherRequest } from "@bitwarden/common/models/request/cipher.request"; @@ -29,7 +28,6 @@ import { AddEditComponent as BaseAddEditComponent } from "../../vault/add-edit.c templateUrl: "../../vault/add-edit.component.html", }) export class AddEditComponent extends BaseAddEditComponent { - organization: Organization; originalCipher: Cipher = null; constructor( diff --git a/apps/web/src/app/organizations/vault/vault-items.component.ts b/apps/web/src/app/organizations/vault/vault-items.component.ts index 670217a724a..044b3d6d752 100644 --- a/apps/web/src/app/organizations/vault/vault-items.component.ts +++ b/apps/web/src/app/organizations/vault/vault-items.component.ts @@ -12,7 +12,6 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; -import { Organization } from "@bitwarden/common/models/domain/organization"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { VaultItemsComponent as BaseVaultItemsComponent } from "../../vault/vault-items.component"; @@ -24,9 +23,6 @@ import { VaultItemsComponent as BaseVaultItemsComponent } from "../../vault/vaul export class VaultItemsComponent extends BaseVaultItemsComponent { @Output() onEventsClicked = new EventEmitter(); - organization: Organization; - accessEvents = false; - protected allCiphers: CipherView[] = []; constructor( @@ -86,6 +82,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent { async search(timeout: number = null) { await super.search(timeout, this.allCiphers); } + events(c: CipherView) { this.onEventsClicked.emit(c); } diff --git a/apps/web/src/app/organizations/vault/vault.component.html b/apps/web/src/app/organizations/vault/vault.component.html index dcc20913b9b..7a75ce01c42 100644 --- a/apps/web/src/app/organizations/vault/vault.component.html +++ b/apps/web/src/app/organizations/vault/vault.component.html @@ -19,7 +19,7 @@

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