diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 247107e87e8..772c7209e20 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -123,7 +123,6 @@ ./libs/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts ./libs/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts ./libs/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts -./libs/common/src/importers/chromeCsvImporter.ts ./libs/common/src/importers/meldiumCsvImporter.ts ./libs/common/src/importers/safariCsvImporter.ts ./libs/common/src/importers/msecureCsvImporter.ts diff --git a/apps/web/src/app/organizations/settings/image-subscription-hidden.component.ts b/apps/web/src/app/organizations/settings/image-subscription-hidden.component.ts deleted file mode 100644 index ff2a639e398..00000000000 --- a/apps/web/src/app/organizations/settings/image-subscription-hidden.component.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from "@angular/core"; - -// Component is used so that the SVG can embed CSS color variables -@Component({ - selector: "app-image-org-subscription-hidden", - templateUrl: "image-subscription-hidden.component.svg", -}) -export class ImageSubscriptionHiddenComponent {} diff --git a/apps/web/src/app/organizations/settings/organization-subscription.component.html b/apps/web/src/app/organizations/settings/organization-subscription.component.html index e54aa470187..2b5f50a8b62 100644 --- a/apps/web/src/app/organizations/settings/organization-subscription.component.html +++ b/apps/web/src/app/organizations/settings/organization-subscription.component.html @@ -18,7 +18,7 @@
- +

{{ "billingManagedByProvider" | i18n: this.userOrg.providerName }}

{{ "billingContactProviderForAssistance" | i18n }}

diff --git a/apps/web/src/app/organizations/settings/organization-subscription.component.ts b/apps/web/src/app/organizations/settings/organization-subscription.component.ts index fe29a38a96d..dbcdfd907ae 100644 --- a/apps/web/src/app/organizations/settings/organization-subscription.component.ts +++ b/apps/web/src/app/organizations/settings/organization-subscription.component.ts @@ -21,6 +21,7 @@ import { OrganizationSubscriptionResponse } from "@bitwarden/common/models/respo import { BillingSyncKeyComponent } from "../../settings/billing-sync-key.component"; import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component"; +import { SubscriptionHiddenIcon } from "./subscription-hidden.icon"; @Component({ selector: "app-org-subscription", @@ -58,6 +59,8 @@ export class OrganizationSubscriptionComponent implements OnInit { billingSyncKeyViewContainerRef: ViewContainerRef; billingSyncKeyRef: [ModalRef, BillingSyncKeyComponent]; + subscriptionHiddenIcon = SubscriptionHiddenIcon; + constructor( private apiService: ApiService, private platformUtilsService: PlatformUtilsService, diff --git a/apps/web/src/app/organizations/settings/image-subscription-hidden.component.svg b/apps/web/src/app/organizations/settings/subscription-hidden.icon.ts similarity index 90% rename from apps/web/src/app/organizations/settings/image-subscription-hidden.component.svg rename to apps/web/src/app/organizations/settings/subscription-hidden.icon.ts index a45a3deccec..3745747eceb 100644 --- a/apps/web/src/app/organizations/settings/image-subscription-hidden.component.svg +++ b/apps/web/src/app/organizations/settings/subscription-hidden.icon.ts @@ -1,20 +1,24 @@ +import { svgIcon } from "@bitwarden/components"; + +export const SubscriptionHiddenIcon = svgIcon` - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + +`; diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index c68741b5a2b..a8a01714ac2 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -63,7 +63,6 @@ import { BillingSyncApiKeyComponent } from "../organizations/settings/billing-sy import { ChangePlanComponent } from "../organizations/settings/change-plan.component"; import { DeleteOrganizationComponent } from "../organizations/settings/delete-organization.component"; import { DownloadLicenseComponent } from "../organizations/settings/download-license.component"; -import { ImageSubscriptionHiddenComponent as OrgSubscriptionHiddenComponent } from "../organizations/settings/image-subscription-hidden.component"; import { OrganizationBillingComponent } from "../organizations/settings/organization-billing.component"; import { OrganizationSubscriptionComponent } from "../organizations/settings/organization-subscription.component"; import { SettingsComponent as OrgSettingComponent } from "../organizations/settings/settings.component"; @@ -242,7 +241,6 @@ import { SharedModule } from "."; OrgSettingComponent, OrgToolsComponent, OrgTwoFactorSetupComponent, - OrgSubscriptionHiddenComponent, OrgUnsecuredWebsitesReportComponent, OrgUserAddEditComponent, OrgUserConfirmComponent, diff --git a/libs/common/spec/importers/chrome-csv-importer.spec.ts b/libs/common/spec/importers/chrome-csv-importer.spec.ts new file mode 100644 index 00000000000..6b44aacc833 --- /dev/null +++ b/libs/common/spec/importers/chrome-csv-importer.spec.ts @@ -0,0 +1,72 @@ +import { ChromeCsvImporter as Importer } from "@bitwarden/common/importers/chrome-csv-importer"; +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 { data as androidData } from "./testData/chromeCsv/android-data.csv"; +import { data as simplePasswordData } from "./testData/chromeCsv/simple-password-data.csv"; + +const CipherData = [ + { + title: "should parse app name", + csv: androidData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "com.xyz.example.app.android", + login: Object.assign(new LoginView(), { + username: "username@example.com", + password: "Qh6W4Wz55YGFNU", + uris: [ + Object.assign(new LoginUriView(), { + uri: "android://N2H9MndUUUt3JuQSWAKexOU9oJLJeHR4nyUGac5E1TXKppkY7xtdRl6l8vKo1hQWCqAEy4gsNLUBIbVxpdmhOP==@com.xyz.example.app.android/", + }), + ], + }), + notes: null, + type: 1, + }), + }, + { + title: "should parse password", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "www.example.com", + login: Object.assign(new LoginView(), { + username: "username@example.com", + password: "wpC9qFvsbWQK5Z", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://www.example.com/", + }), + ], + }), + notes: null, + type: 1, + }), + }, +]; + +describe("Chrome CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + if (Object.prototype.hasOwnProperty.call(data.expected, property)) { + expect(Object.prototype.hasOwnProperty.call(cipher, property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/libs/common/spec/importers/testData/chromeCsv/android-data.csv.ts b/libs/common/spec/importers/testData/chromeCsv/android-data.csv.ts new file mode 100644 index 00000000000..7b4b18094ae --- /dev/null +++ b/libs/common/spec/importers/testData/chromeCsv/android-data.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password +,android://N2H9MndUUUt3JuQSWAKexOU9oJLJeHR4nyUGac5E1TXKppkY7xtdRl6l8vKo1hQWCqAEy4gsNLUBIbVxpdmhOP==@com.xyz.example.app.android/,username@example.com,Qh6W4Wz55YGFNU`; diff --git a/libs/common/spec/importers/testData/chromeCsv/simple-password-data.csv.ts b/libs/common/spec/importers/testData/chromeCsv/simple-password-data.csv.ts new file mode 100644 index 00000000000..9ae3e9ebe14 --- /dev/null +++ b/libs/common/spec/importers/testData/chromeCsv/simple-password-data.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password +www.example.com,https://www.example.com/,username@example.com,wpC9qFvsbWQK5Z`; diff --git a/libs/common/src/importers/chromeCsvImporter.ts b/libs/common/src/importers/chrome-csv-importer.ts similarity index 74% rename from libs/common/src/importers/chromeCsvImporter.ts rename to libs/common/src/importers/chrome-csv-importer.ts index 085b8cd28aa..0208a29b73b 100644 --- a/libs/common/src/importers/chromeCsvImporter.ts +++ b/libs/common/src/importers/chrome-csv-importer.ts @@ -4,6 +4,8 @@ import { BaseImporter } from "./baseImporter"; import { Importer } from "./importer"; export class ChromeCsvImporter extends BaseImporter implements Importer { + private androidPatternRegex = new RegExp("^android:\\/\\/.*(?<=@)(.*)(?=\\/)"); + parse(data: string): Promise { const result = new ImportResult(); const results = this.parseCsv(data, true); @@ -14,7 +16,11 @@ export class ChromeCsvImporter extends BaseImporter implements Importer { results.forEach((value) => { const cipher = this.initLoginCipher(); - cipher.name = this.getValueOrDefault(value.name, "--"); + let name = value.name; + if (!name && this.androidPatternRegex.test(value.url)) { + name = value.url.match(this.androidPatternRegex)[1]; + } + cipher.name = this.getValueOrDefault(name, "--"); cipher.login.username = this.getValueOrDefault(value.username); cipher.login.password = this.getValueOrDefault(value.password); cipher.login.uris = this.makeUriArray(value.url); diff --git a/libs/common/src/services/import.service.ts b/libs/common/src/services/import.service.ts index a1f76ff5f8d..e719673bda7 100644 --- a/libs/common/src/services/import.service.ts +++ b/libs/common/src/services/import.service.ts @@ -22,7 +22,7 @@ import { BitwardenPasswordProtectedImporter } from "../importers/bitwardenPasswo import { BlackBerryCsvImporter } from "../importers/blackBerryCsvImporter"; import { BlurCsvImporter } from "../importers/blurCsvImporter"; import { ButtercupCsvImporter } from "../importers/buttercupCsvImporter"; -import { ChromeCsvImporter } from "../importers/chromeCsvImporter"; +import { ChromeCsvImporter } from "../importers/chrome-csv-importer"; import { ClipperzHtmlImporter } from "../importers/clipperzHtmlImporter"; import { CodebookCsvImporter } from "../importers/codebookCsvImporter"; import { DashlaneCsvImporter } from "../importers/dashlaneImporters/dashlaneCsvImporter";