2024-07-02 22:22:51 +02:00
|
|
|
import { importProvidersFrom } from "@angular/core";
|
|
|
|
import { action } from "@storybook/addon-actions";
|
|
|
|
import {
|
|
|
|
applicationConfig,
|
|
|
|
componentWrapperDecorator,
|
|
|
|
Meta,
|
|
|
|
moduleMetadata,
|
|
|
|
StoryObj,
|
|
|
|
} from "@storybook/angular";
|
2024-07-12 00:01:24 +02:00
|
|
|
import { BehaviorSubject } from "rxjs";
|
2024-07-02 22:22:51 +02:00
|
|
|
|
2024-10-08 16:14:39 +02:00
|
|
|
import { CollectionView } from "@bitwarden/admin-console/common";
|
2024-07-18 18:38:55 +02:00
|
|
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
2024-09-12 21:47:23 +02:00
|
|
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
2024-07-02 22:22:51 +02:00
|
|
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
2024-08-01 17:35:04 +02:00
|
|
|
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
|
|
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
2024-10-08 18:45:45 +02:00
|
|
|
import { ClientType } from "@bitwarden/common/enums";
|
2024-08-01 17:35:04 +02:00
|
|
|
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
2024-10-08 18:45:45 +02:00
|
|
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
2024-07-02 22:22:51 +02:00
|
|
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
|
|
|
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
|
|
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
|
|
|
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
2024-07-18 18:38:55 +02:00
|
|
|
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
2024-07-02 22:22:51 +02:00
|
|
|
import { AsyncActionsModule, ButtonModule, ToastService } from "@bitwarden/components";
|
2024-07-18 18:38:55 +02:00
|
|
|
import {
|
|
|
|
CipherFormConfig,
|
|
|
|
CipherFormGenerationService,
|
|
|
|
PasswordRepromptService,
|
|
|
|
} from "@bitwarden/vault";
|
2024-08-14 20:24:29 +02:00
|
|
|
// FIXME: remove `/apps` import from `/libs`
|
|
|
|
// eslint-disable-next-line import/no-restricted-paths
|
2024-07-02 22:22:51 +02:00
|
|
|
import { PreloadedEnglishI18nModule } from "@bitwarden/web-vault/src/app/core/tests";
|
|
|
|
|
|
|
|
import { CipherFormService } from "./abstractions/cipher-form.service";
|
2024-07-18 18:38:55 +02:00
|
|
|
import { TotpCaptureService } from "./abstractions/totp-capture.service";
|
2024-07-02 22:22:51 +02:00
|
|
|
import { CipherFormModule } from "./cipher-form.module";
|
|
|
|
import { CipherFormComponent } from "./components/cipher-form.component";
|
|
|
|
|
|
|
|
const defaultConfig: CipherFormConfig = {
|
|
|
|
mode: "add",
|
|
|
|
cipherType: CipherType.Login,
|
|
|
|
admin: false,
|
|
|
|
allowPersonalOwnership: true,
|
|
|
|
collections: [
|
|
|
|
{
|
|
|
|
id: "col1",
|
|
|
|
name: "Org 1 Collection 1",
|
|
|
|
organizationId: "org1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "col2",
|
|
|
|
name: "Org 1 Collection 2",
|
|
|
|
organizationId: "org1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "colA",
|
|
|
|
name: "Org 2 Collection A",
|
|
|
|
organizationId: "org2",
|
|
|
|
},
|
|
|
|
] as CollectionView[],
|
|
|
|
folders: [
|
|
|
|
{
|
|
|
|
id: undefined,
|
|
|
|
name: "No Folder",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "folder2",
|
|
|
|
name: "Folder 2",
|
|
|
|
},
|
|
|
|
] as FolderView[],
|
|
|
|
organizations: [
|
|
|
|
{
|
|
|
|
id: "org1",
|
|
|
|
name: "Organization 1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: "org2",
|
|
|
|
name: "Organization 2",
|
|
|
|
},
|
|
|
|
] as Organization[],
|
|
|
|
originalCipher: {
|
|
|
|
id: "123",
|
|
|
|
organizationId: "org1",
|
|
|
|
name: "Test Cipher",
|
|
|
|
folderId: "folder2",
|
|
|
|
collectionIds: ["col1"],
|
|
|
|
favorite: false,
|
2024-07-12 00:01:24 +02:00
|
|
|
notes: "Example notes",
|
2024-07-18 18:38:55 +02:00
|
|
|
viewPassword: true,
|
|
|
|
login: Object.assign(new LoginView(), {
|
|
|
|
username: "testuser",
|
|
|
|
password: "testpassword",
|
|
|
|
fido2Credentials: [
|
|
|
|
{
|
2024-07-18 22:47:49 +02:00
|
|
|
creationDate: new Date(2024, 6, 18),
|
2024-07-18 18:38:55 +02:00
|
|
|
},
|
|
|
|
],
|
|
|
|
totp: "123456",
|
|
|
|
}) as LoginView,
|
2024-07-02 22:22:51 +02:00
|
|
|
} as unknown as Cipher,
|
|
|
|
};
|
|
|
|
|
|
|
|
class TestAddEditFormService implements CipherFormService {
|
|
|
|
decryptCipher(): Promise<CipherView> {
|
2024-08-01 17:35:04 +02:00
|
|
|
return Promise.resolve({ ...defaultConfig.originalCipher } as any);
|
2024-07-02 22:22:51 +02:00
|
|
|
}
|
|
|
|
async saveCipher(cipher: CipherView): Promise<CipherView> {
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
return cipher;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const actionsData = {
|
|
|
|
onSave: action("onSave"),
|
|
|
|
};
|
|
|
|
|
|
|
|
export default {
|
|
|
|
title: "Vault/Cipher Form",
|
|
|
|
component: CipherFormComponent,
|
|
|
|
decorators: [
|
|
|
|
moduleMetadata({
|
|
|
|
imports: [CipherFormModule, AsyncActionsModule, ButtonModule],
|
|
|
|
providers: [
|
|
|
|
{
|
|
|
|
provide: CipherFormService,
|
|
|
|
useClass: TestAddEditFormService,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
provide: ToastService,
|
|
|
|
useValue: {
|
|
|
|
showToast: action("showToast"),
|
|
|
|
},
|
|
|
|
},
|
2024-07-12 00:01:24 +02:00
|
|
|
{
|
|
|
|
provide: PasswordRepromptService,
|
|
|
|
useValue: {
|
|
|
|
enabled$: new BehaviorSubject(true),
|
|
|
|
},
|
|
|
|
},
|
2024-07-18 18:38:55 +02:00
|
|
|
{
|
|
|
|
provide: CipherFormGenerationService,
|
|
|
|
useValue: {
|
|
|
|
generateInitialPassword: () => Promise.resolve("initial-password"),
|
|
|
|
generatePassword: () => Promise.resolve("random-password"),
|
|
|
|
generateUsername: () => Promise.resolve("random-username"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
provide: TotpCaptureService,
|
|
|
|
useValue: {
|
|
|
|
captureTotpSecret: () => Promise.resolve("some-value"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
provide: AuditService,
|
|
|
|
useValue: {
|
|
|
|
passwordLeaked: () => Promise.resolve(0),
|
|
|
|
},
|
|
|
|
},
|
2024-08-01 17:35:04 +02:00
|
|
|
{
|
|
|
|
provide: DomainSettingsService,
|
|
|
|
useValue: {
|
|
|
|
defaultUriMatchStrategy$: new BehaviorSubject(UriMatchStrategy.StartsWith),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
provide: AutofillSettingsServiceAbstraction,
|
|
|
|
useValue: {
|
|
|
|
autofillOnPageLoadDefault$: new BehaviorSubject(true),
|
|
|
|
},
|
|
|
|
},
|
2024-09-12 21:47:23 +02:00
|
|
|
{
|
|
|
|
provide: EventCollectionService,
|
|
|
|
useValue: {
|
|
|
|
collect: () => Promise.resolve(),
|
|
|
|
},
|
|
|
|
},
|
2024-10-08 18:45:45 +02:00
|
|
|
{
|
|
|
|
provide: PlatformUtilsService,
|
|
|
|
useValue: {
|
|
|
|
getClientType: () => ClientType.Browser,
|
|
|
|
},
|
|
|
|
},
|
2024-07-02 22:22:51 +02:00
|
|
|
],
|
|
|
|
}),
|
|
|
|
componentWrapperDecorator(
|
|
|
|
(story) => `<div class="tw-bg-background-alt tw-text-main tw-border">${story}</div>`,
|
|
|
|
),
|
|
|
|
applicationConfig({
|
|
|
|
providers: [importProvidersFrom(PreloadedEnglishI18nModule)],
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
args: {
|
|
|
|
config: defaultConfig,
|
|
|
|
},
|
|
|
|
argTypes: {
|
|
|
|
config: {
|
|
|
|
description: "The configuration object for the form.",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
} as Meta;
|
|
|
|
|
|
|
|
type Story = StoryObj<CipherFormComponent>;
|
|
|
|
|
|
|
|
export const Default: Story = {
|
|
|
|
render: (args) => {
|
|
|
|
return {
|
|
|
|
props: {
|
|
|
|
onSave: actionsData.onSave,
|
|
|
|
...args,
|
|
|
|
},
|
|
|
|
template: /*html*/ `
|
|
|
|
<vault-cipher-form [config]="config" (cipherSaved)="onSave($event)" formId="test-form" [submitBtn]="submitBtn"></vault-cipher-form>
|
|
|
|
<button type="submit" form="test-form" bitButton buttonType="primary" #submitBtn>Submit</button>
|
|
|
|
`,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const Edit: Story = {
|
|
|
|
...Default,
|
|
|
|
args: {
|
|
|
|
config: {
|
|
|
|
...defaultConfig,
|
|
|
|
mode: "edit",
|
|
|
|
originalCipher: defaultConfig.originalCipher,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const PartialEdit: Story = {
|
|
|
|
...Default,
|
|
|
|
args: {
|
|
|
|
config: {
|
|
|
|
...defaultConfig,
|
|
|
|
mode: "partial-edit",
|
|
|
|
originalCipher: defaultConfig.originalCipher,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const Clone: Story = {
|
|
|
|
...Default,
|
|
|
|
args: {
|
|
|
|
config: {
|
|
|
|
...defaultConfig,
|
|
|
|
mode: "clone",
|
|
|
|
originalCipher: defaultConfig.originalCipher,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const NoPersonalOwnership: Story = {
|
|
|
|
...Default,
|
|
|
|
args: {
|
|
|
|
config: {
|
|
|
|
...defaultConfig,
|
|
|
|
mode: "add",
|
|
|
|
allowPersonalOwnership: false,
|
|
|
|
originalCipher: defaultConfig.originalCipher,
|
|
|
|
organizations: defaultConfig.organizations,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|