mirror of
https://github.com/bitwarden/browser
synced 2026-02-14 23:45:37 +00:00
Merge branch 'main' into auth/pm-19555/defect-clicking-log-out-button
This commit is contained in:
@@ -8,24 +8,24 @@ export function CipherAction({
|
||||
handleAction = () => {
|
||||
/* no-op */
|
||||
},
|
||||
i18n,
|
||||
notificationType,
|
||||
theme,
|
||||
}: {
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: typeof NotificationTypes.Change | typeof NotificationTypes.Add;
|
||||
theme: Theme;
|
||||
}) {
|
||||
return notificationType === NotificationTypes.Change
|
||||
? BadgeButton({
|
||||
buttonAction: handleAction,
|
||||
// @TODO localize
|
||||
buttonText: "Update",
|
||||
buttonText: i18n.notificationUpdate,
|
||||
theme,
|
||||
})
|
||||
: EditButton({
|
||||
buttonAction: handleAction,
|
||||
// @TODO localize
|
||||
buttonText: "Edit",
|
||||
buttonText: i18n.notificationEdit,
|
||||
theme,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,11 +19,13 @@ const cipherIconWidth = "24px";
|
||||
export function CipherItem({
|
||||
cipher,
|
||||
handleAction,
|
||||
i18n,
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
}: {
|
||||
cipher: NotificationCipherData;
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
}) {
|
||||
@@ -34,7 +36,7 @@ export function CipherItem({
|
||||
|
||||
if (notificationType === NotificationTypes.Change || notificationType === NotificationTypes.Add) {
|
||||
cipherActionButton = html`<div>
|
||||
${CipherAction({ handleAction, notificationType, theme })}
|
||||
${CipherAction({ handleAction, i18n, notificationType, theme })}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CipherAction } from "../../cipher/cipher-action";
|
||||
|
||||
type Args = {
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: typeof NotificationTypes.Change | typeof NotificationTypes.Add;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import { NotificationBody } from "../../notification/body";
|
||||
|
||||
type Args = {
|
||||
ciphers: NotificationCipherData[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
|
||||
@@ -17,12 +17,14 @@ const { css } = createEmotion({
|
||||
|
||||
export function NotificationBody({
|
||||
ciphers = [],
|
||||
i18n,
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
handleEditOrUpdateAction,
|
||||
}: {
|
||||
ciphers?: NotificationCipherData[];
|
||||
customClasses?: string[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
@@ -37,6 +39,7 @@ export function NotificationBody({
|
||||
theme,
|
||||
children: CipherItem({
|
||||
cipher,
|
||||
i18n,
|
||||
notificationType,
|
||||
theme,
|
||||
handleAction: handleEditOrUpdateAction,
|
||||
|
||||
@@ -22,17 +22,19 @@ function getVaultIconByProductTier(productTierType?: ProductTierType): Option["i
|
||||
}
|
||||
|
||||
export type NotificationButtonRowProps = {
|
||||
theme: Theme;
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
organizations?: OrgView[];
|
||||
primaryButton: {
|
||||
text: string;
|
||||
handlePrimaryButtonClick: (args: any) => void;
|
||||
};
|
||||
folders?: FolderView[];
|
||||
organizations?: OrgView[];
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function NotificationButtonRow({
|
||||
folders,
|
||||
i18n,
|
||||
organizations,
|
||||
primaryButton,
|
||||
theme,
|
||||
@@ -40,7 +42,7 @@ export function NotificationButtonRow({
|
||||
const currentUserVaultOption: Option = {
|
||||
icon: User,
|
||||
default: true,
|
||||
text: "My vault", // @TODO localize
|
||||
text: i18n.myVault,
|
||||
value: "0",
|
||||
};
|
||||
const organizationOptions: Option[] = organizations?.length
|
||||
@@ -84,7 +86,7 @@ export function NotificationButtonRow({
|
||||
? [
|
||||
{
|
||||
id: "organization",
|
||||
label: "Vault", // @TODO localize
|
||||
label: i18n.vault,
|
||||
options: organizationOptions,
|
||||
},
|
||||
]
|
||||
@@ -93,7 +95,7 @@ export function NotificationButtonRow({
|
||||
? [
|
||||
{
|
||||
id: "folder",
|
||||
label: "Folder", // @TODO localize
|
||||
label: i18n.folder,
|
||||
options: folderOptions,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -59,6 +59,7 @@ export function NotificationContainer({
|
||||
ciphers,
|
||||
notificationType: type,
|
||||
theme,
|
||||
i18n,
|
||||
})
|
||||
: null}
|
||||
${NotificationFooter({
|
||||
|
||||
@@ -38,6 +38,7 @@ export function NotificationFooter({
|
||||
? NotificationButtonRow({
|
||||
folders,
|
||||
organizations,
|
||||
i18n,
|
||||
primaryButton: {
|
||||
handlePrimaryButtonClick: handleSaveAction,
|
||||
text: primaryButtonText,
|
||||
|
||||
@@ -53,6 +53,7 @@ function getI18n() {
|
||||
return {
|
||||
appName: chrome.i18n.getMessage("appName"),
|
||||
close: chrome.i18n.getMessage("close"),
|
||||
collection: chrome.i18n.getMessage("collection"),
|
||||
folder: chrome.i18n.getMessage("folder"),
|
||||
loginSaveSuccess: chrome.i18n.getMessage("loginSaveSuccess"),
|
||||
loginSaveSuccessDetails: chrome.i18n.getMessage("loginSaveSuccessDetails"),
|
||||
@@ -63,10 +64,11 @@ function getI18n() {
|
||||
nextSecurityTaskAction: chrome.i18n.getMessage("nextSecurityTaskAction"),
|
||||
newItem: chrome.i18n.getMessage("newItem"),
|
||||
never: chrome.i18n.getMessage("never"),
|
||||
myVault: chrome.i18n.getMessage("myVault"),
|
||||
notificationAddDesc: chrome.i18n.getMessage("notificationAddDesc"),
|
||||
notificationAddSave: chrome.i18n.getMessage("notificationAddSave"),
|
||||
notificationChangeDesc: chrome.i18n.getMessage("notificationChangeDesc"),
|
||||
notificationChangeSave: chrome.i18n.getMessage("notificationChangeSave"),
|
||||
notificationUpdate: chrome.i18n.getMessage("notificationChangeSave"),
|
||||
notificationEdit: chrome.i18n.getMessage("edit"),
|
||||
notificationUnlock: chrome.i18n.getMessage("notificationUnlock"),
|
||||
notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"),
|
||||
@@ -78,6 +80,7 @@ function getI18n() {
|
||||
typeLogin: chrome.i18n.getMessage("typeLogin"),
|
||||
updateLoginAction: chrome.i18n.getMessage("updateLoginAction"),
|
||||
updateLoginPrompt: chrome.i18n.getMessage("updateLoginPrompt"),
|
||||
vault: chrome.i18n.getMessage("vault"),
|
||||
view: chrome.i18n.getMessage("view"),
|
||||
};
|
||||
}
|
||||
@@ -200,7 +203,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
const changeTemplate = document.getElementById("template-change") as HTMLTemplateElement;
|
||||
|
||||
const changeButton = findElementById<HTMLSelectElement>(changeTemplate, "change-save");
|
||||
changeButton.textContent = i18n.notificationChangeSave;
|
||||
changeButton.textContent = i18n.notificationUpdate;
|
||||
|
||||
const changeEditButton = findElementById<HTMLButtonElement>(changeTemplate, "change-edit");
|
||||
changeEditButton.textContent = i18n.notificationEdit;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">The Bitwarden Password Manager</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Store logins, secure notes, and more</li>
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-forbes></app-logo-forbes>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-abm-enterprise-content",
|
||||
templateUrl: "abm-enterprise-content.component.html",
|
||||
})
|
||||
export class AbmEnterpriseContentComponent {}
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">The Bitwarden Password Manager</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Store logins, secure notes, and more</li>
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-forbes></app-logo-forbes>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-abm-teams-content",
|
||||
templateUrl: "abm-teams-content.component.html",
|
||||
})
|
||||
export class AbmTeamsContentComponent {}
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">Start Your Enterprise Free Trial Now</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Deploy and manage quickly and easily</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-cnet></app-logo-cnet>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-cnet-enterprise-content",
|
||||
templateUrl: "cnet-enterprise-content.component.html",
|
||||
})
|
||||
export class CnetEnterpriseContentComponent {}
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">Start Your Premium Account Now</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Store logins, secure notes, and more</li>
|
||||
<li>Secure your account with advanced two-step login</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-cnet></app-logo-cnet>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-cnet-individual-content",
|
||||
templateUrl: "cnet-individual-content.component.html",
|
||||
})
|
||||
export class CnetIndividualContentComponent {}
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">Start Your Teams Free Trial Now</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Deploy and manage quickly and easily</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-cnet></app-logo-cnet>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-cnet-teams-content",
|
||||
templateUrl: "cnet-teams-content.component.html",
|
||||
})
|
||||
export class CnetTeamsContentComponent {}
|
||||
@@ -1,16 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">The Bitwarden Password Manager</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Store logins, secure notes, and more</li>
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28">
|
||||
<app-logo-company-testimonial></app-logo-company-testimonial>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-default-content",
|
||||
templateUrl: "default-content.component.html",
|
||||
})
|
||||
export class DefaultContentComponent {}
|
||||
@@ -1,44 +0,0 @@
|
||||
<h1 class="tw-text-3xl !tw-text-alt2">Start your 7-day Enterprise free trial</h1>
|
||||
<div class="tw-pt-20">
|
||||
<h2 class="tw-text-2xl">
|
||||
Bitwarden is the most trusted password manager designed for seamless administration and employee
|
||||
usability.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main tw-list-none tw-pl-0">
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Instantly and securely share credentials with the groups and individuals who need them</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Strengthen company-wide security through centralized administrative control and
|
||||
policies</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Streamline user onboarding and automate account provisioning with flexible SSO and SCIM
|
||||
integrations</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Migrate to Bitwarden in minutes with comprehensive import options</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Give all Enterprise users the gift of 360º security with a free Families plan</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-badges></app-logo-badges>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-enterprise-content",
|
||||
templateUrl: "enterprise-content.component.html",
|
||||
})
|
||||
export class EnterpriseContentComponent {}
|
||||
@@ -1,44 +0,0 @@
|
||||
<h1 class="tw-text-3xl !tw-text-alt2">Start your 7-day Enterprise free trial</h1>
|
||||
<div class="tw-pt-20">
|
||||
<h2 class="tw-text-2xl">
|
||||
Bitwarden is the most trusted password manager designed for seamless administration and employee
|
||||
usability.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main tw-list-none tw-pl-0">
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Instantly and securely share credentials with the groups and individuals who need them</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Strengthen company-wide security through centralized administrative control and
|
||||
policies</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Streamline user onboarding and automate account provisioning with flexible SSO and SCIM
|
||||
integrations</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Migrate to Bitwarden in minutes with comprehensive import options</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Give all Enterprise users the gift of 360º security with a free Families plan</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-badges></app-logo-badges>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-enterprise1-content",
|
||||
templateUrl: "enterprise1-content.component.html",
|
||||
})
|
||||
export class Enterprise1ContentComponent {}
|
||||
@@ -1,44 +0,0 @@
|
||||
<h1 class="tw-text-3xl !tw-text-alt2">Start your 7-day Enterprise free trial</h1>
|
||||
<div class="tw-pt-20">
|
||||
<h2 class="tw-text-2xl">
|
||||
Bitwarden is the most trusted password manager designed for seamless administration and employee
|
||||
usability.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main tw-list-none tw-pl-0">
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Instantly and securely share credentials with the groups and individuals who need them</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Strengthen company-wide security through centralized administrative control and
|
||||
policies</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Streamline user onboarding and automate account provisioning with flexible SSO and SCIM
|
||||
integrations</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Migrate to Bitwarden in minutes with comprehensive import options</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Give all Enterprise users the gift of 360º security with a free Families plan</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-badges></app-logo-badges>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-enterprise2-content",
|
||||
templateUrl: "enterprise2-content.component.html",
|
||||
})
|
||||
export class Enterprise2ContentComponent {}
|
||||
@@ -1,11 +0,0 @@
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/vault-signup-badges.png"
|
||||
class="tw-mx-auto tw-block tw-w-full"
|
||||
alt="third party awards"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
</figure>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-badges",
|
||||
templateUrl: "logo-badges.component.html",
|
||||
})
|
||||
export class LogoBadgesComponent {}
|
||||
@@ -1,23 +0,0 @@
|
||||
<figure>
|
||||
<div class="tw-flex tw-justify-center tw-gap-4 tw-text-[#eab308] tw-text-5xl">
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
</div>
|
||||
<blockquote class="tw-mx-auto tw-my-2 tw-max-w-xl tw-px-4 tw-text-center">
|
||||
“Bitwarden scores points for being fully open-source, secure and audited annually by third-party
|
||||
cybersecurity firms, giving it a level of transparency that sets it apart from its peers.”
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/cnet-logo.svg"
|
||||
class="tw-mx-auto tw-block tw-w-40"
|
||||
alt="CNET Logo"
|
||||
/>
|
||||
</cite>
|
||||
<p class="tw-text-center tw-font-bold -tw-translate-y-4">Best Password Manager in 2024</p>
|
||||
</figcaption>
|
||||
</figure>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-cnet-5-stars",
|
||||
templateUrl: "logo-cnet-5-stars.component.html",
|
||||
})
|
||||
export class LogoCnet5StarsComponent {}
|
||||
@@ -1,15 +0,0 @@
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/cnet-logo.svg"
|
||||
class="tw-mx-auto tw-block tw-w-40"
|
||||
alt="CNET Logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="tw-mx-auto tw-mt-2 tw-max-w-xl tw-px-4 tw-text-center">
|
||||
"No more excuses; start using Bitwarden today. The identity you save could be your own. The
|
||||
money definitely will be."
|
||||
</blockquote>
|
||||
</figure>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-cnet",
|
||||
templateUrl: "logo-cnet.component.html",
|
||||
})
|
||||
export class LogoCnetComponent {}
|
||||
@@ -1,28 +0,0 @@
|
||||
<figure class="tw-text-center">
|
||||
<p class="tw-mx-auto tw-my-2 tw-max-w-xl tw-px-4 tw-text-center">
|
||||
Recommended by industry experts
|
||||
</p>
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-8 tw-items-center tw-justify-center tw-mb-4">
|
||||
<div class="tw-flex tw-gap-8">
|
||||
<img src="../../images/register-layout/cnet-logo.svg" class="tw-w-32" alt="CNET Logo" />
|
||||
<img
|
||||
src="../../images/register-layout/wired-logo.png"
|
||||
class="tw-w-32 tw-object-contain"
|
||||
alt="WIRED Logo"
|
||||
/>
|
||||
</div>
|
||||
<div class="tw-flex tw-gap-8">
|
||||
<img
|
||||
src="../../images/register-layout/new-york-times-logo.svg"
|
||||
class="tw-w-32"
|
||||
alt="New York Times Logo"
|
||||
/>
|
||||
<img src="../../images/register-layout/pcmag-logo.svg" class="tw-w-32" alt="PC Mag Logo" />
|
||||
</div>
|
||||
</div>
|
||||
<blockquote>
|
||||
“Bitwarden is currently CNET's top pick for the best password manager, thanks in part to
|
||||
its commitment to transparency and its unbeatable free tier.”
|
||||
</blockquote>
|
||||
<p class="tw-font-bold">Best Password Manager in 2024</p>
|
||||
</figure>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-company-testimonial",
|
||||
templateUrl: "logo-company-testimonial.component.html",
|
||||
})
|
||||
export class LogoCompanyTestimonialComponent {}
|
||||
@@ -1,15 +0,0 @@
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/forbes-logo.svg"
|
||||
class="tw-mx-auto tw-block tw-w-40"
|
||||
alt="Forbes Logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="tw-mx-auto tw-mt-2 tw-max-w-xl tw-px-4 tw-text-center">
|
||||
“Bitwarden boasts the backing of some of the world's best security experts and an attractive,
|
||||
easy-to-use interface”
|
||||
</blockquote>
|
||||
</figure>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-forbes",
|
||||
templateUrl: "logo-forbes.component.html",
|
||||
})
|
||||
export class LogoForbesComponent {}
|
||||
@@ -1,5 +0,0 @@
|
||||
<img
|
||||
src="../../images/register-layout/usnews-360-badge.svg"
|
||||
class="tw-mx-auto tw-block tw-w-48"
|
||||
alt="US News 360 Reviews Best Password Manager"
|
||||
/>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-logo-us-news",
|
||||
templateUrl: "logo-us-news.component.html",
|
||||
})
|
||||
export class LogoUSNewsComponent {}
|
||||
@@ -1,13 +0,0 @@
|
||||
<figure>
|
||||
<h2 class="tw-mx-auto tw-pb-2 tw-max-w-xl tw-font-semibold tw-text-center">
|
||||
{{ header }}
|
||||
</h2>
|
||||
<blockquote class="tw-mx-auto tw-my-2 tw-max-w-xl tw-px-4 tw-text-center">
|
||||
"{{ quote }}"
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<p class="tw-mx-auto tw-text-center tw-font-bold">{{ source }}</p>
|
||||
</cite>
|
||||
</figcaption>
|
||||
</figure>
|
||||
@@ -1,13 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-review-blurb",
|
||||
templateUrl: "review-blurb.component.html",
|
||||
})
|
||||
export class ReviewBlurbComponent {
|
||||
@Input() header: string;
|
||||
@Input() quote: string;
|
||||
@Input() source: string;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<div class="tw-flex tw-flex-col tw-items-center tw-justify-center">
|
||||
<img class="tw-mb-2" [ngClass]="logoClass" [src]="logoSrc" [alt]="logoAlt" />
|
||||
<div class="tw-flex tw-items-center">
|
||||
<div class="tw-flex tw-items-center tw-justify-center tw-text-[#eab308] tw-text-2xl">
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
<div class="tw-relative">
|
||||
<div class="tw-absolute tw-inset-0 tw-w-3 tw-overflow-hidden">
|
||||
<i class="bwi bwi-star-f"></i>
|
||||
</div>
|
||||
<i class="bwi bwi-star-f tw-text-[#cbd5e1]"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span class="tw-ml-2">4.7</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,13 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "review-logo",
|
||||
templateUrl: "review-logo.component.html",
|
||||
})
|
||||
export class ReviewLogoComponent {
|
||||
@Input() logoClass: string;
|
||||
@Input() logoSrc: string;
|
||||
@Input() logoAlt: string;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">{{ header }}</h1>
|
||||
<div class="tw-pt-16">
|
||||
<h2 class="tw-text-2xl tw-font-semibold">
|
||||
{{ headline }}
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li *ngFor="let primaryPoint of primaryPoints">
|
||||
{{ primaryPoint }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-12 tw-flex tw-flex-col">
|
||||
<div class="tw-rounded-[32px] tw-bg-background">
|
||||
<div class="tw-my-8 tw-mx-6">
|
||||
<h2 class="tw-pl-5 tw-font-semibold">{{ calloutHeadline }}</h2>
|
||||
<ul class="tw-space-y-4 tw-mt-4 tw-pl-10">
|
||||
<li *ngFor="let callout of callouts">
|
||||
{{ callout }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-mt-12 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-review-blurb
|
||||
header="Businesses trust Bitwarden to secure their infrastructure"
|
||||
quote="At this point, it would be almost impossible to leak our secrets. It's just one less thing we have to worry about."
|
||||
source="Titanom Technologies"
|
||||
></app-review-blurb>
|
||||
</div>
|
||||
@@ -1,80 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: "app-secrets-manager-content",
|
||||
templateUrl: "secrets-manager-content.component.html",
|
||||
})
|
||||
export class SecretsManagerContentComponent implements OnInit, OnDestroy {
|
||||
header: string;
|
||||
headline =
|
||||
"A simpler, faster way to secure and automate secrets across code and infrastructure deployments";
|
||||
primaryPoints: string[];
|
||||
calloutHeadline: string;
|
||||
callouts: string[];
|
||||
|
||||
private paidPrimaryPoints = [
|
||||
"Unlimited secrets, users, and projects",
|
||||
"Simple and transparent pricing",
|
||||
"Zero-knowledge, end-to-end encryption",
|
||||
];
|
||||
|
||||
private paidCalloutHeadline = "Limited time offer";
|
||||
|
||||
private paidCallouts = [
|
||||
"Sign up today and receive a complimentary 12-month subscription to Bitwarden Password Manager",
|
||||
"Experience complete security across your organization",
|
||||
"Secure all your sensitive credentials, from user applications to machine secrets",
|
||||
];
|
||||
|
||||
private freePrimaryPoints = [
|
||||
"Unlimited secrets",
|
||||
"Simple and transparent pricing",
|
||||
"Zero-knowledge, end-to-end encryption",
|
||||
];
|
||||
|
||||
private freeCalloutHeadline = "Go beyond developer security!";
|
||||
|
||||
private freeCallouts = [
|
||||
"Your Bitwarden account will also grant complimentary access to Bitwarden Password Manager",
|
||||
"Extend end-to-end encryption to your personal passwords, addresses, credit cards and notes",
|
||||
];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute) {}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParameters) => {
|
||||
switch (queryParameters.org) {
|
||||
case "enterprise":
|
||||
this.header = "Secrets Manager for Enterprise";
|
||||
this.primaryPoints = this.paidPrimaryPoints;
|
||||
this.calloutHeadline = this.paidCalloutHeadline;
|
||||
this.callouts = this.paidCallouts;
|
||||
break;
|
||||
case "free":
|
||||
this.header = "Bitwarden Secrets Manager";
|
||||
this.primaryPoints = this.freePrimaryPoints;
|
||||
this.calloutHeadline = this.freeCalloutHeadline;
|
||||
this.callouts = this.freeCallouts;
|
||||
break;
|
||||
case "teams":
|
||||
case "teamsStarter":
|
||||
this.header = "Secrets Manager for Teams";
|
||||
this.primaryPoints = this.paidPrimaryPoints;
|
||||
this.calloutHeadline = this.paidCalloutHeadline;
|
||||
this.callouts = this.paidCallouts;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">The Bitwarden Password Manager</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>Store logins, secure notes, and more</li>
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-forbes></app-logo-forbes>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-teams-content",
|
||||
templateUrl: "teams-content.component.html",
|
||||
})
|
||||
export class TeamsContentComponent {}
|
||||
@@ -1,35 +0,0 @@
|
||||
<h1 class="tw-text-3xl !tw-text-alt2">Start your 7-day free trial for Teams</h1>
|
||||
<div class="tw-pt-20">
|
||||
<h2 class="tw-text-2xl">
|
||||
Strengthen business security with an easy-to-use password manager your team will love.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main tw-list-none tw-pl-0">
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Instantly and securely share credentials with the groups and individuals who need them</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Migrate to Bitwarden in minutes with comprehensive import options</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Save time and increase productivity with autofill and instant device syncing</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Enhance security practices across your team with easy user management</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-badges></app-logo-badges>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-teams1-content",
|
||||
templateUrl: "teams1-content.component.html",
|
||||
})
|
||||
export class Teams1ContentComponent {}
|
||||
@@ -1,35 +0,0 @@
|
||||
<h1 class="tw-text-3xl !tw-text-alt2">Start your 7-day free trial for Teams</h1>
|
||||
<div class="tw-pt-20">
|
||||
<h2 class="tw-text-2xl">
|
||||
Strengthen business security with an easy-to-use password manager your team will love.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main tw-list-none tw-pl-0">
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Instantly and securely share credentials with the groups and individuals who need them</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Migrate to Bitwarden in minutes with comprehensive import options</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Save time and increase productivity with autofill and instant device syncing</span
|
||||
>
|
||||
</li>
|
||||
<li class="tw-flex tw-items-center">
|
||||
<i class="bwi bwi-lg bwi-check-circle tw-mr-4 tw-flex-none"></i
|
||||
><span class="tw-flex-auto"
|
||||
>Enhance security practices across your team with easy user management</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-badges></app-logo-badges>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-teams2-content",
|
||||
templateUrl: "teams2-content.component.html",
|
||||
})
|
||||
export class Teams2ContentComponent {}
|
||||
@@ -1,26 +0,0 @@
|
||||
<h1 class="tw-text-4xl !tw-text-alt2">Begin Teams Starter Free Trial Now</h1>
|
||||
<div class="tw-pt-32">
|
||||
<h2 class="tw-text-2xl">
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure password
|
||||
storage and sharing.
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
|
||||
<li>
|
||||
Powerful security for up to 10 users
|
||||
<div class="tw-mt-2 tw-text-base">
|
||||
Have more than 10 users?
|
||||
<a routerLink="/register" [queryParams]="{ org: 'teams', layout: 'teams1' }"
|
||||
>Start a Teams trial</a
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li>Collaborate and share securely</li>
|
||||
<li>Deploy and manage quickly and easily</li>
|
||||
<li>Access anywhere on any device</li>
|
||||
<li>Create your account to get started</li>
|
||||
</ul>
|
||||
<div class="tw-mt-28 tw-flex tw-flex-col tw-items-center tw-gap-5">
|
||||
<app-logo-forbes></app-logo-forbes>
|
||||
<app-logo-us-news></app-logo-us-news>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "app-teams3-content",
|
||||
templateUrl: "teams3-content.component.html",
|
||||
})
|
||||
export class Teams3ContentComponent {}
|
||||
@@ -1,45 +0,0 @@
|
||||
<app-vertical-stepper #stepper linear>
|
||||
<app-vertical-step
|
||||
label="{{ 'organizationInformation' | i18n | titlecase }}"
|
||||
[subLabel]="subLabels.organizationInfo"
|
||||
>
|
||||
<app-org-info [nameOnly]="true" [formGroup]="formGroup"> </app-org-info>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="formGroup.get('name').invalid"
|
||||
(click)="createOrganization()"
|
||||
>
|
||||
{{ "next" | i18n }}
|
||||
</button>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="{{ 'confirmationDetails' | i18n | titlecase }}">
|
||||
<div class="tw-pb-6 tw-pl-6">
|
||||
<p class="tw-text-xl">{{ "smFreeTrialThankYou" | i18n }}</p>
|
||||
<ul class="tw-list-disc">
|
||||
<li>
|
||||
<p>
|
||||
{{ "smFreeTrialConfirmationEmail" | i18n }}
|
||||
<span class="tw-font-bold">{{ formGroup.get("email").value }}</span
|
||||
>.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<button type="button" bitButton buttonType="primary" (click)="navigateToSecretsManager()">
|
||||
{{ "getStarted" | i18n | titlecase }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
(click)="navigateToMembers()"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
{{ "inviteUsers" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</app-vertical-step>
|
||||
</app-vertical-stepper>
|
||||
@@ -1,90 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import { VerticalStepperComponent } from "../../trial-initiation/vertical-stepper/vertical-stepper.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-secrets-manager-trial-free-stepper",
|
||||
templateUrl: "secrets-manager-trial-free-stepper.component.html",
|
||||
})
|
||||
export class SecretsManagerTrialFreeStepperComponent implements OnInit {
|
||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||
|
||||
formGroup = this.formBuilder.group({
|
||||
name: [
|
||||
"",
|
||||
{
|
||||
validators: [Validators.required, Validators.maxLength(50)],
|
||||
updateOn: "change",
|
||||
},
|
||||
],
|
||||
email: [
|
||||
"",
|
||||
{
|
||||
validators: [Validators.email],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
subLabels = {
|
||||
createAccount:
|
||||
"Before creating your free organization, you first need to log in or create a personal account.",
|
||||
organizationInfo: "Enter your organization information",
|
||||
};
|
||||
|
||||
organizationId: string;
|
||||
|
||||
referenceEventRequest: ReferenceEventRequest;
|
||||
|
||||
constructor(
|
||||
protected formBuilder: UntypedFormBuilder,
|
||||
protected i18nService: I18nService,
|
||||
protected organizationBillingService: OrganizationBillingService,
|
||||
protected router: Router,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.referenceEventRequest = new ReferenceEventRequest();
|
||||
this.referenceEventRequest.initiationPath = "Secrets Manager trial from marketing website";
|
||||
}
|
||||
|
||||
accountCreated(email: string): void {
|
||||
this.formGroup.get("email")?.setValue(email);
|
||||
this.subLabels.createAccount = email;
|
||||
this.verticalStepper.next();
|
||||
}
|
||||
|
||||
async createOrganization(): Promise<void> {
|
||||
const response = await this.organizationBillingService.startFree({
|
||||
organization: {
|
||||
name: this.formGroup.get("name").value,
|
||||
billingEmail: this.formGroup.get("email").value,
|
||||
},
|
||||
plan: {
|
||||
type: PlanType.Free,
|
||||
subscribeToSecretsManager: true,
|
||||
isFromSecretsManagerTrial: true,
|
||||
},
|
||||
});
|
||||
|
||||
this.organizationId = response.id;
|
||||
this.subLabels.organizationInfo = response.name;
|
||||
this.verticalStepper.next();
|
||||
}
|
||||
|
||||
async navigateToMembers(): Promise<void> {
|
||||
await this.router.navigate(["organizations", this.organizationId, "members"]);
|
||||
}
|
||||
|
||||
async navigateToSecretsManager(): Promise<void> {
|
||||
await this.router.navigate(["sm", this.organizationId]);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<app-vertical-stepper #stepper linear>
|
||||
<app-vertical-step
|
||||
label="{{ 'organizationInformation' | i18n | titlecase }}"
|
||||
[subLabel]="subLabels.organizationInfo"
|
||||
>
|
||||
<app-org-info [nameOnly]="true" [formGroup]="formGroup"></app-org-info>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="formGroup.get('name').invalid"
|
||||
[loading]="createOrganizationLoading"
|
||||
(click)="createOrganizationOnTrial()"
|
||||
*ngIf="enableTrialPayment$ | async"
|
||||
>
|
||||
{{ "startTrial" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="formGroup.get('name').invalid"
|
||||
[loading]="createOrganizationLoading"
|
||||
cdkStepperNext
|
||||
*ngIf="!(enableTrialPayment$ | async)"
|
||||
>
|
||||
{{ "next" | i18n }}
|
||||
</button>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step
|
||||
label="{{ 'billing' | i18n | titlecase }}"
|
||||
[subLabel]="billingSubLabel"
|
||||
*ngIf="!(enableTrialPayment$ | async)"
|
||||
>
|
||||
<app-trial-billing-step
|
||||
*ngIf="stepper.selectedIndex === 2"
|
||||
[organizationInfo]="{
|
||||
name: formGroup.get('name').value,
|
||||
email: formGroup.get('email').value,
|
||||
type: productType,
|
||||
}"
|
||||
[subscriptionProduct]="SubscriptionProduct.SecretsManager"
|
||||
(steppedBack)="steppedBack()"
|
||||
(organizationCreated)="organizationCreated($event)"
|
||||
></app-trial-billing-step>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="{{ 'confirmationDetails' | i18n | titlecase }}">
|
||||
<app-trial-confirmation-details
|
||||
[email]="formGroup.get('email').value"
|
||||
[orgLabel]="organizationTypeQueryParameter"
|
||||
></app-trial-confirmation-details>
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<button type="button" bitButton buttonType="primary" (click)="navigateToSecretsManager()">
|
||||
{{ "getStarted" | i18n | titlecase }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
(click)="navigateToMembers()"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
{{ "inviteUsers" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</app-vertical-step>
|
||||
</app-vertical-stepper>
|
||||
@@ -1,144 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, Input, OnInit, ViewChild } from "@angular/core";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service";
|
||||
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import {
|
||||
OrganizationCreatedEvent,
|
||||
SubscriptionProduct,
|
||||
TrialOrganizationType,
|
||||
} from "../../../billing/accounts/trial-initiation/trial-billing-step.component";
|
||||
import { VerticalStepperComponent } from "../../trial-initiation/vertical-stepper/vertical-stepper.component";
|
||||
import { SecretsManagerTrialFreeStepperComponent } from "../secrets-manager/secrets-manager-trial-free-stepper.component";
|
||||
|
||||
export enum ValidOrgParams {
|
||||
families = "families",
|
||||
enterprise = "enterprise",
|
||||
teams = "teams",
|
||||
teamsStarter = "teamsStarter",
|
||||
individual = "individual",
|
||||
premium = "premium",
|
||||
free = "free",
|
||||
}
|
||||
|
||||
const trialFlowOrgs = [
|
||||
ValidOrgParams.teams,
|
||||
ValidOrgParams.teamsStarter,
|
||||
ValidOrgParams.enterprise,
|
||||
ValidOrgParams.families,
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: "app-secrets-manager-trial-paid-stepper",
|
||||
templateUrl: "secrets-manager-trial-paid-stepper.component.html",
|
||||
})
|
||||
export class SecretsManagerTrialPaidStepperComponent
|
||||
extends SecretsManagerTrialFreeStepperComponent
|
||||
implements OnInit
|
||||
{
|
||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||
@Input() organizationTypeQueryParameter: string;
|
||||
|
||||
plan: PlanType;
|
||||
createOrganizationLoading = false;
|
||||
billingSubLabel = this.i18nService.t("billingTrialSubLabel");
|
||||
organizationId: string;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
protected enableTrialPayment$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.TrialPaymentOptional,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private configService: ConfigService,
|
||||
protected formBuilder: UntypedFormBuilder,
|
||||
protected i18nService: I18nService,
|
||||
protected organizationBillingService: OrganizationBillingService,
|
||||
protected router: Router,
|
||||
) {
|
||||
super(formBuilder, i18nService, organizationBillingService, router);
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.referenceEventRequest = new ReferenceEventRequest();
|
||||
this.referenceEventRequest.initiationPath = "Secrets Manager trial from marketing website";
|
||||
|
||||
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((qParams) => {
|
||||
if (trialFlowOrgs.includes(qParams.org)) {
|
||||
if (qParams.org === ValidOrgParams.teamsStarter) {
|
||||
this.plan = PlanType.TeamsStarter;
|
||||
} else if (qParams.org === ValidOrgParams.teams) {
|
||||
this.plan = PlanType.TeamsAnnually;
|
||||
} else if (qParams.org === ValidOrgParams.enterprise) {
|
||||
this.plan = PlanType.EnterpriseAnnually;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
organizationCreated(event: OrganizationCreatedEvent) {
|
||||
this.organizationId = event.organizationId;
|
||||
this.billingSubLabel = event.planDescription;
|
||||
this.verticalStepper.next();
|
||||
}
|
||||
|
||||
steppedBack() {
|
||||
this.verticalStepper.previous();
|
||||
}
|
||||
|
||||
async createOrganizationOnTrial(): Promise<void> {
|
||||
this.createOrganizationLoading = true;
|
||||
const response = await this.organizationBillingService.purchaseSubscriptionNoPaymentMethod({
|
||||
organization: {
|
||||
name: this.formGroup.get("name").value,
|
||||
billingEmail: this.formGroup.get("email").value,
|
||||
initiationPath: "Secrets Manager trial from marketing website",
|
||||
},
|
||||
plan: {
|
||||
type: this.plan,
|
||||
subscribeToSecretsManager: true,
|
||||
isFromSecretsManagerTrial: true,
|
||||
passwordManagerSeats: 1,
|
||||
secretsManagerSeats: 1,
|
||||
},
|
||||
});
|
||||
|
||||
this.organizationId = response?.id;
|
||||
this.subLabels.organizationInfo = response?.name;
|
||||
this.createOrganizationLoading = false;
|
||||
this.verticalStepper.next();
|
||||
}
|
||||
|
||||
get createAccountLabel() {
|
||||
const organizationType =
|
||||
this.productType === ProductTierType.TeamsStarter
|
||||
? "Teams Starter"
|
||||
: ProductTierType[this.productType];
|
||||
return `Before creating your ${organizationType} organization, you first need to log in or create a personal account.`;
|
||||
}
|
||||
|
||||
get productType(): TrialOrganizationType {
|
||||
switch (this.organizationTypeQueryParameter) {
|
||||
case "enterprise":
|
||||
return ProductTierType.Enterprise;
|
||||
case "families":
|
||||
return ProductTierType.Families;
|
||||
case "teams":
|
||||
return ProductTierType.Teams;
|
||||
case "teamsStarter":
|
||||
return ProductTierType.TeamsStarter;
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly SubscriptionProduct = SubscriptionProduct;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<!-- eslint-disable tailwindcss/no-custom-classname -->
|
||||
<ng-container>
|
||||
<div class="tw-absolute tw--z-10 tw--mt-48 tw-h-[28rem] tw-w-full tw-bg-background-alt2"></div>
|
||||
<div class="tw-min-w-4xl tw-mx-auto tw-flex tw-max-w-screen-xl tw-gap-12 tw-px-4">
|
||||
<div class="tw-w-1/2">
|
||||
<img
|
||||
alt="Bitwarden"
|
||||
style="height: 50px; width: 335px"
|
||||
class="tw-mt-6"
|
||||
src="../../../../images/register-layout/logo-horizontal-white.svg"
|
||||
/>
|
||||
<div class="tw-pt-12">
|
||||
<app-secrets-manager-content></app-secrets-manager-content>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-w-1/2">
|
||||
<div class="tw-pt-44">
|
||||
<div class="tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background">
|
||||
<div
|
||||
*ngIf="!freeOrganization"
|
||||
class="tw-flex tw-h-auto tw-w-full tw-gap-5 tw-rounded-t tw-bg-secondary-100"
|
||||
>
|
||||
<h2 class="tw-pb-4 tw-pl-4 tw-pt-5 tw-text-base tw-font-bold tw-uppercase">
|
||||
{{
|
||||
"startYour7DayFreeTrialOfBitwardenSecretsManagerFor"
|
||||
| i18n: organizationTypeQueryParameter
|
||||
}}
|
||||
</h2>
|
||||
<environment-selector
|
||||
class="tw-mr-4 tw-mt-6 tw-flex-shrink-0 tw-text-end"
|
||||
></environment-selector>
|
||||
</div>
|
||||
<app-secrets-manager-trial-free-stepper
|
||||
*ngIf="freeOrganization"
|
||||
></app-secrets-manager-trial-free-stepper>
|
||||
<app-secrets-manager-trial-paid-stepper
|
||||
*ngIf="!freeOrganization"
|
||||
[organizationTypeQueryParameter]="organizationTypeQueryParameter"
|
||||
></app-secrets-manager-trial-paid-stepper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -1,32 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: "app-secrets-manager-trial",
|
||||
templateUrl: "secrets-manager-trial.component.html",
|
||||
})
|
||||
export class SecretsManagerTrialComponent implements OnInit, OnDestroy {
|
||||
organizationTypeQueryParameter: string;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private route: ActivatedRoute) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParameters) => {
|
||||
this.organizationTypeQueryParameter = queryParameters.org;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
get freeOrganization() {
|
||||
return this.organizationTypeQueryParameter === "free";
|
||||
}
|
||||
}
|
||||
@@ -7,36 +7,10 @@ import { FormFieldModule } from "@bitwarden/components";
|
||||
|
||||
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
|
||||
import { TrialBillingStepComponent } from "../../billing/accounts/trial-initiation/trial-billing-step.component";
|
||||
import { SecretsManagerTrialFreeStepperComponent } from "../../billing/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component";
|
||||
import { SecretsManagerTrialPaidStepperComponent } from "../../billing/trial-initiation/secrets-manager/secrets-manager-trial-paid-stepper.component";
|
||||
import { SecretsManagerTrialComponent } from "../../billing/trial-initiation/secrets-manager/secrets-manager-trial.component";
|
||||
import { EnvironmentSelectorModule } from "../../components/environment-selector/environment-selector.module";
|
||||
import { SharedModule } from "../../shared";
|
||||
|
||||
import { CompleteTrialInitiationComponent } from "./complete-trial-initiation/complete-trial-initiation.component";
|
||||
import { ConfirmationDetailsComponent } from "./confirmation-details.component";
|
||||
import { AbmEnterpriseContentComponent } from "./content/abm-enterprise-content.component";
|
||||
import { AbmTeamsContentComponent } from "./content/abm-teams-content.component";
|
||||
import { CnetEnterpriseContentComponent } from "./content/cnet-enterprise-content.component";
|
||||
import { CnetIndividualContentComponent } from "./content/cnet-individual-content.component";
|
||||
import { CnetTeamsContentComponent } from "./content/cnet-teams-content.component";
|
||||
import { DefaultContentComponent } from "./content/default-content.component";
|
||||
import { EnterpriseContentComponent } from "./content/enterprise-content.component";
|
||||
import { Enterprise1ContentComponent } from "./content/enterprise1-content.component";
|
||||
import { Enterprise2ContentComponent } from "./content/enterprise2-content.component";
|
||||
import { LogoBadgesComponent } from "./content/logo-badges.component";
|
||||
import { LogoCnet5StarsComponent } from "./content/logo-cnet-5-stars.component";
|
||||
import { LogoCnetComponent } from "./content/logo-cnet.component";
|
||||
import { LogoCompanyTestimonialComponent } from "./content/logo-company-testimonial.component";
|
||||
import { LogoForbesComponent } from "./content/logo-forbes.component";
|
||||
import { LogoUSNewsComponent } from "./content/logo-us-news.component";
|
||||
import { ReviewBlurbComponent } from "./content/review-blurb.component";
|
||||
import { ReviewLogoComponent } from "./content/review-logo.component";
|
||||
import { SecretsManagerContentComponent } from "./content/secrets-manager-content.component";
|
||||
import { TeamsContentComponent } from "./content/teams-content.component";
|
||||
import { Teams1ContentComponent } from "./content/teams1-content.component";
|
||||
import { Teams2ContentComponent } from "./content/teams2-content.component";
|
||||
import { Teams3ContentComponent } from "./content/teams3-content.component";
|
||||
import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.module";
|
||||
|
||||
@NgModule({
|
||||
@@ -46,41 +20,10 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul
|
||||
VerticalStepperModule,
|
||||
FormFieldModule,
|
||||
OrganizationCreateModule,
|
||||
EnvironmentSelectorModule,
|
||||
TrialBillingStepComponent,
|
||||
InputPasswordComponent,
|
||||
],
|
||||
declarations: [
|
||||
CompleteTrialInitiationComponent,
|
||||
EnterpriseContentComponent,
|
||||
TeamsContentComponent,
|
||||
ConfirmationDetailsComponent,
|
||||
DefaultContentComponent,
|
||||
EnterpriseContentComponent,
|
||||
Enterprise1ContentComponent,
|
||||
Enterprise2ContentComponent,
|
||||
TeamsContentComponent,
|
||||
Teams1ContentComponent,
|
||||
Teams2ContentComponent,
|
||||
Teams3ContentComponent,
|
||||
CnetEnterpriseContentComponent,
|
||||
CnetIndividualContentComponent,
|
||||
CnetTeamsContentComponent,
|
||||
AbmEnterpriseContentComponent,
|
||||
AbmTeamsContentComponent,
|
||||
LogoBadgesComponent,
|
||||
LogoCnet5StarsComponent,
|
||||
LogoCompanyTestimonialComponent,
|
||||
LogoCnetComponent,
|
||||
LogoForbesComponent,
|
||||
LogoUSNewsComponent,
|
||||
ReviewLogoComponent,
|
||||
SecretsManagerContentComponent,
|
||||
ReviewBlurbComponent,
|
||||
SecretsManagerTrialComponent,
|
||||
SecretsManagerTrialFreeStepperComponent,
|
||||
SecretsManagerTrialPaidStepperComponent,
|
||||
],
|
||||
declarations: [CompleteTrialInitiationComponent, ConfirmationDetailsComponent],
|
||||
exports: [CompleteTrialInitiationComponent],
|
||||
providers: [TitleCasePipe],
|
||||
})
|
||||
|
||||
@@ -116,6 +116,12 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
throw new Error("No encryption key provided.");
|
||||
}
|
||||
|
||||
if (this.blockType0) {
|
||||
if (key.inner().type === EncryptionType.AesCbc256_B64 || key.key.byteLength < 64) {
|
||||
throw new Error("Type 0 encryption is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
if (plainValue == null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,19 @@ describe("EncryptService", () => {
|
||||
"No wrappingKey provided for wrapping.",
|
||||
);
|
||||
});
|
||||
it("fails if type 0 key is provided with flag turned on", async () => {
|
||||
(encryptService as any).blockType0 = true;
|
||||
const mock32Key = mock<SymmetricCryptoKey>();
|
||||
mock32Key.key = makeStaticByteArray(32);
|
||||
mock32Key.inner.mockReturnValue({
|
||||
type: 0,
|
||||
encryptionKey: mock32Key.key,
|
||||
});
|
||||
|
||||
await expect(encryptService.wrapSymmetricKey(mock32Key, mock32Key)).rejects.toThrow(
|
||||
"Type 0 encryption is not supported.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("wrapDecapsulationKey", () => {
|
||||
@@ -83,6 +96,19 @@ describe("EncryptService", () => {
|
||||
"No wrappingKey provided for wrapping.",
|
||||
);
|
||||
});
|
||||
it("throws if type 0 key is provided with flag turned on", async () => {
|
||||
(encryptService as any).blockType0 = true;
|
||||
const mock32Key = mock<SymmetricCryptoKey>();
|
||||
mock32Key.key = makeStaticByteArray(32);
|
||||
mock32Key.inner.mockReturnValue({
|
||||
type: 0,
|
||||
encryptionKey: mock32Key.key,
|
||||
});
|
||||
|
||||
await expect(
|
||||
encryptService.wrapDecapsulationKey(new Uint8Array(200), mock32Key),
|
||||
).rejects.toThrow("Type 0 encryption is not supported.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("wrapEncapsulationKey", () => {
|
||||
@@ -111,6 +137,19 @@ describe("EncryptService", () => {
|
||||
"No wrappingKey provided for wrapping.",
|
||||
);
|
||||
});
|
||||
it("throws if type 0 key is provided with flag turned on", async () => {
|
||||
(encryptService as any).blockType0 = true;
|
||||
const mock32Key = mock<SymmetricCryptoKey>();
|
||||
mock32Key.key = makeStaticByteArray(32);
|
||||
mock32Key.inner.mockReturnValue({
|
||||
type: 0,
|
||||
encryptionKey: mock32Key.key,
|
||||
});
|
||||
|
||||
await expect(
|
||||
encryptService.wrapEncapsulationKey(new Uint8Array(200), mock32Key),
|
||||
).rejects.toThrow("Type 0 encryption is not supported.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("onServerConfigChange", () => {
|
||||
|
||||
@@ -50,7 +50,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
model: SendView,
|
||||
file: File | ArrayBuffer,
|
||||
password: string,
|
||||
key?: SymmetricCryptoKey,
|
||||
userKey?: SymmetricCryptoKey,
|
||||
): Promise<[Send, EncArrayBuffer]> {
|
||||
let fileData: EncArrayBuffer = null;
|
||||
const send = new Send();
|
||||
@@ -62,15 +62,19 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
send.deletionDate = model.deletionDate;
|
||||
send.expirationDate = model.expirationDate;
|
||||
if (model.key == null) {
|
||||
// Sends use a seed, stored in the URL fragment. This seed is used to derive the key that is used for encryption.
|
||||
const key = await this.keyGenerationService.createKeyWithPurpose(
|
||||
128,
|
||||
this.sendKeyPurpose,
|
||||
this.sendKeySalt,
|
||||
);
|
||||
// key.material is the seed that can be used to re-derive the key
|
||||
model.key = key.material;
|
||||
model.cryptoKey = key.derivedKey;
|
||||
}
|
||||
if (password != null) {
|
||||
// Note: Despite being called key, the passwordKey is not used for encryption.
|
||||
// It is used as a static proof that the client knows the password, and has the encryption key.
|
||||
const passwordKey = await this.keyGenerationService.deriveKeyFromPassword(
|
||||
password,
|
||||
model.key,
|
||||
@@ -78,11 +82,11 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
);
|
||||
send.password = passwordKey.keyB64;
|
||||
}
|
||||
if (key == null) {
|
||||
key = await this.keyService.getUserKey();
|
||||
if (userKey == null) {
|
||||
userKey = await this.keyService.getUserKey();
|
||||
}
|
||||
// Key is not a SymmetricCryptoKey, but key material used to derive the cryptoKey
|
||||
send.key = await this.encryptService.encrypt(model.key, key);
|
||||
send.key = await this.encryptService.encrypt(model.key, userKey);
|
||||
send.name = await this.encryptService.encrypt(model.name, model.cryptoKey);
|
||||
send.notes = await this.encryptService.encrypt(model.notes, model.cryptoKey);
|
||||
if (send.type === SendType.Text) {
|
||||
|
||||
Reference in New Issue
Block a user