1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-13 15:03:26 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Victoria League
2024-10-08 09:24:16 -04:00
committed by GitHub
57 changed files with 669 additions and 715 deletions

34
.github/workflows/locales-lint.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
---
name: Locales lint for Crowdin
on:
pull_request:
branches-ignore:
- 'l10n_master'
- 'cf-pages'
paths:
- '**/messages.json'
jobs:
lint:
name: Lint
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Checkout base branch repo
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- name: Install dependencies
run: npm ci
- name: Compare
run: |
npm run test:locales
if [ $? -eq 0 ]; then
echo "Lint check successful."
else
echo "Lint check failed."
exit 1
fi

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2024.10.0",
"version": "2024.10.1",
"scripts": {
"build": "cross-env MANIFEST_VERSION=3 webpack",
"build:mv2": "webpack",

View File

@@ -42,7 +42,7 @@ describe("AutoSubmitLoginBackground", () => {
const validIpdUrl1 = "https://example.com";
const validIpdUrl2 = "https://subdomain.example3.com";
const validAutoSubmitHost = "some-valid-url.com";
const validAutoSubmitUrl = `https://${validAutoSubmitHost}/?autofill=1`;
const validAutoSubmitUrl = `https://${validAutoSubmitHost}/#autosubmit=1`;
beforeEach(() => {
logService = mock<LogService>();
@@ -122,7 +122,7 @@ describe("AutoSubmitLoginBackground", () => {
await autoSubmitLoginBackground.init();
});
it("sets up the auto-submit workflow when the web request occurs in the main frame and the destination URL contains a valid auto-fill param", () => {
it("sets up the auto-submit workflow when the web request occurs in the main frame and the destination URL contains a valid auto-fill hash", () => {
triggerWebRequestOnBeforeRequestEvent(webRequestDetails);
expect(autoSubmitLoginBackground["currentAutoSubmitHostData"]).toStrictEqual({
@@ -226,7 +226,7 @@ describe("AutoSubmitLoginBackground", () => {
it("disables the auto-submit workflow if a web request is initiated after the auto-submit route has been visited", () => {
webRequestDetails.url = `https://${validAutoSubmitHost}`;
webRequestDetails.initiator = `https://${validAutoSubmitHost}?autofill=1`;
webRequestDetails.initiator = `https://${validAutoSubmitHost}#autosubmit=1`;
triggerWebRequestOnBeforeRequestEvent(webRequestDetails);

View File

@@ -234,7 +234,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
) => {
if (
details.tabId === this.currentAutoSubmitHostData.tabId &&
this.urlContainsAutoFillParam(details.url)
this.urlContainsAutoSubmitHash(details.url)
) {
this.injectAutoSubmitLoginScript(details.tabId).catch((error) =>
this.logService.error(error),
@@ -277,7 +277,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
private handleWebRequestOnBeforeRedirect = (
details: chrome.webRequest.WebRedirectionResponseDetails,
) => {
if (this.isRequestInMainFrame(details) && this.urlContainsAutoFillParam(details.redirectUrl)) {
if (this.isRequestInMainFrame(details) && this.urlContainsAutoSubmitHash(details.redirectUrl)) {
this.validAutoSubmitHosts.add(this.getUrlHost(details.redirectUrl));
this.validAutoSubmitHosts.add(this.getUrlHost(details.url));
}
@@ -369,7 +369,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
/**
* Determines if the provided URL is a valid auto-submit host. If the request is occurring
* in the main frame, we will check for the presence of the `autofill=1` query parameter.
* in the main frame, we will check for the presence of the `autosubmit=1` uri hash.
* If the request is occurring in a sub frame, the main frame URL should be set as a
* valid auto-submit host and can be used to validate the request.
*
@@ -382,7 +382,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
) => {
if (this.isRequestInMainFrame(details)) {
return !!(
this.urlContainsAutoFillParam(details.url) ||
this.urlContainsAutoSubmitHash(details.url) ||
this.triggerAutoSubmitAfterRedirectOnSafari(details.url)
);
}
@@ -391,14 +391,14 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
};
/**
* Determines if the provided URL contains the `autofill=1` query parameter.
* Determines if the provided URL contains the `autosubmit=1` uri hash.
*
* @param url - The URL to check for the `autofill=1` query parameter.
* @param url - The URL to check for the `autosubmit=1` uri hash.
*/
private urlContainsAutoFillParam = (url: string) => {
private urlContainsAutoSubmitHash = (url: string) => {
try {
const urlObj = new URL(url);
return urlObj.search.indexOf("autofill=1") !== -1;
return urlObj.hash.indexOf("autosubmit=1") !== -1;
} catch {
return false;
}

View File

@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.10.0",
"version": "2024.10.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.10.0",
"version": "2024.10.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -1,6 +1,8 @@
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import type { BitwardenClient } from "@bitwarden/sdk-internal";
import { BrowserApi } from "../../browser/browser-api";
// https://stackoverflow.com/a/47880734
const supported = (() => {
try {
@@ -18,14 +20,34 @@ const supported = (() => {
return false;
})();
if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
import("./fallback");
// Manifest v3 does not support dynamic imports in the service worker.
if (BrowserApi.isManifestVersion(3)) {
if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
import("./fallback");
}
}
// Manifest v2 expects dynamic imports to prevent timing issues.
async function load() {
if (BrowserApi.isManifestVersion(3)) {
return;
}
if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
await import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
await import("./fallback");
}
}
/**
@@ -37,6 +59,8 @@ export class BrowserSdkClientFactory implements SdkClientFactory {
async createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
await load();
return Promise.resolve((globalThis as any).init_sdk(...args));
}
}

View File

@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
03100CAF291891F4008E14EF /* encrypt-worker.js in Resources */ = {isa = PBXBuildFile; fileRef = 03100CAE291891F4008E14EF /* encrypt-worker.js */; };
55BC93932CB4268A008CA4C6 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 55BC93922CB4268A008CA4C6 /* assets */; };
55E0374D2577FA6B00979016 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E0374C2577FA6B00979016 /* AppDelegate.swift */; };
55E037502577FA6B00979016 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55E0374E2577FA6B00979016 /* Main.storyboard */; };
55E037522577FA6B00979016 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E037512577FA6B00979016 /* ViewController.swift */; };
@@ -54,6 +55,7 @@
/* Begin PBXFileReference section */
03100CAE291891F4008E14EF /* encrypt-worker.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = "encrypt-worker.js"; path = "../../../build/encrypt-worker.js"; sourceTree = "<group>"; };
5508DD7926051B5900A85C58 /* libswiftAppKit.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftAppKit.tbd; path = usr/lib/swift/libswiftAppKit.tbd; sourceTree = SDKROOT; };
55BC93922CB4268A008CA4C6 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = ../../../build/assets; sourceTree = "<group>"; };
55E037482577FA6B00979016 /* desktop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktop.app; sourceTree = BUILT_PRODUCTS_DIR; };
55E0374B2577FA6B00979016 /* desktop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = desktop.entitlements; sourceTree = "<group>"; };
55E0374C2577FA6B00979016 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -152,6 +154,7 @@
55E0376F2577FA6F00979016 /* Resources */ = {
isa = PBXGroup;
children = (
55BC93922CB4268A008CA4C6 /* assets */,
03100CAE291891F4008E14EF /* encrypt-worker.js */,
55E037702577FA6F00979016 /* popup */,
55E037712577FA6F00979016 /* background.js */,
@@ -270,6 +273,7 @@
55E0377A2577FA6F00979016 /* background.js in Resources */,
55E037792577FA6F00979016 /* popup in Resources */,
03100CAF291891F4008E14EF /* encrypt-worker.js in Resources */,
55BC93932CB4268A008CA4C6 /* assets in Resources */,
55E0377C2577FA6F00979016 /* notification in Resources */,
55E0377E2577FA6F00979016 /* vendor.js in Resources */,
55E0377D2577FA6F00979016 /* content in Resources */,

View File

@@ -316,6 +316,8 @@ const mainConfig = {
},
output: {
filename: "[name].js",
chunkFilename: "assets/[name].js",
webassemblyModuleFilename: "assets/[modulehash].wasm",
path: path.resolve(__dirname, "build"),
clean: true,
},

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/cli",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.9.1",
"version": "2024.10.0",
"keywords": [
"bitwarden",
"password",

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.9.2",
"version": "2024.10.0",
"keywords": [
"bitwarden",
"password",

View File

@@ -1,26 +1,18 @@
{
"name": "@bitwarden/desktop",
"version": "2024.9.2",
"version": "2024.10.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
"version": "2024.9.2",
"version": "2024.10.0",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
"argon2": "0.40.1"
}
},
"../desktop_native/napi": {
"name": "@bitwarden/desktop-napi",
"version": "0.1.0",
"license": "GPL-3.0",
"devDependencies": {
"@napi-rs/cli": "2.16.2"
}
},
"../desktop_native/napi": {
"version": "0.1.0",
"license": "GPL-3.0",

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.9.2",
"version": "2024.10.0",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
"version": "2024.10.0",
"version": "2024.10.1",
"scripts": {
"build:oss": "webpack",
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",

View File

@@ -16,38 +16,13 @@
"all" | i18n
}}</label>
</th>
<!-- Organization vault -->
<th
*ngIf="showAdminActions"
bitCell
bitSortable="name"
[fn]="sortByName"
[class]="showExtraColumn ? 'lg:tw-w-3/5' : 'tw-w-full'"
>
{{ "name" | i18n }}
</th>
<!-- Individual vault -->
<th
*ngIf="!showAdminActions"
bitCell
[class]="showExtraColumn ? 'lg:tw-w-3/5' : 'tw-w-full'"
>
{{ "name" | i18n }}
</th>
<th bitCell [class]="showExtraColumn ? 'lg:tw-w-3/5' : 'tw-w-full'">{{ "name" | i18n }}</th>
<th bitCell *ngIf="showOwner" class="tw-hidden tw-w-2/5 lg:tw-table-cell">
{{ "owner" | i18n }}
</th>
<th bitCell class="tw-w-2/5" *ngIf="showCollections">{{ "collections" | i18n }}</th>
<th bitCell bitSortable="groups" [fn]="sortByGroups" class="tw-w-2/5" *ngIf="showGroups">
{{ "groups" | i18n }}
</th>
<th
bitCell
bitSortable="permissions"
[fn]="sortByPermissions"
class="tw-w-2/5"
*ngIf="showPermissionsColumn"
>
<th bitCell class="tw-w-2/5" *ngIf="showGroups">{{ "groups" | i18n }}</th>
<th bitCell class="tw-w-2/5" *ngIf="showPermissionsColumn">
{{ "permission" | i18n }}
</th>
<th bitCell class="tw-w-12 tw-text-right">

View File

@@ -1,19 +1,14 @@
import { SelectionModel } from "@angular/cdk/collections";
import { Component, EventEmitter, inject, Input, Output } from "@angular/core";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { CollectionAdminView, Unassigned } from "@bitwarden/admin-console/common";
import { Unassigned } from "@bitwarden/admin-console/common";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { TableDataSource } from "@bitwarden/components";
import { GroupView } from "../../../admin-console/organizations/core";
import {
CollectionPermission,
convertToPermission,
} from "./../../../admin-console/organizations/shared/components/access-selector/access-selector.models";
import { VaultItem } from "./vault-item";
import { VaultItemEvent } from "./vault-item-event";
@@ -30,7 +25,6 @@ const MaxSelectionCount = 500;
// changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VaultItemsComponent {
protected i18nService = inject(I18nService);
protected RowHeight = RowHeight;
@Input() disabled: boolean;
@@ -203,7 +197,7 @@ export class VaultItemsComponent {
private refreshItems() {
const collections: VaultItem[] = this.collections.map((collection) => ({ collection }));
const ciphers: VaultItem[] = this.ciphers.map((cipher) => ({ cipher }));
let items: VaultItem[] = [].concat(collections).concat(ciphers);
const items: VaultItem[] = [].concat(collections).concat(ciphers);
this.selection.clear();
@@ -214,11 +208,6 @@ export class VaultItemsComponent {
(item.collection !== undefined && item.collection.id !== Unassigned),
);
// Apply sorting only for organization vault
if (this.showAdminActions) {
items = items.sort(this.sortByGroups);
}
this.dataSource.data = items;
}
@@ -304,112 +293,6 @@ export class VaultItemsComponent {
return false;
}
/**
* Sorts VaultItems, grouping collections before ciphers, and sorting each group alphabetically by name.
*/
protected sortByName = (a: VaultItem, b: VaultItem) => {
const getName = (item: VaultItem) => item.collection?.name || item.cipher?.name;
// First, sort collections before ciphers
if (a.collection && !b.collection) {
return -1;
}
if (!a.collection && b.collection) {
return 1;
}
return getName(a).localeCompare(getName(b));
};
/**
* Sorts VaultItems based on group names
*/
protected sortByGroups = (a: VaultItem, b: VaultItem): number => {
const getGroupNames = (item: VaultItem): string => {
if (item.collection instanceof CollectionAdminView) {
return item.collection.groups
.map((group) => this.getGroupName(group.id))
.filter(Boolean)
.join(",");
}
return "";
};
const aGroupNames = getGroupNames(a);
const bGroupNames = getGroupNames(b);
if (aGroupNames.length !== bGroupNames.length) {
return bGroupNames.length - aGroupNames.length;
}
return aGroupNames.localeCompare(bGroupNames);
};
/**
* Sorts VaultItems based on their permissions, with higher permissions taking precedence.
* If permissions are equal, it falls back to sorting by name.
*/
protected sortByPermissions = (a: VaultItem, b: VaultItem): number => {
const getPermissionPriority = (item: VaultItem): number => {
if (item.collection instanceof CollectionAdminView) {
const permission = this.getCollectionPermission(item.collection);
switch (permission) {
case CollectionPermission.Manage:
return 5;
case CollectionPermission.Edit:
return 4;
case CollectionPermission.EditExceptPass:
return 3;
case CollectionPermission.View:
return 2;
case CollectionPermission.ViewExceptPass:
return 1;
case "NoAccess":
return 0;
}
}
return -1;
};
const priorityA = getPermissionPriority(a);
const priorityB = getPermissionPriority(b);
// Higher priority first
if (priorityA !== priorityB) {
return priorityB - priorityA;
}
return this.sortByName(a, b);
};
/**
* Default sorting function for vault items.
* Sorts by: 1. Collections before ciphers
* 2. Highest permission first
* 3. Alphabetical order of collections and ciphers
*/
private defaultSort = (a: VaultItem, b: VaultItem) => {
// First, sort collections before ciphers
if (a.collection && !b.collection) {
return -1;
}
if (!a.collection && b.collection) {
return 1;
}
// Next, sort by permissions
const permissionSort = this.sortByPermissions(a, b);
if (permissionSort !== 0) {
return permissionSort;
}
// Finally, sort by name
return this.sortByName(a, b);
};
private hasPersonalItems(): boolean {
return this.selection.selected.some(({ cipher }) => cipher?.organizationId === null);
}
@@ -423,24 +306,4 @@ export class VaultItemsComponent {
private getUniqueOrganizationIds(): Set<string> {
return new Set(this.selection.selected.flatMap((i) => i.cipher?.organizationId ?? []));
}
private getGroupName(groupId: string): string | undefined {
return this.allGroups.find((g) => g.id === groupId)?.name;
}
private getCollectionPermission(
collection: CollectionAdminView,
): CollectionPermission | "NoAccess" {
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
if (collection.id == Unassigned && organization?.canEditUnassignedCiphers) {
return CollectionPermission.Edit;
}
if (collection.assigned) {
return convertToPermission(collection);
}
return "NoAccess";
}
}

View File

@@ -0,0 +1,119 @@
import { TestBed } from "@angular/core/testing";
import { BehaviorSubject } from "rxjs";
import { CollectionAdminService } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service";
import { AdminConsoleCipherFormConfigService } from "./admin-console-cipher-form-config.service";
describe("AdminConsoleCipherFormConfigService", () => {
let adminConsoleConfigService: AdminConsoleCipherFormConfigService;
const cipherId = "333-444-555" as CipherId;
const testOrg = { id: "333-44-55", name: "Test Org", canEditAllCiphers: false };
const organization$ = new BehaviorSubject<Organization>(testOrg as Organization);
const getCipherAdmin = jest.fn().mockResolvedValue(null);
const getCipher = jest.fn().mockResolvedValue(null);
beforeEach(async () => {
getCipherAdmin.mockClear();
getCipher.mockClear();
getCipher.mockResolvedValue({ id: cipherId, name: "Test Cipher - (non-admin)" });
getCipherAdmin.mockResolvedValue({ id: cipherId, name: "Test Cipher - (admin)" });
await TestBed.configureTestingModule({
providers: [
AdminConsoleCipherFormConfigService,
{ provide: OrganizationService, useValue: { get$: () => organization$ } },
{ provide: CipherService, useValue: { get: getCipher } },
{ provide: CollectionAdminService, useValue: { getAll: () => Promise.resolve([]) } },
{
provide: RoutedVaultFilterService,
useValue: { filter$: new BehaviorSubject({ organizationId: testOrg.id }) },
},
{ provide: ApiService, useValue: { getCipherAdmin } },
],
});
});
describe("buildConfig", () => {
it("sets individual attributes", async () => {
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
const { folders, hideIndividualVaultFields } = await adminConsoleConfigService.buildConfig(
"add",
cipherId,
);
expect(folders).toEqual([]);
expect(hideIndividualVaultFields).toBe(true);
});
it("sets mode based on passed mode", async () => {
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
const { mode } = await adminConsoleConfigService.buildConfig("edit", cipherId);
expect(mode).toBe("edit");
});
it("sets admin flag based on `canEditAllCiphers`", async () => {
// Disable edit all ciphers on org
testOrg.canEditAllCiphers = false;
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
let result = await adminConsoleConfigService.buildConfig("add", cipherId);
expect(result.admin).toBe(false);
// Enable edit all ciphers on org
testOrg.canEditAllCiphers = true;
result = await adminConsoleConfigService.buildConfig("add", cipherId);
expect(result.admin).toBe(true);
});
it("sets `allowPersonalOwnership` to false", async () => {
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
const result = await adminConsoleConfigService.buildConfig("clone", cipherId);
expect(result.allowPersonalOwnership).toBe(false);
});
describe("getCipher", () => {
it("retrieves the cipher from the cipher service", async () => {
testOrg.canEditAllCiphers = false;
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
const result = await adminConsoleConfigService.buildConfig("clone", cipherId);
expect(getCipher).toHaveBeenCalledWith(cipherId);
expect(result.originalCipher.name).toBe("Test Cipher - (non-admin)");
// Admin service not needed when cipher service can return the cipher
expect(getCipherAdmin).not.toHaveBeenCalled();
});
it("retrieves the cipher from the admin service", async () => {
getCipher.mockResolvedValueOnce(null);
getCipherAdmin.mockResolvedValue({ id: cipherId, name: "Test Cipher - (admin)" });
adminConsoleConfigService = TestBed.inject(AdminConsoleCipherFormConfigService);
await adminConsoleConfigService.buildConfig("add", cipherId);
expect(getCipherAdmin).toHaveBeenCalledWith(cipherId);
expect(getCipher).toHaveBeenCalledWith(cipherId);
});
});
});
});

View File

@@ -0,0 +1,99 @@
import { inject, Injectable } from "@angular/core";
import { combineLatest, filter, firstValueFrom, map, switchMap } from "rxjs";
import { CollectionAdminService } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import {
CipherFormConfig,
CipherFormConfigService,
CipherFormMode,
} from "../../../../../../../libs/vault/src/cipher-form/abstractions/cipher-form-config.service";
import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service";
/** Admin Console implementation of the `CipherFormConfigService`. */
@Injectable()
export class AdminConsoleCipherFormConfigService implements CipherFormConfigService {
private organizationService: OrganizationService = inject(OrganizationService);
private cipherService: CipherService = inject(CipherService);
private routedVaultFilterService: RoutedVaultFilterService = inject(RoutedVaultFilterService);
private collectionAdminService: CollectionAdminService = inject(CollectionAdminService);
private apiService: ApiService = inject(ApiService);
private organizationId$ = this.routedVaultFilterService.filter$.pipe(
map((filter) => filter.organizationId),
filter((filter) => filter !== undefined),
);
private organization$ = this.organizationId$.pipe(
switchMap((organizationId) => this.organizationService.get$(organizationId)),
);
private editableCollections$ = this.organization$.pipe(
switchMap(async (org) => {
const collections = await this.collectionAdminService.getAll(org.id);
// Users that can edit all ciphers can implicitly add to / edit within any collection
if (org.canEditAllCiphers) {
return collections;
}
// The user is only allowed to add/edit items to assigned collections that are not readonly
return collections.filter((c) => c.assigned && !c.readOnly);
}),
);
async buildConfig(
mode: CipherFormMode,
cipherId?: CipherId,
cipherType?: CipherType,
): Promise<CipherFormConfig> {
const [organization, allCollections] = await firstValueFrom(
combineLatest([this.organization$, this.editableCollections$]),
);
const cipher = await this.getCipher(organization, cipherId);
const collections = allCollections.filter(
(c) => c.organizationId === organization.id && c.assigned && !c.readOnly,
);
return {
mode,
cipherType: cipher?.type ?? cipherType ?? CipherType.Login,
admin: organization.canEditAllCiphers ?? false,
allowPersonalOwnership: false,
originalCipher: cipher,
collections,
organizations: [organization], // only a single org is in context at a time
folders: [], // folders not applicable in the admin console
hideIndividualVaultFields: true,
};
}
private async getCipher(organization: Organization, id?: CipherId): Promise<Cipher | null> {
if (id == null) {
return Promise.resolve(null);
}
// Check to see if the user has direct access to the cipher
const cipherFromCipherService = await this.cipherService.get(id);
// If the organization doesn't allow admin/owners to edit all ciphers return the cipher
if (!organization.canEditAllCiphers && cipherFromCipherService != null) {
return cipherFromCipherService;
}
// Retrieve the cipher through the means of an admin
const cipherResponse = await this.apiService.getCipherAdmin(id);
cipherResponse.edit = true;
const cipherData = new CipherData(cipherResponse);
return new Cipher(cipherData);
}
}

View File

@@ -1,3 +1,4 @@
import { DialogRef } from "@angular/cdk/dialog";
import {
ChangeDetectorRef,
Component,
@@ -42,16 +43,17 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EventType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SyncService } from "@bitwarden/common/platform/sync";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
@@ -62,7 +64,12 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
import { DialogService, Icons, NoItemsModule, ToastService } from "@bitwarden/components";
import { CollectionAssignmentResult, PasswordRepromptService } from "@bitwarden/vault";
import {
CipherFormConfig,
CipherFormConfigService,
CollectionAssignmentResult,
PasswordRepromptService,
} from "@bitwarden/vault";
import { GroupService, GroupView } from "../../admin-console/organizations/core";
import { openEntityEventsDialog } from "../../admin-console/organizations/manage/entity-events.component";
@@ -75,6 +82,11 @@ import {
CollectionDialogTabType,
openCollectionDialog,
} from "../components/collection-dialog";
import {
VaultItemDialogComponent,
VaultItemDialogMode,
VaultItemDialogResult,
} from "../components/vault-item-dialog/vault-item-dialog.component";
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
import {
@@ -89,12 +101,6 @@ import {
All,
RoutedVaultFilterModel,
} from "../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
import {
openViewCipherDialog,
ViewCipherDialogCloseResult,
ViewCipherDialogResult,
ViewComponent,
} from "../individual-vault/view.component";
import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.component";
import { getNestedCollectionTree } from "../utils/collection-utils";
@@ -106,8 +112,8 @@ import {
} from "./bulk-collections-dialog";
import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component";
import { openOrgVaultCollectionsDialog } from "./collections.component";
import { AdminConsoleCipherFormConfigService } from "./services/admin-console-cipher-form-config.service";
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
const BroadcasterSubscriptionId = "OrgVaultComponent";
const SearchTextDebounceInterval = 200;
@@ -127,9 +133,12 @@ enum AddAccessStatusType {
VaultItemsModule,
SharedModule,
NoItemsModule,
ViewComponent,
],
providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService],
providers: [
RoutedVaultFilterService,
RoutedVaultFilterBridgeService,
{ provide: CipherFormConfigService, useClass: AdminConsoleCipherFormConfigService },
],
})
export class VaultComponent implements OnInit, OnDestroy {
protected Unassigned = Unassigned;
@@ -174,6 +183,8 @@ export class VaultComponent implements OnInit, OnDestroy {
private refresh$ = new BehaviorSubject<void>(null);
private destroy$ = new Subject<void>();
protected addAccessStatus$ = new BehaviorSubject<AddAccessStatusType>(0);
private extensionRefreshEnabled: boolean;
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
constructor(
private route: ActivatedRoute,
@@ -203,10 +214,15 @@ export class VaultComponent implements OnInit, OnDestroy {
private apiService: ApiService,
private collectionService: CollectionService,
private toastService: ToastService,
private accountService: AccountService,
private configService: ConfigService,
private cipherFormConfigService: CipherFormConfigService,
) {}
async ngOnInit() {
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
FeatureFlag.ExtensionRefresh,
);
this.trashCleanupWarning = this.i18nService.t(
this.platformUtilsService.isSelfHost()
? "trashCleanupWarningSelfHosted"
@@ -466,22 +482,27 @@ export class VaultComponent implements OnInit, OnDestroy {
firstSetup$
.pipe(
switchMap(() => this.route.queryParams),
// Only process the queryParams if the dialog is not open (only when extension refresh is enabled)
filter(() => this.vaultItemDialogRef == undefined || !this.extensionRefreshEnabled),
withLatestFrom(allCipherMap$, allCollections$, organization$),
switchMap(async ([qParams, allCiphersMap, allCollections]) => {
switchMap(async ([qParams, allCiphersMap]) => {
const cipherId = getCipherIdFromParams(qParams);
if (!cipherId) {
return;
}
const cipher = allCiphersMap[cipherId];
const cipherCollections = allCollections.filter((c) =>
cipher.collectionIds.includes(c.id),
);
if (cipher) {
if (qParams.action === "view") {
await this.viewCipher(cipher, cipherCollections);
let action = qParams.action;
// Default to "view" if extension refresh is enabled
if (action == null && this.extensionRefreshEnabled) {
action = "view";
}
if (action === "view") {
await this.viewCipherById(cipher);
} else {
await this.editCipherId(cipherId);
await this.editCipherId(cipher, false);
}
} else {
this.toastService.showToast({
@@ -730,12 +751,16 @@ export class VaultComponent implements OnInit, OnDestroy {
}
async addCipher(cipherType?: CipherType) {
if (this.extensionRefreshEnabled) {
return this.addCipherV2(cipherType);
}
let collections: CollectionView[] = [];
// Admins limited to only adding items to collections they have access to.
collections = await firstValueFrom(this.editableCollections$);
await this.editCipher(null, (comp) => {
await this.editCipher(null, false, (comp) => {
comp.type = cipherType || this.activeFilter.cipherType;
comp.collections = collections;
if (this.activeFilter.collectionId) {
@@ -744,20 +769,46 @@ export class VaultComponent implements OnInit, OnDestroy {
});
}
/** Opens the Add/Edit Dialog. Only to be used when the BrowserExtension feature flag is active */
async addCipherV2(cipherType?: CipherType) {
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
"add",
null,
cipherType,
);
const collectionId: CollectionId | undefined = this.activeFilter.collectionId as CollectionId;
cipherFormConfig.initialValues = {
organizationId: this.organization.id as OrganizationId,
collectionIds: collectionId ? [collectionId] : [],
};
await this.openVaultItemDialog("form", cipherFormConfig);
}
/**
* Edit the given cipher
* @param cipherView - The cipher to be edited
* @param cloneCipher - `true` when the cipher should be cloned.
* Used in place of the `additionalComponentParameters`, as
* the `editCipherIdV2` method has a differing implementation.
* @param defaultComponentParameters - A method that takes in an instance of
* the `AddEditComponent` to edit methods directly.
*/
async editCipher(
cipher: CipherView,
cloneCipher: boolean,
additionalComponentParameters?: (comp: AddEditComponent) => void,
) {
return this.editCipherId(cipher?.id, additionalComponentParameters);
return this.editCipherId(cipher, cloneCipher, additionalComponentParameters);
}
async editCipherId(
cipherId: string,
cipher: CipherView,
cloneCipher: boolean,
additionalComponentParameters?: (comp: AddEditComponent) => void,
) {
const cipher = await this.cipherService.get(cipherId);
// if cipher exists (cipher is null when new) and MP reprompt
// is on for this cipher, then show password reprompt
if (
cipher &&
cipher.reprompt !== 0 &&
@@ -768,10 +819,15 @@ export class VaultComponent implements OnInit, OnDestroy {
return;
}
if (this.extensionRefreshEnabled) {
await this.editCipherIdV2(cipher, cloneCipher);
return;
}
const defaultComponentParameters = (comp: AddEditComponent) => {
comp.organization = this.organization;
comp.organizationId = this.organization.id;
comp.cipherId = cipherId;
comp.cipherId = cipher.id;
comp.onSavedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
modal.close();
this.refresh();
@@ -807,46 +863,70 @@ export class VaultComponent implements OnInit, OnDestroy {
}
/**
* Takes a cipher and its assigned collections to opens dialog where it can be viewed.
* @param cipher - the cipher to view
* @param collections - the collections the cipher is assigned to
* Edit a cipher using the new AddEditCipherDialogV2 component.
* Only to be used behind the ExtensionRefresh feature flag.
*/
async viewCipher(cipher: CipherView, collections: CollectionView[] = []) {
private async editCipherIdV2(cipher: CipherView, cloneCipher: boolean) {
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
cloneCipher ? "clone" : "edit",
cipher.id as CipherId,
);
await this.openVaultItemDialog("form", cipherFormConfig, cipher);
}
/** Opens the view dialog for the given cipher unless password reprompt fails */
async viewCipherById(cipher: CipherView) {
if (!cipher) {
this.go({ cipherId: null, itemId: null });
return;
}
if (cipher.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) {
// didn't pass password prompt, so don't open the dialog
this.go({ cipherId: null, itemId: null });
if (
cipher &&
cipher.reprompt !== 0 &&
!(await this.passwordRepromptService.showPasswordPrompt())
) {
// Didn't pass password prompt, so don't open add / edit modal.
await this.go({ cipherId: null, itemId: null, action: null });
return;
}
const dialogRef = openViewCipherDialog(this.dialogService, {
data: {
cipher: cipher,
collections: collections,
disableEdit: !cipher.edit && !this.organization.canEditAllCiphers,
},
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
"edit",
cipher.id as CipherId,
cipher.type,
);
await this.openVaultItemDialog("view", cipherFormConfig, cipher);
}
/**
* Open the combined view / edit dialog for a cipher.
*/
async openVaultItemDialog(
mode: VaultItemDialogMode,
formConfig: CipherFormConfig,
cipher?: CipherView,
) {
const disableForm = cipher ? !cipher.edit && !this.organization.canEditAllCiphers : false;
// If the form is disabled, force the mode into `view`
const dialogMode = disableForm ? "view" : mode;
this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, {
mode: dialogMode,
formConfig,
disableForm,
});
// Wait for the dialog to close.
const result: ViewCipherDialogCloseResult = await lastValueFrom(dialogRef.closed);
// If the dialog was closed by clicking the edit button, navigate to open the edit dialog.
if (result?.action === ViewCipherDialogResult.Edited) {
this.go({ itemId: cipher.id, action: "edit" });
return;
}
const result = await lastValueFrom(this.vaultItemDialogRef.closed);
this.vaultItemDialogRef = undefined;
// If the dialog was closed by deleting the cipher, refresh the vault.
if (result?.action === ViewCipherDialogResult.Deleted) {
if (result === VaultItemDialogResult.Deleted || result === VaultItemDialogResult.Saved) {
this.refresh();
}
// Clear the query params when the view dialog closes
this.go({ cipherId: null, itemId: null, action: null });
// Clear the query params when the dialog closes
await this.go({ cipherId: null, itemId: null, action: null });
}
async cloneCipher(cipher: CipherView) {
@@ -867,7 +947,7 @@ export class VaultComponent implements OnInit, OnDestroy {
// Admins limited to only adding items to collections they have access to.
collections = await firstValueFrom(this.editableCollections$);
await this.editCipher(cipher, (comp) => {
await this.editCipher(cipher, true, (comp) => {
comp.cloneMode = true;
comp.collections = collections;
comp.collectionIds = cipher.collectionIds;

View File

@@ -673,6 +673,8 @@ export class CipherService implements CipherServiceAbstraction {
if (orgAdmin && cipher.organizationId != null) {
const request = new CipherCreateRequest(cipher);
response = await this.apiService.postCipherAdmin(request);
const data = new CipherData(response, cipher.collectionIds);
return new Cipher(data);
} else if (cipher.collectionIds != null) {
const request = new CipherCreateRequest(cipher);
response = await this.apiService.postCipherCreate(request);
@@ -697,6 +699,8 @@ export class CipherService implements CipherServiceAbstraction {
if (orgAdmin && isNotClone) {
const request = new CipherRequest(cipher);
response = await this.apiService.putCipherAdmin(cipher.id, request);
const data = new CipherData(response, cipher.collectionIds);
return new Cipher(data, cipher.localData);
} else if (cipher.edit) {
const request = new CipherRequest(cipher);
response = await this.apiService.putCipher(cipher.id, request);

View File

@@ -56,6 +56,16 @@ describe("1Password 1Pux Importer", () => {
const SecureNoteDataJson = JSON.stringify(SecureNoteData);
const SanitizedExportJson = JSON.stringify(SanitizedExport);
it("should not import items with state 'archived'", async () => {
const importer = new OnePassword1PuxImporter();
const archivedLoginData = LoginData;
archivedLoginData["accounts"][0]["vaults"][0]["items"][0]["state"] = "archived";
const archivedDataJson = JSON.stringify(archivedLoginData);
const result = await importer.parse(archivedDataJson);
expect(result != null).toBe(true);
expect(result.ciphers.length).toBe(0);
});
it("should parse login data", async () => {
const importer = new OnePassword1PuxImporter();
const result = await importer.parse(LoginDataJson);

View File

@@ -26,7 +26,7 @@ export const APICredentialsData: ExportData = {
favIndex: 0,
createdAt: 1619465969,
updatedAt: 1619466052,
trashed: false,
state: "active",
categoryUuid: "112",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const APICredentialsData: ExportData = {
value: {
string: "apiuser@nullvalue.test",
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const APICredentialsData: ExportData = {
value: {
concealed: "apiapiapiapiapiapiappy",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: true,
@@ -73,7 +71,6 @@ export const APICredentialsData: ExportData = {
value: {
menu: "jwt",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const APICredentialsData: ExportData = {
value: {
string: "filename.jwt",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const APICredentialsData: ExportData = {
value: {
date: 1301918460,
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const APICredentialsData: ExportData = {
value: {
date: 1932811260,
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const APICredentialsData: ExportData = {
value: {
string: "not.your.everyday.hostname",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const BankAccountData: ExportData = {
favIndex: 0,
createdAt: 1619466056,
updatedAt: 1619466187,
trashed: false,
state: "active",
categoryUuid: "101",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const BankAccountData: ExportData = {
value: {
string: "Super Credit Union",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const BankAccountData: ExportData = {
value: {
string: "Cool Guy",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const BankAccountData: ExportData = {
value: {
menu: "checking",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const BankAccountData: ExportData = {
value: {
string: "111000999",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const BankAccountData: ExportData = {
value: {
string: "192837465918273645",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const BankAccountData: ExportData = {
value: {
string: "123456",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const BankAccountData: ExportData = {
value: {
string: "DE12 123456",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const BankAccountData: ExportData = {
value: {
concealed: "5555",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: true,
@@ -175,7 +167,6 @@ export const BankAccountData: ExportData = {
value: {
phone: "9399399933",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -191,7 +182,6 @@ export const BankAccountData: ExportData = {
value: {
string: "1 Fifth Avenue",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const CreditCardData: ExportData = {
favIndex: 0,
createdAt: 1619465282,
updatedAt: 1619465447,
trashed: false,
state: "active",
categoryUuid: "002",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const CreditCardData: ExportData = {
value: {
string: "Fred Engels",
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const CreditCardData: ExportData = {
value: {
creditCardType: "discover",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const CreditCardData: ExportData = {
value: {
creditCardNumber: "6011111111111117",
},
indexAtSource: 2,
guarded: true,
clipboardFilter: "0123456789",
multiline: false,
@@ -90,7 +87,6 @@ export const CreditCardData: ExportData = {
value: {
concealed: "1312",
},
indexAtSource: 3,
guarded: true,
multiline: false,
dontGenerate: true,
@@ -106,7 +102,6 @@ export const CreditCardData: ExportData = {
value: {
monthYear: 209912,
},
indexAtSource: 4,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -122,7 +117,6 @@ export const CreditCardData: ExportData = {
value: {
monthYear: 200101,
},
indexAtSource: 5,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -138,7 +132,6 @@ export const CreditCardData: ExportData = {
value: {
string: "card",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -160,7 +153,6 @@ export const CreditCardData: ExportData = {
value: {
string: "Some bank",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -176,7 +168,6 @@ export const CreditCardData: ExportData = {
value: {
phone: "123456",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -192,7 +183,6 @@ export const CreditCardData: ExportData = {
value: {
phone: "0800123456",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -208,7 +198,6 @@ export const CreditCardData: ExportData = {
value: {
phone: "+49123456",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -224,7 +213,6 @@ export const CreditCardData: ExportData = {
value: {
url: "somebank.com",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -246,7 +234,6 @@ export const CreditCardData: ExportData = {
value: {
concealed: "1234",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: true,
@@ -262,7 +249,6 @@ export const CreditCardData: ExportData = {
value: {
string: "$1312",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -278,7 +264,6 @@ export const CreditCardData: ExportData = {
value: {
string: "$500",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -294,7 +279,6 @@ export const CreditCardData: ExportData = {
value: {
string: "1%",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -310,7 +294,6 @@ export const CreditCardData: ExportData = {
value: {
string: "123456",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const DatabaseData: ExportData = {
favIndex: 0,
createdAt: 1619466193,
updatedAt: 1619466276,
trashed: false,
state: "active",
categoryUuid: "102",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const DatabaseData: ExportData = {
value: {
menu: "postgresql",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const DatabaseData: ExportData = {
value: {
string: "my.secret.db.server",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const DatabaseData: ExportData = {
value: {
string: "1337",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const DatabaseData: ExportData = {
value: {
string: "user_database",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const DatabaseData: ExportData = {
value: {
string: "cooldbuser",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const DatabaseData: ExportData = {
value: {
concealed: "^+kTjhLaN7wVPAhGU)*J",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const DatabaseData: ExportData = {
value: {
string: "ASDIUFU-283234",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const DatabaseData: ExportData = {
value: {
string: "cdbu",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -169,7 +161,6 @@ export const DatabaseData: ExportData = {
value: {
string: "ssh",
},
indexAtSource: 8,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const DriversLicenseData: ExportData = {
favIndex: 0,
createdAt: 1619466279,
updatedAt: 1619466425,
trashed: false,
state: "active",
categoryUuid: "103",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "Michael Scarn",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "2120 Mifflin Rd.",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const DriversLicenseData: ExportData = {
value: {
date: 252504060,
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const DriversLicenseData: ExportData = {
value: {
gender: "male",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "5'11\"",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "12345678901",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "C",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "B",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -169,7 +161,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "Pennsylvania",
},
indexAtSource: 8,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -185,7 +176,6 @@ export const DriversLicenseData: ExportData = {
value: {
string: "United States",
},
indexAtSource: 9,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -201,7 +191,6 @@ export const DriversLicenseData: ExportData = {
value: {
monthYear: 203012,
},
indexAtSource: 10,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const EmailAccountData: ExportData = {
favIndex: 0,
createdAt: 1619466428,
updatedAt: 1619466585,
trashed: false,
state: "active",
categoryUuid: "111",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const EmailAccountData: ExportData = {
value: {
menu: "either",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "someuser@nullvalue.test",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "mailserver.nullvalue.test",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "587",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const EmailAccountData: ExportData = {
value: {
concealed: "u1jsf<UI*&YU&^T",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const EmailAccountData: ExportData = {
value: {
menu: "TLS",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const EmailAccountData: ExportData = {
value: {
menu: "kerberos_v5",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -159,7 +152,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "mailserver.nullvalue.test",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -175,7 +167,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "589",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -191,7 +182,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "someuser@nullvalue.test",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -207,7 +197,6 @@ export const EmailAccountData: ExportData = {
value: {
concealed: "(*1674%^UIUJ*UI(IUI8u98uyy",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -223,7 +212,6 @@ export const EmailAccountData: ExportData = {
value: {
menu: "TLS",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -239,7 +227,6 @@ export const EmailAccountData: ExportData = {
value: {
menu: "password",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -261,7 +248,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "Telum",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -277,7 +263,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "https://telum.nullvalue.test",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -293,7 +278,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "2346666666",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -309,7 +293,6 @@ export const EmailAccountData: ExportData = {
value: {
string: "18005557777",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const EmailFieldOnIdentityData: ExportData = {
favIndex: 0,
createdAt: 1619465450,
updatedAt: 1619465789,
trashed: false,
state: "active",
categoryUuid: "004",
details: {
loginFields: [],
@@ -55,7 +55,6 @@ export const EmailFieldOnIdentityData: ExportData = {
provider: "myEmailProvider",
},
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const EmailFieldOnIdentityPrefilledData: ExportData = {
favIndex: 0,
createdAt: 1619465450,
updatedAt: 1619465789,
trashed: false,
state: "active",
categoryUuid: "004",
details: {
loginFields: [],
@@ -52,7 +52,6 @@ export const EmailFieldOnIdentityPrefilledData: ExportData = {
value: {
string: "gengels@nullvalue.test",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -71,7 +70,6 @@ export const EmailFieldOnIdentityPrefilledData: ExportData = {
provider: "myEmailProvider",
},
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const EmailFieldData: ExportData = {
favIndex: 1,
createdAt: 1619467985,
updatedAt: 1619468230,
trashed: false,
state: "active",
categoryUuid: "100",
details: {
loginFields: [],
@@ -49,7 +49,6 @@ export const EmailFieldData: ExportData = {
provider: "myEmailProvider",
},
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const IdentityData: ExportData = {
favIndex: 0,
createdAt: 1619465450,
updatedAt: 1619465789,
trashed: false,
state: "active",
categoryUuid: "004",
details: {
loginFields: [],
@@ -42,7 +42,6 @@ export const IdentityData: ExportData = {
value: {
string: "George",
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -58,7 +57,6 @@ export const IdentityData: ExportData = {
value: {
string: "S",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -74,7 +72,6 @@ export const IdentityData: ExportData = {
value: {
string: "Engels",
},
indexAtSource: 2,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -90,7 +87,6 @@ export const IdentityData: ExportData = {
value: {
menu: "male",
},
indexAtSource: 3,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -106,7 +102,6 @@ export const IdentityData: ExportData = {
value: {
date: 347198460,
},
indexAtSource: 4,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -122,7 +117,6 @@ export const IdentityData: ExportData = {
value: {
string: "Steel Worker",
},
indexAtSource: 5,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -138,7 +132,6 @@ export const IdentityData: ExportData = {
value: {
string: "Acme Inc.",
},
indexAtSource: 6,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -154,7 +147,6 @@ export const IdentityData: ExportData = {
value: {
string: "QA",
},
indexAtSource: 7,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -170,7 +162,6 @@ export const IdentityData: ExportData = {
value: {
string: "Quality Assurance Manager",
},
indexAtSource: 8,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -198,7 +189,6 @@ export const IdentityData: ExportData = {
state: "California",
},
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -214,7 +204,6 @@ export const IdentityData: ExportData = {
value: {
phone: "4565555555",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -230,7 +219,6 @@ export const IdentityData: ExportData = {
value: {
phone: "4575555555",
},
indexAtSource: 2,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -246,7 +234,6 @@ export const IdentityData: ExportData = {
value: {
phone: "4585555555",
},
indexAtSource: 3,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -262,7 +249,6 @@ export const IdentityData: ExportData = {
value: {
phone: "4595555555",
},
indexAtSource: 4,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -284,7 +270,6 @@ export const IdentityData: ExportData = {
value: {
string: "gengels",
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -300,7 +285,6 @@ export const IdentityData: ExportData = {
value: {
string: "Who's a super cool guy?",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -316,7 +300,6 @@ export const IdentityData: ExportData = {
value: {
string: "Me, buddy.",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -332,7 +315,6 @@ export const IdentityData: ExportData = {
value: {
string: "gengels@nullvalue.test",
},
indexAtSource: 3,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -348,7 +330,6 @@ export const IdentityData: ExportData = {
value: {
string: "cv.gengels.nullvalue.test",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -364,7 +345,6 @@ export const IdentityData: ExportData = {
value: {
string: "12345678",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -380,7 +360,6 @@ export const IdentityData: ExportData = {
value: {
string: "skypeisbad1619",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -396,7 +375,6 @@ export const IdentityData: ExportData = {
value: {
string: "aollol@lololol.aol.com",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -412,7 +390,6 @@ export const IdentityData: ExportData = {
value: {
string: "sk8rboi13@yah00.com",
},
indexAtSource: 8,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -428,7 +405,6 @@ export const IdentityData: ExportData = {
value: {
string: "msnothankyou@msn&m&m.com",
},
indexAtSource: 9,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -444,7 +420,6 @@ export const IdentityData: ExportData = {
value: {
string: "super cool guy",
},
indexAtSource: 10,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const LoginData: ExportData = {
favIndex: 0,
createdAt: 1635522833,
updatedAt: 1635522872,
trashed: false,
state: "active",
categoryUuid: "001",
details: {
loginFields: [
@@ -68,7 +68,6 @@ export const LoginData: ExportData = {
value: {
string: "username123123",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -84,7 +83,6 @@ export const LoginData: ExportData = {
value: {
totp: "otpseed777",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const MedicalRecordData: ExportData = {
favIndex: 0,
createdAt: 1641220207,
updatedAt: 1641220326,
trashed: false,
state: "active",
categoryUuid: "113",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const MedicalRecordData: ExportData = {
value: {
date: 1641038460,
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "some hospital/clinic",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -74,7 +72,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "Some Doctor",
},
indexAtSource: 2,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -91,7 +88,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "Me",
},
indexAtSource: 3,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -108,7 +104,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "unwell",
},
indexAtSource: 4,
guarded: true,
multiline: true,
dontGenerate: false,
@@ -131,7 +126,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "Insuline",
},
indexAtSource: 0,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -148,7 +142,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "1",
},
indexAtSource: 1,
guarded: true,
multiline: false,
dontGenerate: false,
@@ -165,7 +158,6 @@ export const MedicalRecordData: ExportData = {
value: {
string: "multiple times a day",
},
indexAtSource: 2,
guarded: true,
multiline: true,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const MembershipData: ExportData = {
favIndex: 1,
createdAt: 1619467269,
updatedAt: 1619467368,
trashed: false,
state: "active",
categoryUuid: "105",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const MembershipData: ExportData = {
value: {
string: "National Public Library",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const MembershipData: ExportData = {
value: {
url: "https://npl.nullvalue.gov.test",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const MembershipData: ExportData = {
value: {
phone: "9995555555",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const MembershipData: ExportData = {
value: {
string: "George Engels",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const MembershipData: ExportData = {
value: {
monthYear: 199901,
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const MembershipData: ExportData = {
value: {
monthYear: 203412,
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const MembershipData: ExportData = {
value: {
string: "64783862",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const MembershipData: ExportData = {
value: {
concealed: "19191",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const OnePuxExampleFile: ExportData = {
favIndex: 1,
createdAt: 1614298956,
updatedAt: 1635346445,
trashed: false,
state: "active",
categoryUuid: "001",
details: {
loginFields: [
@@ -50,7 +50,6 @@ export const OnePuxExampleFile: ExportData = {
value: {
concealed: "12345",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const OutdoorLicenseData: ExportData = {
favIndex: 0,
createdAt: 1619467374,
updatedAt: 1619467492,
trashed: false,
state: "active",
categoryUuid: "104",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
string: "Cash Bandit",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
date: 1617278460,
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
date: 2343124860,
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
string: "Bananas,blueberries,corn",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
string: "100/each",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
string: "Washington",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const OutdoorLicenseData: ExportData = {
value: {
string: "United States of America",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const PassportData: ExportData = {
favIndex: 0,
createdAt: 1619467498,
updatedAt: 1619467655,
trashed: false,
state: "active",
categoryUuid: "106",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const PassportData: ExportData = {
value: {
string: "US Passport",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const PassportData: ExportData = {
value: {
string: "United States of America",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const PassportData: ExportData = {
value: {
string: "76436847",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const PassportData: ExportData = {
value: {
string: "David Global",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const PassportData: ExportData = {
value: {
gender: "female",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const PassportData: ExportData = {
value: {
string: "International",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const PassportData: ExportData = {
value: {
string: "Department of State",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const PassportData: ExportData = {
value: {
date: 418046460,
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -169,7 +161,6 @@ export const PassportData: ExportData = {
value: {
string: "A cave somewhere in Maine",
},
indexAtSource: 8,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -185,7 +176,6 @@ export const PassportData: ExportData = {
value: {
date: 1577880060,
},
indexAtSource: 9,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -201,7 +191,6 @@ export const PassportData: ExportData = {
value: {
date: 2524651260,
},
indexAtSource: 10,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const PasswordData: ExportData = {
favIndex: 0,
createdAt: 1619465796,
updatedAt: 1619465869,
trashed: false,
state: "active",
categoryUuid: "005",
details: {
loginFields: [],

View File

@@ -26,7 +26,7 @@ export const RewardsProgramData: ExportData = {
favIndex: 0,
createdAt: 1619467659,
updatedAt: 1619467765,
trashed: false,
state: "active",
categoryUuid: "107",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const RewardsProgramData: ExportData = {
value: {
string: "Super Cool Store Co.",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const RewardsProgramData: ExportData = {
value: {
string: "Chef Coldroom",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const RewardsProgramData: ExportData = {
value: {
string: "member-29813569",
},
indexAtSource: 2,
guarded: false,
clipboardFilter:
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
@@ -91,7 +88,6 @@ export const RewardsProgramData: ExportData = {
value: {
concealed: "99913",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -113,7 +109,6 @@ export const RewardsProgramData: ExportData = {
value: {
string: "additional member id",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -129,7 +124,6 @@ export const RewardsProgramData: ExportData = {
value: {
monthYear: 202101,
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -145,7 +139,6 @@ export const RewardsProgramData: ExportData = {
value: {
phone: "123456",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -161,7 +154,6 @@ export const RewardsProgramData: ExportData = {
value: {
phone: "123456",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -177,7 +169,6 @@ export const RewardsProgramData: ExportData = {
value: {
url: "supercoolstore.com",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const SecureNoteData: ExportData = {
favIndex: 0,
createdAt: 1619465226,
updatedAt: 1619465278,
trashed: false,
state: "active",
categoryUuid: "003",
details: {
loginFields: [],

View File

@@ -26,7 +26,7 @@ export const ServerData: ExportData = {
favIndex: 0,
createdAt: 1619467769,
updatedAt: 1619467906,
trashed: false,
state: "active",
categoryUuid: "110",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const ServerData: ExportData = {
value: {
string: "https://coolserver.nullvalue.test",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const ServerData: ExportData = {
value: {
string: "frankly-notsure",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const ServerData: ExportData = {
value: {
concealed: "*&YHJI87yjy78u",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -95,7 +92,6 @@ export const ServerData: ExportData = {
value: {
string: "https://coolserver.nullvalue.test/admin",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -111,7 +107,6 @@ export const ServerData: ExportData = {
value: {
string: "frankly-idontknowwhatimdoing",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -127,7 +122,6 @@ export const ServerData: ExportData = {
value: {
concealed: "^%RY&^YUiju8iUYHJI(U",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -149,7 +143,6 @@ export const ServerData: ExportData = {
value: {
string: "Private Hosting Provider Inc.",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -165,7 +158,6 @@ export const ServerData: ExportData = {
value: {
string: "https://phpi.nullvalue.test",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -181,7 +173,6 @@ export const ServerData: ExportData = {
value: {
string: "https://phpi.nullvalue.test/support",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -197,7 +188,6 @@ export const ServerData: ExportData = {
value: {
string: "8882569382",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const SoftwareLicenseData: ExportData = {
favIndex: 1,
createdAt: 1619467985,
updatedAt: 1619468230,
trashed: false,
state: "active",
categoryUuid: "100",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "5.10.1000",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "265453-13457355-847327",
},
indexAtSource: 1,
guarded: true,
multiline: true,
dontGenerate: false,
@@ -79,7 +77,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "Kay Riddler",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -98,7 +95,6 @@ export const SoftwareLicenseData: ExportData = {
provider: null,
},
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -114,7 +110,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "Riddles and Jigsaw Puzzles GmbH",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -136,7 +131,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
url: "https://limuxcompany.nullvalue.test/5.10.1000/isos",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -152,7 +146,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "Limux Software and Hardware",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -168,7 +161,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
url: "https://limuxcompany.nullvalue.test/",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -184,7 +176,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "$999",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -203,7 +194,6 @@ export const SoftwareLicenseData: ExportData = {
provider: null,
},
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -225,7 +215,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
date: 1617278460,
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -241,7 +230,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "594839",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -257,7 +245,6 @@ export const SoftwareLicenseData: ExportData = {
value: {
string: "$1086.59",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -26,7 +26,7 @@ export const SSNData: ExportData = {
favIndex: 1,
createdAt: 1619467910,
updatedAt: 1619467982,
trashed: false,
state: "active",
categoryUuid: "108",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const SSNData: ExportData = {
value: {
string: "Jack Judd",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const SSNData: ExportData = {
value: {
concealed: "131-216-1900",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: true,

View File

@@ -26,7 +26,7 @@ export const WirelessRouterData: ExportData = {
favIndex: 0,
createdAt: 1577652307,
updatedAt: 1577652307,
trashed: false,
state: "active",
categoryUuid: "109",
details: {
loginFields: [],
@@ -41,7 +41,6 @@ export const WirelessRouterData: ExportData = {
value: {
string: "pixel 2Xl",
},
indexAtSource: 0,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -57,7 +56,6 @@ export const WirelessRouterData: ExportData = {
value: {
concealed: "BqatGTVQ9TCN72tLbjrsHqkb",
},
indexAtSource: 1,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -73,7 +71,6 @@ export const WirelessRouterData: ExportData = {
value: {
string: "127.0.0.1",
},
indexAtSource: 2,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -89,7 +86,6 @@ export const WirelessRouterData: ExportData = {
value: {
string: "some airportId",
},
indexAtSource: 3,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -105,7 +101,6 @@ export const WirelessRouterData: ExportData = {
value: {
string: "some network name",
},
indexAtSource: 4,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -121,7 +116,6 @@ export const WirelessRouterData: ExportData = {
value: {
menu: "WPA",
},
indexAtSource: 5,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -137,7 +131,6 @@ export const WirelessRouterData: ExportData = {
value: {
concealed: "wifipassword",
},
indexAtSource: 6,
guarded: false,
multiline: false,
dontGenerate: false,
@@ -153,7 +146,6 @@ export const WirelessRouterData: ExportData = {
value: {
concealed: "diskpassword",
},
indexAtSource: 7,
guarded: false,
multiline: false,
dontGenerate: false,

View File

@@ -22,7 +22,7 @@ export class Parser {
/*
May return null when the chunk does not represent an account.
All secure notes are ACCTs but not all of them store account information.
TODO: Add a test for the folder case!
TODO: Add a test case that covers secure note account!
*/
@@ -60,9 +60,17 @@ export class Parser {
// 3: url
step = 3;
let url = Utils.fromBufferToUtf8(
this.decodeHexLoose(Utils.fromBufferToUtf8(this.readItem(reader))),
);
const urlEncoded = this.readItem(reader);
let url =
urlEncoded.length > 0 && urlEncoded[0] === 33 // 33 = '!'
? // URL is encrypted
await this.cryptoUtils.decryptAes256PlainWithDefault(
urlEncoded,
encryptionKey,
placeholder,
)
: // URL is not encrypted
Utils.fromBufferToUtf8(this.decodeHexLoose(Utils.fromBufferToUtf8(urlEncoded)));
// Ignore "group" accounts. They have no credentials.
if (url == "http://group") {

View File

@@ -37,7 +37,7 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
// const personalVaults = account.vaults[0].filter((v) => v.attrs.type === VaultAttributeTypeEnum.Personal);
account.vaults.forEach((vault: VaultsEntity) => {
vault.items.forEach((item: Item) => {
if (item.trashed === true) {
if (item.state === "archived") {
return;
}

View File

@@ -53,7 +53,7 @@ export interface Item {
favIndex: number;
createdAt: number;
updatedAt: number;
trashed?: boolean;
state: "active" | "archived";
categoryUuid: string;
details: Details;
overview: Overview;
@@ -88,12 +88,12 @@ export interface SectionsEntity {
title: string;
name?: string | null;
fields?: FieldsEntity[] | null;
hideAddAnotherField?: boolean | null;
}
export interface FieldsEntity {
title: string;
id: string;
value: Value;
indexAtSource: number;
guarded: boolean;
multiline: boolean;
dontGenerate: boolean;
@@ -153,6 +153,8 @@ export interface Overview {
pbe?: number | null;
pgrng?: boolean | null;
tags?: string[] | null;
icons?: string | null;
watchtowerExclusions?: string | null;
}
export interface UrlsEntity {
label: string;

View File

@@ -27,6 +27,7 @@
bitIconButton="bwi-generate"
bitSuffix
[appA11yTitle]="'generatePassword' | i18n"
[disabled]="!config.areSendsAllowed"
(click)="generatePassword()"
data-testid="generate-password"
></button>
@@ -35,7 +36,7 @@
bitIconButton="bwi-clone"
bitSuffix
[appA11yTitle]="'copyPassword' | i18n"
[disabled]="!sendOptionsForm.get('password').value"
[disabled]="!config.areSendsAllowed || !sendOptionsForm.get('password').value"
[valueLabel]="'password' | i18n"
[appCopyClick]="sendOptionsForm.get('password').value"
showToast

View File

@@ -79,6 +79,9 @@ type BaseCipherFormConfig = {
* List of organizations that the user can create ciphers for.
*/
organizations?: Organization[];
/** Hides the fields that are only applicable to individuals, useful in the Admin Console where folders aren't applicable */
hideIndividualVaultFields?: true;
};
/**

View File

@@ -2,6 +2,7 @@
<bit-section-header>
<h2 bitTypography="h6">{{ "itemDetails" | i18n }}</h2>
<button
*ngIf="!config.hideIndividualVaultFields"
slot="end"
type="button"
size="small"
@@ -18,7 +19,11 @@
<input bitInput formControlName="name" />
</bit-form-field>
<div class="tw-grid tw-grid-cols-2 tw-gap-1">
<bit-form-field *ngIf="showOwnership" [disableMargin]="!showCollectionsControl">
<bit-form-field
*ngIf="showOwnership"
[disableMargin]="!showCollectionsControl"
[class.tw-col-span-2]="config.hideIndividualVaultFields"
>
<bit-label>{{ "owner" | i18n }}</bit-label>
<bit-select formControlName="organizationId">
<bit-option
@@ -36,6 +41,7 @@
<bit-form-field
[class.tw-col-span-2]="!showOwnership"
[disableMargin]="!showCollectionsControl"
*ngIf="!config.hideIndividualVaultFields"
>
<bit-label>{{ "folder" | i18n }}</bit-label>
<bit-select formControlName="folderId">

8
package-lock.json generated
View File

@@ -194,11 +194,11 @@
},
"apps/browser": {
"name": "@bitwarden/browser",
"version": "2024.10.0"
"version": "2024.10.1"
},
"apps/cli": {
"name": "@bitwarden/cli",
"version": "2024.9.1",
"version": "2024.10.0",
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@koa/multer": "3.0.2",
@@ -234,7 +234,7 @@
},
"apps/desktop": {
"name": "@bitwarden/desktop",
"version": "2024.9.2",
"version": "2024.10.0",
"hasInstallScript": true,
"license": "GPL-3.0"
},
@@ -248,7 +248,7 @@
},
"apps/web": {
"name": "@bitwarden/web-vault",
"version": "2024.10.0"
"version": "2024.10.1"
},
"libs/admin-console": {
"name": "@bitwarden/admin-console",

View File

@@ -22,6 +22,7 @@
"test:watch": "jest --clearCache && jest --watch",
"test:watch:all": "jest --watchAll",
"test:types": "node ./scripts/test-types.js",
"test:locales": "tsc --project ./scripts/tsconfig.json && node ./scripts/dist/test-locales.js",
"docs:json": "compodoc -p ./tsconfig.json -e json -d . --disableRoutesGraph",
"storybook": "ng run components:storybook",
"build-storybook": "ng run components:build-storybook",

5
scripts/.eslintrc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"env": {
"node": true
}
}

105
scripts/test-locales.ts Normal file
View File

@@ -0,0 +1,105 @@
/* eslint no-console:0 */
import fs from "fs";
import path from "path";
type Messages = {
[id: string]: {
message: string;
};
};
function findLocaleFiles(dir: string): string[] {
return fs
.readdirSync(dir, { encoding: null, recursive: true })
.filter((file) => path.basename(file) === "messages.json")
.filter((file) => path.dirname(file).endsWith("en"))
.map((file) => path.join(dir, file));
}
function findAllLocaleFiles(rootDir: string): string[] {
return [
...findLocaleFiles(path.join(rootDir, "apps", "browser", "src")),
...findLocaleFiles(path.join(rootDir, "apps", "cli", "src")),
...findLocaleFiles(path.join(rootDir, "apps", "desktop", "src")),
...findLocaleFiles(path.join(rootDir, "apps", "web", "src")),
].map((file) => path.relative(rootDir, file));
}
function readMessagesJson(file: string): Messages {
let content = fs.readFileSync(file, { encoding: "utf-8" });
// Strip BOM
content = content.replace(/^\uFEFF/, "");
try {
return JSON.parse(content);
} catch (e: unknown) {
console.error(`ERROR: Invalid JSON file ${file}`, e);
throw e;
}
}
function compareMessagesJson(beforeFile: string, afterFile: string): boolean {
try {
console.log("Comparing locale files:", beforeFile, afterFile);
const messagesBeforeJson = readMessagesJson(beforeFile);
const messagesAfterJson = readMessagesJson(afterFile);
const messagesIdMapBefore = toMessageIdMap(messagesBeforeJson);
const messagesIdMapAfter = toMessageIdMap(messagesAfterJson);
let changed = false;
for (const [id, message] of messagesIdMapAfter.entries()) {
if (!messagesIdMapBefore.has(id)) {
console.log("New message:", id);
continue;
}
if (messagesIdMapBefore.get(id) !== message) {
console.error("ERROR: Message changed:", id);
changed = true;
}
}
return changed;
} catch (e: unknown) {
console.error(`ERROR: Unable to compare files ${beforeFile} and ${afterFile}`, e);
throw e;
}
}
function toMessageIdMap(messagesJson: Messages): Map<string, string> {
return Object.entries(messagesJson).reduce((map, [id, value]) => {
map.set(id, value.message);
return map;
}, new Map<string, string>());
}
const rootDir = path.join(__dirname, "..", "..");
const baseBranchRootDir = path.join(rootDir, "base");
const files = findAllLocaleFiles(rootDir);
console.log("Detected valid English locale files:", files);
let changedFiles = false;
for (const file of files) {
const baseBranchFile = path.join(baseBranchRootDir, file);
if (!fs.existsSync(baseBranchFile)) {
console.error("ERROR: File not found in base branch:", file);
continue;
}
const changed = compareMessagesJson(baseBranchFile, path.join(rootDir, file));
changedFiles ||= changed;
}
if (changedFiles) {
console.error(
"ERROR: Incompatible Crowdin locale files. " +
"All messages in messages.json locale files needs to be immutable and cannot be updated. " +
"If a message needs to be changed, create a new message id and update your code to use it instead.",
);
process.exit(1);
}

9
scripts/tsconfig.json Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": "../libs/shared/tsconfig",
"compilerOptions": {
"outDir": "dist",
"module": "NodeNext",
"target": "ESNext"
},
"include": ["*.ts"]
}

View File

@@ -42,6 +42,6 @@
}
]
},
"include": ["apps/**/*", "libs/**/*", "bitwarden_license/**/*"],
"exclude": ["**/build"]
"include": ["apps/**/*", "libs/**/*", "bitwarden_license/**/*", "scripts/**/*"],
"exclude": ["**/build", "**/dist"]
}