diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index eb13f2febb..7900740296 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -16,7 +16,6 @@ "proxyEvents": "https://events.bitwarden.com" }, "flags": { - "showTrial": true, - "showPasswordless": true + "showPasswordless": false } } diff --git a/apps/web/config/development.json b/apps/web/config/development.json index 15a09d724f..7aeffe55d0 100644 --- a/apps/web/config/development.json +++ b/apps/web/config/development.json @@ -10,7 +10,6 @@ "proxyNotifications": "http://localhost:61840" }, "flags": { - "showTrial": true, "secretsManager": true, "showPasswordless": true } diff --git a/apps/web/config/ee.json b/apps/web/config/ee.json index 3ba61fda59..cd36ab15c5 100644 --- a/apps/web/config/ee.json +++ b/apps/web/config/ee.json @@ -6,7 +6,5 @@ "proxyNotifications": "http://localhost:61841", "port": 8081 }, - "flags": { - "showTrial": false - } + "flags": {} } diff --git a/apps/web/config/qa.json b/apps/web/config/qa.json index 00dcc66d62..ab22d03514 100644 --- a/apps/web/config/qa.json +++ b/apps/web/config/qa.json @@ -10,7 +10,6 @@ "proxyEvents": "https://events.qa.bitwarden.pw" }, "flags": { - "showTrial": true, "secretsManager": false, "showPasswordless": true } diff --git a/apps/web/config/selfhosted.json b/apps/web/config/selfhosted.json index b37a922604..c25f21a5c1 100644 --- a/apps/web/config/selfhosted.json +++ b/apps/web/config/selfhosted.json @@ -7,7 +7,6 @@ "port": 8081 }, "flags": { - "showTrial": false, "showPasswordless": false } } diff --git a/apps/web/src/app/accounts/register.component.html b/apps/web/src/app/accounts/register.component.html deleted file mode 100644 index 8a68d32b03..0000000000 --- a/apps/web/src/app/accounts/register.component.html +++ /dev/null @@ -1,210 +0,0 @@ -
- -
-
-
-
- -
-
-
-
- -
-
-
-
- -
-

The Bitwarden Password Manager

-

- Trusted by millions of individuals, teams, and organizations worldwide for secure - password storage and sharing. -

-

Store logins, secure notes, and more

-

Collaborate and share securely

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

- Start Your Teams
Enterprise Free Trial Now -

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

- Start Your Teams
Enterprise Free Trial Now -

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

Start Your Premium Account Now

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Store logins, secure notes, and more

-

Secure your account with advanced two-step login

-

Access anywhere on any device

-

Create your account to get started

-
-
-
-
-
-
-

{{ "createAccount" | i18n }}

-
-
- - {{ "createOrganizationCreatePersonalAccount" | i18n }} - - -
-
-
-
-
-
-
-
-
-
-
- - cnet logo - -
-
- "No more excuses; start using Bitwarden today. The identity you save could be your - own. The money definitely will be." -
-
-
- -
-
-
- - Forbes Logo - -
-
- “Bitwarden boasts the backing of some of the world's best security experts and an - attractive, easy-to-use interface” -
-
-
-
-
- US News 360 Reviews Best Password Manager -
-
- US News 360 Reviews Best Password Manager -
-
-
-
diff --git a/apps/web/src/app/accounts/register.component.ts b/apps/web/src/app/accounts/register.component.ts deleted file mode 100644 index c7c3b7dfc5..0000000000 --- a/apps/web/src/app/accounts/register.component.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; -import { first } from "rxjs/operators"; - -import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuthService } from "@bitwarden/common/abstractions/auth.service"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; -import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; -import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; -import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { PolicyData } from "@bitwarden/common/models/data/policy.data"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options"; -import { Policy } from "@bitwarden/common/models/domain/policy"; -import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request"; - -import { RouterService } from "../core"; - -@Component({ - selector: "app-register", - templateUrl: "register.component.html", -}) -export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy { - email = ""; - showCreateOrgMessage = false; - layout = ""; - enforcedPolicyOptions: MasterPasswordPolicyOptions; - - private policies: Policy[]; - private destroy$ = new Subject(); - - constructor( - formValidationErrorService: FormValidationErrorsService, - formBuilder: UntypedFormBuilder, - authService: AuthService, - router: Router, - i18nService: I18nService, - cryptoService: CryptoService, - apiService: ApiService, - private route: ActivatedRoute, - stateService: StateService, - platformUtilsService: PlatformUtilsService, - passwordGenerationService: PasswordGenerationService, - private policyApiService: PolicyApiServiceAbstraction, - private policyService: PolicyService, - environmentService: EnvironmentService, - logService: LogService, - private routerService: RouterService - ) { - super( - formValidationErrorService, - formBuilder, - authService, - router, - i18nService, - cryptoService, - apiService, - stateService, - platformUtilsService, - passwordGenerationService, - environmentService, - logService - ); - } - - async ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil - this.route.queryParams.pipe(first()).subscribe((qParams) => { - this.referenceData = new ReferenceEventRequest(); - if (qParams.email != null && qParams.email.indexOf("@") > -1) { - this.email = qParams.email; - } - if (qParams.premium != null) { - this.routerService.setPreviousUrl("/settings/premium"); - } else if (qParams.org != null) { - this.showCreateOrgMessage = true; - this.referenceData.flow = qParams.org; - const route = this.router.createUrlTree(["create-organization"], { - queryParams: { plan: qParams.org }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - if (qParams.layout != null) { - this.layout = this.referenceData.layout = qParams.layout; - } - if (qParams.reference != null) { - this.referenceData.id = qParams.reference; - } else { - this.referenceData.id = ("; " + document.cookie) - .split("; reference=") - .pop() - .split(";") - .shift(); - } - // Are they coming from an email for sponsoring a families organization - if (qParams.sponsorshipToken != null) { - // After logging in redirect them to setup the families sponsorship - const route = this.router.createUrlTree(["setup/families-for-enterprise"], { - queryParams: { plan: qParams.sponsorshipToken }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - if (this.referenceData.id === "") { - this.referenceData.id = null; - } - }); - const invite = await this.stateService.getOrganizationInvitation(); - if (invite != null) { - try { - const policies = await this.policyApiService.getPoliciesByToken( - invite.organizationId, - invite.token, - invite.email, - invite.organizationUserId - ); - if (policies.data != null) { - const policiesData = policies.data.map((p) => new PolicyData(p)); - this.policies = policiesData.map((p) => new Policy(p)); - } - } catch (e) { - this.logService.error(e); - } - } - - if (this.policies != null) { - this.policyService - .masterPasswordPolicyOptions$(this.policies) - .pipe(takeUntil(this.destroy$)) - .subscribe((enforcedPasswordPolicyOptions) => { - this.enforcedPolicyOptions = enforcedPasswordPolicyOptions; - }); - } - - await super.ngOnInit(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html index 86419550d9..924f122495 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html @@ -23,26 +23,39 @@
- - - - - - - + + + + + + + - - - + + +
-
+
+
+ +
+
+

diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts index 9cfe933380..3dfabfe9f2 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.spec.ts @@ -168,8 +168,9 @@ describe("TrialInitiationComponent", () => { it("should set org variable to be enterprise and plan to EnterpriseAnnually if org param is enterprise", fakeAsync(() => { mockQueryParams.next({ org: "enterprise" }); tick(); // wait for resolution + fixture = TestBed.createComponent(TrialInitiationComponent); + component = fixture.componentInstance; fixture.detectChanges(); - component.ngOnInit(); expect(component.org).toBe("enterprise"); expect(component.plan).toBe(PlanType.EnterpriseAnnually); })); @@ -182,13 +183,33 @@ describe("TrialInitiationComponent", () => { expect(component.org).toBe(""); expect(component.accountCreateOnly).toBe(true); })); - it("should set the org to be families and plan to FamiliesAnnually if org param is invalid ", fakeAsync(async () => { + it("should not set the org if org param is invalid ", fakeAsync(async () => { mockQueryParams.next({ org: "hahahaha" }); tick(); // wait for resolution + fixture = TestBed.createComponent(TrialInitiationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + expect(component.org).toBe(""); + expect(component.accountCreateOnly).toBe(true); + })); + it("should set the layout variable if layout param is valid ", fakeAsync(async () => { + mockQueryParams.next({ layout: "teams1" }); + tick(); // wait for resolution + fixture = TestBed.createComponent(TrialInitiationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + expect(component.layout).toBe("teams1"); + expect(component.accountCreateOnly).toBe(false); + })); + it("should not set the layout variable and leave as 'default' if layout param is invalid ", fakeAsync(async () => { + mockQueryParams.next({ layout: "asdfasdf" }); + tick(); // wait for resolution + fixture = TestBed.createComponent(TrialInitiationComponent); + component = fixture.componentInstance; fixture.detectChanges(); component.ngOnInit(); - expect(component.org).toBe("families"); - expect(component.plan).toBe(PlanType.FamiliesAnnually); + expect(component.layout).toBe("default"); + expect(component.accountCreateOnly).toBe(true); })); }); diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts index f675fa6816..2f3f75a4a1 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.ts @@ -20,6 +20,30 @@ import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenc import { RouterService } from "./../../core/router.service"; import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component"; +enum ValidOrgParams { + families = "families", + enterprise = "enterprise", + teams = "teams", + individual = "individual", + premium = "premium", + free = "free", +} + +enum ValidLayoutParams { + default = "default", + teams = "teams", + teams1 = "teams1", + teams2 = "teams2", + enterprise = "enterprise", + enterprise1 = "enterprise1", + enterprise2 = "enterprise2", + cnetcmpgnent = "cnetcmpgnent", + cnetcmpgnind = "cnetcmpgnind", + cnetcmpgnteams = "cnetcmpgnteams", + abmenterprise = "abmenterprise", + abmteams = "abmteams", +} + @Component({ selector: "app-trial", templateUrl: "trial-initiation.component.html", @@ -35,9 +59,20 @@ export class TrialInitiationComponent implements OnInit, OnDestroy { plan: PlanType; product: ProductType; accountCreateOnly = true; + useTrialStepper = false; policies: Policy[]; enforcedPolicyOptions: MasterPasswordPolicyOptions; - validOrgs: string[] = ["teams", "enterprise", "families"]; + trialFlowOrgs: string[] = [ + ValidOrgParams.teams, + ValidOrgParams.enterprise, + ValidOrgParams.families, + ]; + routeFlowOrgs: string[] = [ + ValidOrgParams.free, + ValidOrgParams.premium, + ValidOrgParams.individual, + ]; + layouts = ValidLayoutParams; referenceData: ReferenceEventRequest; @ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent; @@ -87,39 +122,38 @@ export class TrialInitiationComponent implements OnInit, OnDestroy { this.referenceDataId = qParams.reference; - if (!qParams.org) { - return; - } - - if (qParams.layout) { + if (Object.values(ValidLayoutParams).includes(qParams.layout)) { this.layout = qParams.layout; + this.accountCreateOnly = false; } - if (this.validOrgs.includes(qParams.org)) { + if (this.trialFlowOrgs.includes(qParams.org)) { this.org = qParams.org; - } else { - this.org = "families"; - } + this.orgLabel = this.titleCasePipe.transform(this.org); + this.useTrialStepper = true; + this.referenceData.flow = qParams.org; - this.referenceData.flow = qParams.org; + if (this.org === ValidOrgParams.families) { + this.plan = PlanType.FamiliesAnnually; + this.product = ProductType.Families; + } else if (this.org === ValidOrgParams.teams) { + this.plan = PlanType.TeamsAnnually; + this.product = ProductType.Teams; + } else if (this.org === ValidOrgParams.enterprise) { + this.plan = PlanType.EnterpriseAnnually; + this.product = ProductType.Enterprise; + } + } else if (this.routeFlowOrgs.includes(qParams.org)) { + this.referenceData.flow = qParams.org; + const route = this.router.createUrlTree(["create-organization"], { + queryParams: { plan: qParams.org }, + }); + this.routerService.setPreviousUrl(route.toString()); + } // Are they coming from an email for sponsoring a families organization // After logging in redirect them to setup the families sponsorship this.setupFamilySponsorship(qParams.sponsorshipToken); - - this.orgLabel = this.titleCasePipe.transform(this.org); - this.accountCreateOnly = false; - - if (this.org === "families") { - this.plan = PlanType.FamiliesAnnually; - this.product = ProductType.Families; - } else if (this.org === "teams") { - this.plan = PlanType.TeamsAnnually; - this.product = ProductType.Teams; - } else if (this.org === "enterprise") { - this.plan = PlanType.EnterpriseAnnually; - this.product = ProductType.Enterprise; - } }); const invite = await this.stateService.getOrganizationInvitation(); diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index f77043a777..76b81f27ea 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -15,7 +15,6 @@ import { LoginWithDeviceComponent } from "./accounts/login/login-with-device.com import { LoginComponent } from "./accounts/login/login.component"; import { RecoverDeleteComponent } from "./accounts/recover-delete.component"; import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component"; -import { RegisterComponent } from "./accounts/register.component"; import { RemovePasswordComponent } from "./accounts/remove-password.component"; import { SetPasswordComponent } from "./accounts/set-password.component"; import { SsoComponent } from "./accounts/sso.component"; @@ -69,16 +68,15 @@ const routes: Routes = [ { path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] }, { path: "register", - component: RegisterComponent, + component: TrialInitiationComponent, canActivate: [UnauthGuard], data: { titleId: "createAccount" }, }, - buildFlaggedRoute("showTrial", { + { path: "trial", - component: TrialInitiationComponent, - canActivate: [UnauthGuard], - data: { titleId: "startTrial" }, - }), + redirectTo: "register", + pathMatch: "full", + }, { path: "sso", component: SsoComponent, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index cd9992bd79..9f57185e0d 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -7,7 +7,6 @@ import { LockComponent } from "../accounts/lock.component"; import { RecoverDeleteComponent } from "../accounts/recover-delete.component"; import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component"; import { RegisterFormModule } from "../accounts/register-form/register-form.module"; -import { RegisterComponent } from "../accounts/register.component"; import { RemovePasswordComponent } from "../accounts/remove-password.component"; import { SetPasswordComponent } from "../accounts/set-password.component"; import { SsoComponent } from "../accounts/sso.component"; @@ -218,7 +217,6 @@ import { SharedModule } from "."; PurgeVaultComponent, RecoverDeleteComponent, RecoverTwoFactorComponent, - RegisterComponent, RemovePasswordComponent, SecurityComponent, SecurityKeysComponent, @@ -341,7 +339,6 @@ import { SharedModule } from "."; PurgeVaultComponent, RecoverDeleteComponent, RecoverTwoFactorComponent, - RegisterComponent, RemovePasswordComponent, SecurityComponent, SecurityKeysComponent, diff --git a/apps/web/src/utils/flags.ts b/apps/web/src/utils/flags.ts index 03e4b2a9c9..601734ea5b 100644 --- a/apps/web/src/utils/flags.ts +++ b/apps/web/src/utils/flags.ts @@ -9,7 +9,6 @@ import { // required to avoid linting errors when there are no flags /* eslint-disable-next-line @typescript-eslint/ban-types */ export type Flags = { - showTrial?: boolean; secretsManager?: boolean; showPasswordless?: boolean; } & SharedFlags;