From acf3135afcb6bf3aa5b8e2ca341baea06fab8eb7 Mon Sep 17 00:00:00 2001 From: Brandon Date: Thu, 16 Jan 2025 17:11:54 -0500 Subject: [PATCH 1/8] remove provider client privay banner, implement account deprovisioning banner --- .../organization-layout.component.html | 17 ++++++++++++++ .../layouts/organization-layout.component.ts | 11 ++++++++++ .../account-deprovisioning-banner.service.ts | 10 ++++----- .../providers/providers-layout.component.html | 20 ----------------- .../providers/providers-layout.component.ts | 22 ++----------------- libs/common/src/enums/feature-flag.enum.ts | 4 ++-- .../src/platform/state/state-definitions.ts | 10 ++++++--- 7 files changed, 43 insertions(+), 51 deletions(-) rename bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts => apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts (66%) diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index 8387c53e5e3..6a8dbbffe99 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -120,6 +120,23 @@ + + {{ "accountDeprovisioningNotification" }} + + {{ "contactBitwardenSupport" | i18n }} . + + ; enterpriseOrganization$: Observable; + showAccountDeprovisioningBanner$: Observable; + constructor( private route: ActivatedRoute, private organizationService: OrganizationService, @@ -66,6 +70,7 @@ export class OrganizationLayoutComponent implements OnInit { private configService: ConfigService, private policyService: PolicyService, private providerService: ProviderService, + protected bannerService: AccountDeprovisioningBannerService, ) {} async ngOnInit() { @@ -77,6 +82,12 @@ export class OrganizationLayoutComponent implements OnInit { filter((org) => org != null), ); + this.showAccountDeprovisioningBanner$ = combineLatest([ + this.bannerService.showBanner$, + this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioningBanner), + this.organization$, + ]).pipe(map(([show, flag, organizaiton]) => organizaiton.isAdmin && show != false && flag)); + this.canAccessExport$ = this.organization$.pipe(map((org) => org.canAccessExport)); this.showPaymentAndHistory$ = this.organization$.pipe( diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts similarity index 66% rename from bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts rename to apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts index c347f5c2aae..7b8addd5de7 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/provider-client-vault-privacy-banner.service.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts @@ -1,24 +1,22 @@ import { Injectable } from "@angular/core"; import { + ACCOUNT_DEPROVISIONING_BANNER_DISK, StateProvider, - AC_BANNERS_DISMISSED_DISK, UserKeyDefinition, } from "@bitwarden/common/platform/state"; export const SHOW_BANNER_KEY = new UserKeyDefinition( - AC_BANNERS_DISMISSED_DISK, - "showProviderClientVaultPrivacyBanner", + ACCOUNT_DEPROVISIONING_BANNER_DISK, + "accountDeprovisioningBanner", { deserializer: (b) => b, clearOn: [], }, ); -/** Displays a banner warning provider users that client organization vaults - * will soon become inaccessible directly. */ @Injectable({ providedIn: "root" }) -export class ProviderClientVaultPrivacyBannerService { +export class AccountDeprovisioningBannerService { private _showBanner = this.stateProvider.getActive(SHOW_BANNER_KEY); showBanner$ = this._showBanner.state$; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html index a20dd1379e2..bf82fbd160b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html @@ -37,25 +37,5 @@ > - - {{ "providerClientVaultPrivacyNotification" | i18n }} - - {{ "contactBitwardenSupport" | i18n }} . - diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts index 3f1a7ff3989..7e47da95e2b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts @@ -10,27 +10,15 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderStatusType } from "@bitwarden/common/admin-console/enums"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { BannerModule, IconModule, LinkModule } from "@bitwarden/components"; +import { IconModule } from "@bitwarden/components"; import { ProviderPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/provider-portal-logo"; import { WebLayoutModule } from "@bitwarden/web-vault/app/layouts/web-layout.module"; -import { ProviderClientVaultPrivacyBannerService } from "./services/provider-client-vault-privacy-banner.service"; - @Component({ selector: "providers-layout", templateUrl: "providers-layout.component.html", standalone: true, - imports: [ - CommonModule, - RouterModule, - JslibModule, - WebLayoutModule, - IconModule, - LinkModule, - BannerModule, - ], + imports: [CommonModule, RouterModule, JslibModule, WebLayoutModule, IconModule], }) export class ProvidersLayoutComponent implements OnInit, OnDestroy { protected readonly logo = ProviderPortalLogo; @@ -41,15 +29,9 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { protected isBillable: Observable; protected canAccessBilling$: Observable; - protected showProviderClientVaultPrivacyWarningBanner$ = this.configService.getFeatureFlag$( - FeatureFlag.ProviderClientVaultPrivacyBanner, - ); - constructor( private route: ActivatedRoute, private providerService: ProviderService, - private configService: ConfigService, - protected providerClientVaultPrivacyBannerService: ProviderClientVaultPrivacyBannerService, ) {} ngOnInit() { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index d008a09d66c..7fcc8a259e2 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -25,7 +25,6 @@ export enum FeatureFlag { PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service", EmailVerification = "email-verification", TwoFactorComponentRefactor = "two-factor-component-refactor", - ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner", VaultBulkManagementAction = "vault-bulk-management-action", IdpAutoSubmitLogin = "idp-auto-submit-login", UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh", @@ -47,6 +46,7 @@ export enum FeatureFlag { PM12443RemovePagingLogic = "pm-12443-remove-paging-logic", PrivateKeyRegeneration = "pm-12241-private-key-regeneration", ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", + AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -82,7 +82,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM4154_BulkEncryptionService]: FALSE, [FeatureFlag.EmailVerification]: FALSE, [FeatureFlag.TwoFactorComponentRefactor]: FALSE, - [FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE, [FeatureFlag.VaultBulkManagementAction]: FALSE, [FeatureFlag.IdpAutoSubmitLogin]: FALSE, [FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE, @@ -104,6 +103,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM12443RemovePagingLogic]: FALSE, [FeatureFlag.PrivateKeyRegeneration]: FALSE, [FeatureFlag.ResellerManagedOrgAlert]: FALSE, + [FeatureFlag.AccountDeprovisioningBanner]: true, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 483a8c050d3..f70e8c99ae7 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -29,9 +29,13 @@ export const ORGANIZATION_MANAGEMENT_PREFERENCES_DISK = new StateDefinition( web: "disk-local", }, ); -export const AC_BANNERS_DISMISSED_DISK = new StateDefinition("acBannersDismissed", "disk", { - web: "disk-local", -}); +export const ACCOUNT_DEPROVISIONING_BANNER_DISK = new StateDefinition( + "showAccountDeprovisioningBanner", + "disk", + { + web: "disk-local", + }, +); // Billing export const BILLING_DISK = new StateDefinition("billing", "disk"); From d830d07692e243827a43faec63c00c7dda47b7d1 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 27 Jan 2025 15:07:24 -0500 Subject: [PATCH 2/8] add copy, make state depend on org plan type and org id --- .../organization-layout.component.html | 33 +++++++++---------- .../layouts/organization-layout.component.ts | 11 ++++++- .../account-deprovisioning-banner.service.ts | 14 ++++++-- apps/web/src/locales/en/messages.json | 5 ++- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index 6a8dbbffe99..ae0972a6828 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -120,24 +120,23 @@ - - {{ "accountDeprovisioningNotification" }} - - {{ "contactBitwardenSupport" | i18n }} . - - + + {{ "accountDeprovisioningNotification" | i18n }} + + {{ "learnMore" | i18n }} + + organizaiton.isAdmin && show != false && flag)); + ]).pipe( + map( + ([dismissedOrgs, featureFlagEnabled, organization]) => + organization.productTierType === ProductTierType.Enterprise && + organization.isAdmin && + !dismissedOrgs?.includes(organization.id) && + featureFlagEnabled, + ), + ); this.canAccessExport$ = this.organization$.pipe(map((org) => org.canAccessExport)); diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts index 7b8addd5de7..c289ebdd20d 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts @@ -1,12 +1,13 @@ import { Injectable } from "@angular/core"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { ACCOUNT_DEPROVISIONING_BANNER_DISK, StateProvider, UserKeyDefinition, } from "@bitwarden/common/platform/state"; -export const SHOW_BANNER_KEY = new UserKeyDefinition( +export const SHOW_BANNER_KEY = new UserKeyDefinition( ACCOUNT_DEPROVISIONING_BANNER_DISK, "accountDeprovisioningBanner", { @@ -23,7 +24,14 @@ export class AccountDeprovisioningBannerService { constructor(private stateProvider: StateProvider) {} - async hideBanner() { - await this._showBanner.update(() => false); + async hideBanner(organization: Organization) { + await this._showBanner.update((state) => { + if (!state) { + state = [organization.id]; + } else if (!state.includes(organization.id)) { + state.push(organization.id); + } + return state; + }); } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 15c5a7fcf6c..2be8943424e 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1780,7 +1780,7 @@ }, "requestPending": { "message": "Request pending" - }, + }, "logBackInOthersToo": { "message": "Please log back in. If you are using other Bitwarden applications log out and back in to those as well." }, @@ -10258,5 +10258,8 @@ "example": "Acme c" } } + }, + "accountDeprovisioningNotification" : { + "message": "Administrators now have the ability to delete member accounts that belong to a claimed domain." } } From 84546d8223aaa5314888837bf54f04ef0462203a Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 27 Jan 2025 15:32:03 -0500 Subject: [PATCH 3/8] cleanup --- .../organizations/layouts/organization-layout.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts index f30968e43d2..c674545b7ef 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts @@ -20,9 +20,9 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { PolicyType, ProviderStatusType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { ProductTierType } from "@bitwarden/common/billing/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { ProductTierType } from "@bitwarden/common/billing/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -80,7 +80,7 @@ export class OrganizationLayoutComponent implements OnInit { async ngOnInit() { document.body.classList.remove("layout_frontend"); - const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); this.organization$ = this.route.params.pipe( map((p) => p.organizationId), switchMap((id) => this.organizationService.organizations$(userId).pipe(getById(id))), From a9d9cf2443829e14eb3f4e04676ce85bc942f22b Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 29 Jan 2025 11:20:39 -0500 Subject: [PATCH 4/8] refactor, add test --- ...ount-deprovisioning-banner.service.spec.ts | 75 +++++++++++++++++++ .../account-deprovisioning-banner.service.ts | 7 +- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts new file mode 100644 index 00000000000..f4a42f4f35a --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts @@ -0,0 +1,75 @@ +import { firstValueFrom } from "rxjs"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + FakeAccountService, + FakeStateProvider, + mockAccountServiceWith, +} from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { AccountDeprovisioningBannerService } from "./account-deprovisioning-banner.service"; + +describe("Is Enterprise Org Guard", () => { + const userId = Utils.newGuid() as UserId; + let accountService: FakeAccountService; + let stateProvider: FakeStateProvider; + let bannerService: AccountDeprovisioningBannerService; + + beforeEach(async () => { + accountService = mockAccountServiceWith(userId); + stateProvider = new FakeStateProvider(accountService); + bannerService = new AccountDeprovisioningBannerService(stateProvider); + }); + + it("updates state with single org", async () => { + const fakeOrg = new Organization(); + fakeOrg.id = "123"; + + await bannerService.hideBanner(fakeOrg); + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toEqual([fakeOrg.id]); + }); + + it("updates state with multiple orgs", async () => { + const fakeOrg1 = new Organization(); + fakeOrg1.id = "123"; + const fakeOrg2 = new Organization(); + fakeOrg2.id = "234"; + const fakeOrg3 = new Organization(); + fakeOrg3.id = "987"; + + await bannerService.hideBanner(fakeOrg1); + await bannerService.hideBanner(fakeOrg2); + await bannerService.hideBanner(fakeOrg3); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toContain(fakeOrg1.id); + expect(state).toContain(fakeOrg2.id); + expect(state).toContain(fakeOrg3.id); + }); + + it("does not add the same org id multiple times", async () => { + const fakeOrg = new Organization(); + fakeOrg.id = "123"; + + await bannerService.hideBanner(fakeOrg); + await bannerService.hideBanner(fakeOrg); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toEqual([fakeOrg.id]); + }); + + it("does not add null to the state", async () => { + await bannerService.hideBanner(null as unknown as Organization); + await bannerService.hideBanner(undefined as unknown as Organization); + + const state = await firstValueFrom(bannerService.showBanner$); + + expect(state).toBeNull(); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts index c289ebdd20d..86a6b7df3e2 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.ts @@ -26,10 +26,13 @@ export class AccountDeprovisioningBannerService { async hideBanner(organization: Organization) { await this._showBanner.update((state) => { + if (!organization) { + return state; + } if (!state) { - state = [organization.id]; + return [organization.id]; } else if (!state.includes(organization.id)) { - state.push(organization.id); + return [...state, organization.id]; } return state; }); From 4f71d597dc57c4ed0a41511b84ecef828554e4b1 Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 29 Jan 2025 11:22:24 -0500 Subject: [PATCH 5/8] cleanup --- .../services/account-deprovisioning-banner.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts index f4a42f4f35a..414828969df 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/services/account-deprovisioning-banner.service.spec.ts @@ -11,7 +11,7 @@ import { UserId } from "@bitwarden/common/types/guid"; import { AccountDeprovisioningBannerService } from "./account-deprovisioning-banner.service"; -describe("Is Enterprise Org Guard", () => { +describe("Account Deprovisioning Banner Service", () => { const userId = Utils.newGuid() as UserId; let accountService: FakeAccountService; let stateProvider: FakeStateProvider; From 2c13618014d512fb848231d759613445fb5a4ef2 Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 29 Jan 2025 11:31:09 -0500 Subject: [PATCH 6/8] cleanup --- .../layouts/organization-layout.component.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts index c674545b7ef..1f31ae4fc0c 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts @@ -3,7 +3,7 @@ import { CommonModule } from "@angular/common"; import { Component, OnInit } from "@angular/core"; import { ActivatedRoute, RouterModule } from "@angular/router"; -import { combineLatest, filter, firstValueFrom, map, Observable, switchMap } from "rxjs"; +import { combineLatest, filter, map, Observable, switchMap, withLatestFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { @@ -80,10 +80,12 @@ export class OrganizationLayoutComponent implements OnInit { async ngOnInit() { document.body.classList.remove("layout_frontend"); - const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); this.organization$ = this.route.params.pipe( map((p) => p.organizationId), - switchMap((id) => this.organizationService.organizations$(userId).pipe(getById(id))), + withLatestFrom(this.accountService.activeAccount$.pipe(getUserId)), + switchMap(([orgId, userId]) => + this.organizationService.organizations$(userId).pipe(getById(orgId)), + ), filter((org) => org != null), ); From da61b3348af9ce112aa95a8ad82999dbfc42e7e1 Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 5 Feb 2025 13:02:47 -0500 Subject: [PATCH 7/8] add state migration --- .../70-remove-ac-banner-dismissed.spec.ts | 50 +++++++++++++++++++ .../70-remove-ac-banner-dismissed.ts | 23 +++++++++ 2 files changed, 73 insertions(+) create mode 100644 libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts diff --git a/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts new file mode 100644 index 00000000000..59f39d195e9 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.spec.ts @@ -0,0 +1,50 @@ +import { runMigrator } from "../migration-helper.spec"; +import { IRREVERSIBLE } from "../migrator"; + +import { RemoveAcBannersDismissed } from "./70-remove-ac-banner-dismissed"; + +describe("RemoveAcBannersDismissed", () => { + const sut = new RemoveAcBannersDismissed(69, 70); + + describe("migrate", () => { + it("deletes ac banner from all users", async () => { + const output = await runMigrator(sut, { + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + user_user1_showProviderClientVaultPrivacyBanner_acBannersDismissed: true, + user_user2_showProviderClientVaultPrivacyBanner_acBannersDismissed: true, + }); + + expect(output).toEqual({ + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + }); + }); + }); + + describe("rollback", () => { + it("is irreversible", async () => { + await expect(runMigrator(sut, {}, "rollback")).rejects.toThrow(IRREVERSIBLE); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts new file mode 100644 index 00000000000..087994b508f --- /dev/null +++ b/libs/common/src/state-migrations/migrations/70-remove-ac-banner-dismissed.ts @@ -0,0 +1,23 @@ +import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; +import { IRREVERSIBLE, Migrator } from "../migrator"; + +export const SHOW_BANNER_KEY: KeyDefinitionLike = { + key: "acBannersDismissed", + stateDefinition: { name: "showProviderClientVaultPrivacyBanner" }, +}; + +export class RemoveAcBannersDismissed extends Migrator<69, 70> { + async migrate(helper: MigrationHelper): Promise { + await Promise.all( + (await helper.getAccounts()).map(async ({ userId }) => { + if (helper.getFromUser(userId, SHOW_BANNER_KEY) != null) { + await helper.removeFromUser(userId, SHOW_BANNER_KEY); + } + }), + ); + } + + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } +} From b47b731fabe505aea7456723c0b8a3863dc9ece2 Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 5 Feb 2025 15:39:18 -0500 Subject: [PATCH 8/8] Fix lintter error --- libs/common/src/platform/state/state-definitions.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 4b1b975113b..c7901bc34e2 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -31,6 +31,11 @@ export const ORGANIZATION_MANAGEMENT_PREFERENCES_DISK = new StateDefinition( ); export const ACCOUNT_DEPROVISIONING_BANNER_DISK = new StateDefinition( "showAccountDeprovisioningBanner", + "disk", + { + web: "disk-local", + }, +); export const DELETE_MANAGED_USER_WARNING = new StateDefinition( "showDeleteManagedUserWarning", "disk",